2011-01-19 29 views
5

Cơ sở dữ liệu MySQL hỗ trợ trang web Django của chúng tôi đã phát triển một số vấn đề về tính toàn vẹn; ví dụ. khóa ngoài tham chiếu đến các hàng không tồn tại. Tôi sẽ không đi vào làm thế nào chúng tôi nhận được vào mớ hỗn độn này, nhưng tôi bây giờ nhìn vào cách sửa chữa nó.Có công cụ nào để kiểm tra tính toàn vẹn của cơ sở dữ liệu ở Django không?

Về cơ bản, Tôi đang tìm tập lệnh quét tất cả các mô hình trong trang web Django và kiểm tra xem tất cả các khóa ngoại và các ràng buộc khác có chính xác không. Hy vọng rằng, số lượng vấn đề sẽ đủ nhỏ để chúng có thể được cố định bằng tay.

Tôi có thể tự viết mã này nhưng tôi hy vọng rằng ai đó ở đây có ý tưởng tốt hơn.

Tôi tìm thấy django-check-constraints nhưng không hoàn toàn phù hợp với hóa đơn: ngay bây giờ, tôi không cần thứ gì đó để ngăn chặn những vấn đề này, nhưng để tìm chúng để chúng có thể được sửa thủ công trước khi thực hiện các bước khác.

trở ngại khác:

  • Django 1.1.1 và nâng cấp đã được xác định để phá vỡ mọi thứ
  • MySQL 5.0.51 (Debian Lenny), hiện tại với MyISAM bảng
  • Python 2.5 , có thể nâng cấp nhưng tôi không muốn ngay bây giờ

(Sau đó, chúng tôi sẽ chuyển đổi thành I nnoDB để hỗ trợ giao dịch thích hợp và có thể là các ràng buộc khóa ngoài ở cấp cơ sở dữ liệu, để ngăn chặn các vấn đề tương tự trong tương lai. Nhưng đó không phải là chủ đề của câu hỏi này.)

+0

Bạn không đề cập đến việc sử dụng PostGreSQL, MySQL là bắt buộc? –

+0

Không bắt buộc, không. Chúng tôi chắc chắn sẽ xem xét PostgreSQL sau, nhưng thay đổi DBMS là một chút quá nguy hiểm vào lúc này. – Thomas

Trả lời

7

Tôi đã tự mình làm một điều gì đó. Tập lệnh quản lý dưới đây sẽ được lưu trong myapp/management/commands/checkdb.py. Đảm bảo rằng các thư mục trung gian có tệp __init__.py.

Cách sử dụng: ./manage.py checkdb để kiểm tra toàn bộ; sử dụng --exclude app.Model hoặc -e app.Model để loại trừ kiểu Model trong ứng dụng app.

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 

Tôi đang tạo một wiki cộng đồng này vì tôi hoan nghênh mọi và cải tiến mã của mình!

+1

Điều này có vẻ tuyệt vời. Bạn nên liên hệ với các nhà phát triển cốt lõi Django và yêu cầu họ nếu họ sẽ xem xét một cái gì đó như thế này trong Django chính nó. Tôi nghĩ rằng đó là một tính năng mất tích rất lớn. – slacy

+0

nơi nào người ta tìm thấy lib.progress with_progress_meter? CHeers –

+0

Trên ổ cứng của tôi. Và bây giờ cũng ở đây: http://pastebin.com/Bdd75Rkk – Thomas

0

Câu trả lời của Thomas thật tuyệt vời nhưng hiện đã hơi lạc hậu. Tôi đã cập nhật nó as a gist để hỗ trợ Django 1.8+.

Các vấn đề liên quan