2011-01-19 18 views
5

Il database MySQL che alimenta il nostro sito Django ha sviluppato alcuni problemi di integrità; per esempio. chiavi esterne che si riferiscono a file inesistenti. Non entrerò nel modo in cui siamo entrati in questo casino, ma ora sto cercando come sistemarlo.Esiste uno strumento per verificare l'integrità del database in Django?

Fondamentalmente, Sto cercando uno script che analizzi tutti i modelli nel sito Django e controlla se tutte le chiavi esterne e altri vincoli sono corretti. Si spera che il numero di problemi sia abbastanza piccolo da poter essere riparato a mano.

Potrei farlo da solo, ma spero che qualcuno qui abbia un'idea migliore.

Ho trovato django-check-constraints ma non si adatta al problema: al momento, non ho bisogno di qualcosa per prevenire questi problemi, ma per trovarli in modo che possano essere riparati manualmente prima di prendere altri passaggi.

Altri vincoli:

  • Django 1.1.1 e riqualificazione è stata determinata a rompere le cose
  • MySQL 5.0.51 (Debian Lenny), attualmente con MyISAM tavoli
  • Python 2.5 , potrebbe essere aggiornabile ma preferirei non ora

(Più tardi, convertiremo in I nnoDB per il corretto supporto delle transazioni e forse vincoli di chiave esterna a livello di database, per prevenire problemi simili in futuro. Ma questo non è l'argomento di questa domanda.)

+0

Non hai menzionato solo l'uso di PostGreSQL, MySQL è obbligatorio? –

+0

Non obbligatorio, no. Considereremo PostgreSQL in seguito, ma cambiare il DBMS è un po 'troppo rischioso al momento. – Thomas

risposta

7

Ho montato qualcosa di me stesso. Lo script di gestione di seguito deve essere salvato in myapp/management/commands/checkdb.py. Assicurati che le directory intermedie abbiano un file __init__.py.

Uso: ./manage.py checkdb per un controllo completo; utilizzare --exclude app.Model o -e app.Model per escludere il modello Model nell'app 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 

Sto facendo di questo un wiki della comunità perché accolgo qualsiasi e qualsiasi miglioramento del mio codice!

+1

Sembra fantastico. Dovresti contattare gli sviluppatori del core di Django e chiedere loro se considererebbero qualcosa di simile a Django stesso. Penso che sia una caratteristica mancante enorme. – slacy

+0

dove si trova lib.progress with_progress_meter? CHeers –

+0

Sul mio disco rigido. E ora anche qui: http://pastebin.com/Bdd75Rkk – Thomas

0

La risposta di Thomas è ottima, ma ora è un po 'obsoleta. L'ho aggiornato as a gist per supportare Django 1.8+.

Problemi correlati