Skip to content

Commit 09e3f4e

Browse files
committed
Added a management command to check referential integrity, original code
conceived and written by rjsparks@nostrum.com, with some additions and adaptation as a management command by henrik@levkowetz.com. This integrity checker, when run on the IETF database in November 2015, seemed to have a factor on the order of 100 better speed and lower memory consumption than the code in https://stackoverflow.com/questions/4734645/is-there-a-tool-to-check-database-integrity-in-django No exact figure is available, as the latter had to be killed when it had taken all available ram (~14G) on the development machine, and started to cause heavy swapping. - Legacy-Id: 10508
1 parent ff69698 commit 09e3f4e

1 file changed

Lines changed: 71 additions & 0 deletions

File tree

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
2+
import django
3+
django.setup()
4+
5+
from django.core.management.base import BaseCommand #, CommandError
6+
from django.core.exceptions import FieldError
7+
from django.db import models
8+
from django.db.models.fields.related import ForeignKey, OneToOneField
9+
10+
class Command(BaseCommand):
11+
help = "Check all models for referential integrity."
12+
13+
def handle(self, *args, **options):
14+
verbosity = options.get("verbosity", "1")
15+
verbosity = int(verbosity) if verbosity.isdigit() else 1
16+
17+
def check_field(field):
18+
foreign_model = field.related.parent_model
19+
if verbosity > 1:
20+
print " %s -> %s.%s"%(field.name,foreign_model.__module__,foreign_model.__name__),
21+
used = set(field.model.objects.values_list(field.name,flat=True))
22+
used.discard(None)
23+
exists = set(foreign_model.objects.values_list('pk',flat=True))
24+
if verbosity > 1:
25+
if used - exists:
26+
print ' ** Bad key values:',list(used - exists)
27+
else:
28+
print 'ok'
29+
else:
30+
if used - exists:
31+
print '%s.%s.%s->%s.%s Bad key values:'%(model.__module__,model.__name__,field.name,foreign_model.__module__,foreign_model.__name__),list(used - exists)
32+
33+
def check_reverse_field(field):
34+
foreign_model = field.related.parent_model
35+
if foreign_model == field.model:
36+
return
37+
foreign_field_name = field.related.var_name
38+
foreign_accessor_name = field.related.get_accessor_name()
39+
if verbosity > 1:
40+
print " %s.%s.%s -> %s"%(foreign_model.__module__, foreign_model.__name__, foreign_field_name, field.model.__name__),
41+
try:
42+
used = set(foreign_model.objects.values_list(foreign_field_name, flat=True))
43+
except FieldError:
44+
try:
45+
used = set(foreign_model.objects.values_list(foreign_accessor_name, flat=True))
46+
except FieldError:
47+
print " ** Warning: could not find reverse name for %s.%s -> %s.%s" % (field.model.__module__, field.model.__name__, foreign_model.__name__, foreign_field_name),
48+
used.discard(None)
49+
exists = set(field.model.objects.values_list('pk',flat=True))
50+
if verbosity > 1:
51+
if used - exists:
52+
print ' ** Bad key values:\n ',list(used - exists)
53+
else:
54+
print ' ok'
55+
else:
56+
if used - exists:
57+
print " %s.%s.%s -> %s.%s ** Bad key values:\n "%(foreign_model.__module__, foreign_model.__name__, foreign_field_name, field.model.__module__, field.model.__name__), list(used - exists)
58+
59+
for app in [a for a in models.get_apps() if a.__name__.startswith('ietf.')]:
60+
if verbosity > 1:
61+
print "Checking",app.__name__
62+
for model in models.get_models(app):
63+
if model._meta.proxy:
64+
continue
65+
if verbosity > 1:
66+
print " %s.%s"%(model.__module__,model.__name__)
67+
for field in [f for f in model._meta.fields if isinstance(f, (ForeignKey, OneToOneField)) ]:
68+
check_field(field)
69+
for field in [f for f in model._meta.many_to_many ]:
70+
check_field(field)
71+
check_reverse_field(field)

0 commit comments

Comments
 (0)