6

Djangoサイトを強化しているMySQLデータベースは、いくつかの整合性の問題を引き起こしました。たとえば、存在しない行を参照する外部キー。この混乱にどのように陥ったかについては説明しませんが、現在、それを修正する方法を検討しています。

基本的に、Djangoサイトのすべてのモデルをスキャンし、すべての外部キーとその他の制約が正しいかどうかを確認するスクリプトを探しています。うまくいけば、問題の数は十分に少なく、手作業で修正できます。

私はこれを自分でコーディングすることもできますが、ここの誰かがより良いアイデアを持っていることを望んでいます。

django-check-constraintsを見つけましたが、法案に完全には適合していません。現時点では、これらの問題を防ぐための何かは必要ありませんが、他の手順を実行する前に手動で修正できるように見つけることができます。

その他の制約:

  • Django1.1.1とアップグレードは物事を壊すと決定されまし
  • MySQL 5.0.51(Debian Lenny)、現在MyISAMテーブルを使用
  • Python 2.5はアップグレード可能かもしれませんが、今はしたくないです

(後で、適切なトランザクションサポートとデータベースレベルでの外部キー制約のためにInnoDBに変換して、将来同様の問題を防止します。しかし、それはこの質問のトピックではありません。)

4

2 に答える 2

8

私は自分で何かを泡立てました。以下の管理スクリプトはに保存する必要がありますmyapp/management/commands/checkdb.py。中間ディレクトリに__init__.pyファイルがあることを確認してください。

使用法:./manage.py checkdb完全なチェックのため。--exclude app.Modelまたはを使用して、アプリ-e app.Modelでモデルを除外します。Modelapp

from django.core.management.base import BaseCommand, CommandError
from django.core.management.base import NoArgsCommand
from django.core.exceptions import ObjectDoesNotExist
from django.db import models
from optparse import make_option
from lib.progress import with_progress_meter

def model_name(model):
    return '%s.%s' % (model._meta.app_label, model._meta.object_name)

class Command(BaseCommand):
    args = '[-e|--exclude app_name.ModelName]'
    help = 'Checks constraints in the database and reports violations on stdout'

    option_list = NoArgsCommand.option_list + (
        make_option('-e', '--exclude', action='append', type='string', dest='exclude'),
    )

    def handle(self, *args, **options):
        # TODO once we're on Django 1.2, write to self.stdout and self.stderr instead of plain print

        exclude = options.get('exclude', None) or []

        failed_instance_count = 0
        failed_model_count = 0
        for app in models.get_apps():
            for model in models.get_models(app):
                if model_name(model) in exclude:
                    print 'Skipping model %s' % model_name(model)
                    continue
                fail_count = self.check_model(app, model)
                if fail_count > 0:
                    failed_model_count += 1
                    failed_instance_count += fail_count
        print 'Detected %d errors in %d models' % (failed_instance_count, failed_model_count)

    def check_model(self, app, model):
        meta = model._meta
        if meta.proxy:
            print 'WARNING: proxy models not currently supported; ignored'
            return

        # Define all the checks we can do; they return True if they are ok,
        # False if not (and print a message to stdout)
        def check_foreign_key(model, field):
            foreign_model = field.related.parent_model
            def check_instance(instance):
                try:
                    # name: name of the attribute containing the model instance (e.g. 'user')
                    # attname: name of the attribute containing the id (e.g. 'user_id')
                    getattr(instance, field.name)
                    return True
                except ObjectDoesNotExist:
                    print '%s with pk %s refers via field %s to nonexistent %s with pk %s' % \
                        (model_name(model), str(instance.pk), field.name, model_name(foreign_model), getattr(instance, field.attname))
            return check_instance

        # Make a list of checks to run on each model instance
        checks = []
        for field in meta.local_fields + meta.local_many_to_many + meta.virtual_fields:
            if isinstance(field, models.ForeignKey):
                checks.append(check_foreign_key(model, field))

        # Run all checks
        fail_count = 0
        if checks:
            for instance in with_progress_meter(model.objects.all(), model.objects.count(), 'Checking model %s ...' % model_name(model)):
                for check in checks:
                    if not check(instance):
                        fail_count += 1
        return fail_count

私はこれをコミュニティウィキにしています。コードのあらゆる改善を歓迎するからです。

于 2011-01-19T14:10:34.427 に答える
2

トーマスの答えは素晴らしいですが、今では少し時代遅れになっています。Django1.8以降をサポートするための要点として更新しました。

于 2017-09-28T00:56:13.517 に答える