2007-08-16 14:06:55 +08:00
import sys
from django . core . management . color import color_style
2007-09-15 03:55:24 +08:00
from django . utils . itercompat import is_iterable
2007-08-16 14:06:55 +08:00
class ModelErrorCollection :
def __init__ ( self , outfile = sys . stdout ) :
self . errors = [ ]
self . outfile = outfile
self . style = color_style ( )
def add ( self , context , error ) :
self . errors . append ( ( context , error ) )
self . outfile . write ( self . style . ERROR ( " %s : %s \n " % ( context , error ) ) )
def get_validation_errors ( outfile , app = None ) :
"""
Validates all models that are part of the specified app . If no app name is provided ,
validates all models of all installed apps . Writes errors , if any , to outfile .
Returns number of errors .
"""
from django . conf import settings
from django . db import models , connection
from django . db . models . loading import get_app_errors
from django . db . models . fields . related import RelatedObject
e = ModelErrorCollection ( outfile )
for ( app_name , error ) in get_app_errors ( ) . items ( ) :
e . add ( app_name , error )
for cls in models . get_models ( app ) :
opts = cls . _meta
# Do field-specific validation.
Merged the queryset-refactor branch into trunk.
This is a big internal change, but mostly backwards compatible with existing
code. Also adds a couple of new features.
Fixed #245, #1050, #1656, #1801, #2076, #2091, #2150, #2253, #2306, #2400, #2430, #2482, #2496, #2676, #2737, #2874, #2902, #2939, #3037, #3141, #3288, #3440, #3592, #3739, #4088, #4260, #4289, #4306, #4358, #4464, #4510, #4858, #5012, #5020, #5261, #5295, #5321, #5324, #5325, #5555, #5707, #5796, #5817, #5987, #6018, #6074, #6088, #6154, #6177, #6180, #6203, #6658
git-svn-id: http://code.djangoproject.com/svn/django/trunk@7477 bcc190cf-cafb-0310-a4f2-bffc1f526a37
2008-04-27 10:50:16 +08:00
for f in opts . local_fields :
2007-08-16 14:06:55 +08:00
if f . name == ' id ' and not f . primary_key and opts . pk . name == ' id ' :
e . add ( opts , ' " %s " : You can \' t use " id " as a field name, because each model automatically gets an " id " field if none of the fields have primary_key=True. You need to either remove/rename your " id " field or add primary_key=True to a field. ' % f . name )
2007-10-22 05:53:18 +08:00
if f . name . endswith ( ' _ ' ) :
e . add ( opts , ' " %s " : Field names cannot end with underscores, because this would lead to ambiguous queryset filters. ' % f . name )
2007-08-16 14:06:55 +08:00
if isinstance ( f , models . CharField ) and f . max_length in ( None , 0 ) :
e . add ( opts , ' " %s " : CharFields require a " max_length " attribute. ' % f . name )
if isinstance ( f , models . DecimalField ) :
if f . decimal_places is None :
e . add ( opts , ' " %s " : DecimalFields require a " decimal_places " attribute. ' % f . name )
if f . max_digits is None :
e . add ( opts , ' " %s " : DecimalFields require a " max_digits " attribute. ' % f . name )
if isinstance ( f , models . FileField ) and not f . upload_to :
e . add ( opts , ' " %s " : FileFields require an " upload_to " attribute. ' % f . name )
if isinstance ( f , models . ImageField ) :
try :
from PIL import Image
except ImportError :
e . add ( opts , ' " %s " : To use ImageFields, you need to install the Python Imaging Library. Get it at http://www.pythonware.com/products/pil/ . ' % f . name )
2009-04-09 23:09:35 +08:00
if isinstance ( f , models . BooleanField ) and getattr ( f , ' null ' , False ) :
e . add ( opts , ' " %s " : BooleanFields do not accept null values. Use a NullBooleanField instead. ' % f . name )
2007-08-16 14:06:55 +08:00
if f . choices :
2007-09-15 06:49:01 +08:00
if isinstance ( f . choices , basestring ) or not is_iterable ( f . choices ) :
2007-08-16 14:06:55 +08:00
e . add ( opts , ' " %s " : " choices " should be iterable (e.g., a tuple or list). ' % f . name )
else :
for c in f . choices :
if not type ( c ) in ( tuple , list ) or len ( c ) != 2 :
e . add ( opts , ' " %s " : " choices " should be a sequence of two-tuples. ' % f . name )
if f . db_index not in ( None , True , False ) :
e . add ( opts , ' " %s " : " db_index " should be either None, True or False. ' % f . name )
2008-08-11 20:11:25 +08:00
# Perform any backend-specific field validation.
connection . validation . validate_field ( e , opts , f )
2007-08-16 14:06:55 +08:00
Merged the queryset-refactor branch into trunk.
This is a big internal change, but mostly backwards compatible with existing
code. Also adds a couple of new features.
Fixed #245, #1050, #1656, #1801, #2076, #2091, #2150, #2253, #2306, #2400, #2430, #2482, #2496, #2676, #2737, #2874, #2902, #2939, #3037, #3141, #3288, #3440, #3592, #3739, #4088, #4260, #4289, #4306, #4358, #4464, #4510, #4858, #5012, #5020, #5261, #5295, #5321, #5324, #5325, #5555, #5707, #5796, #5817, #5987, #6018, #6074, #6088, #6154, #6177, #6180, #6203, #6658
git-svn-id: http://code.djangoproject.com/svn/django/trunk@7477 bcc190cf-cafb-0310-a4f2-bffc1f526a37
2008-04-27 10:50:16 +08:00
# Check to see if the related field will clash with any existing
# fields, m2m fields, m2m related objects or related objects
2007-08-16 14:06:55 +08:00
if f . rel :
if f . rel . to not in models . get_models ( ) :
2008-08-19 22:17:24 +08:00
e . add ( opts , " ' %s ' has a relation with model %s , which has either not been installed or is abstract. " % ( f . name , f . rel . to ) )
2007-12-03 02:10:07 +08:00
# it is a string and we could not find the model it refers to
# so skip the next section
if isinstance ( f . rel . to , ( str , unicode ) ) :
continue
2007-08-16 14:06:55 +08:00
2007-12-03 02:10:07 +08:00
rel_opts = f . rel . to . _meta
2007-08-16 14:06:55 +08:00
rel_name = RelatedObject ( f . rel . to , cls , f ) . get_accessor_name ( )
rel_query_name = f . related_query_name ( )
2009-11-03 22:02:49 +08:00
if not f . rel . is_hidden ( ) :
for r in rel_opts . fields :
if r . name == rel_name :
e . add ( opts , " Accessor for field ' %s ' clashes with field ' %s . %s ' . Add a related_name argument to the definition for ' %s ' . " % ( f . name , rel_opts . object_name , r . name , f . name ) )
if r . name == rel_query_name :
e . add ( opts , " Reverse query name for field ' %s ' clashes with field ' %s . %s ' . Add a related_name argument to the definition for ' %s ' . " % ( f . name , rel_opts . object_name , r . name , f . name ) )
for r in rel_opts . local_many_to_many :
if r . name == rel_name :
e . add ( opts , " Accessor for field ' %s ' clashes with m2m field ' %s . %s ' . Add a related_name argument to the definition for ' %s ' . " % ( f . name , rel_opts . object_name , r . name , f . name ) )
if r . name == rel_query_name :
e . add ( opts , " Reverse query name for field ' %s ' clashes with m2m field ' %s . %s ' . Add a related_name argument to the definition for ' %s ' . " % ( f . name , rel_opts . object_name , r . name , f . name ) )
for r in rel_opts . get_all_related_many_to_many_objects ( ) :
2007-08-16 14:06:55 +08:00
if r . get_accessor_name ( ) == rel_name :
2009-11-03 22:02:49 +08:00
e . add ( opts , " Accessor for field ' %s ' clashes with related m2m field ' %s . %s ' . Add a related_name argument to the definition for ' %s ' . " % ( f . name , rel_opts . object_name , r . get_accessor_name ( ) , f . name ) )
2007-08-16 14:06:55 +08:00
if r . get_accessor_name ( ) == rel_query_name :
2009-11-03 22:02:49 +08:00
e . add ( opts , " Reverse query name for field ' %s ' clashes with related m2m field ' %s . %s ' . Add a related_name argument to the definition for ' %s ' . " % ( f . name , rel_opts . object_name , r . get_accessor_name ( ) , f . name ) )
for r in rel_opts . get_all_related_objects ( ) :
if r . field is not f :
if r . get_accessor_name ( ) == rel_name :
e . add ( opts , " Accessor for field ' %s ' clashes with related field ' %s . %s ' . Add a related_name argument to the definition for ' %s ' . " % ( f . name , rel_opts . object_name , r . get_accessor_name ( ) , f . name ) )
if r . get_accessor_name ( ) == rel_query_name :
e . add ( opts , " Reverse query name for field ' %s ' clashes with related field ' %s . %s ' . Add a related_name argument to the definition for ' %s ' . " % ( f . name , rel_opts . object_name , r . get_accessor_name ( ) , f . name ) )
2007-08-16 14:06:55 +08:00
2008-08-24 00:56:41 +08:00
seen_intermediary_signatures = [ ]
Merged the queryset-refactor branch into trunk.
This is a big internal change, but mostly backwards compatible with existing
code. Also adds a couple of new features.
Fixed #245, #1050, #1656, #1801, #2076, #2091, #2150, #2253, #2306, #2400, #2430, #2482, #2496, #2676, #2737, #2874, #2902, #2939, #3037, #3141, #3288, #3440, #3592, #3739, #4088, #4260, #4289, #4306, #4358, #4464, #4510, #4858, #5012, #5020, #5261, #5295, #5321, #5324, #5325, #5555, #5707, #5796, #5817, #5987, #6018, #6074, #6088, #6154, #6177, #6180, #6203, #6658
git-svn-id: http://code.djangoproject.com/svn/django/trunk@7477 bcc190cf-cafb-0310-a4f2-bffc1f526a37
2008-04-27 10:50:16 +08:00
for i , f in enumerate ( opts . local_many_to_many ) :
2007-08-16 14:06:55 +08:00
# Check to see if the related m2m field will clash with any
Merged the queryset-refactor branch into trunk.
This is a big internal change, but mostly backwards compatible with existing
code. Also adds a couple of new features.
Fixed #245, #1050, #1656, #1801, #2076, #2091, #2150, #2253, #2306, #2400, #2430, #2482, #2496, #2676, #2737, #2874, #2902, #2939, #3037, #3141, #3288, #3440, #3592, #3739, #4088, #4260, #4289, #4306, #4358, #4464, #4510, #4858, #5012, #5020, #5261, #5295, #5321, #5324, #5325, #5555, #5707, #5796, #5817, #5987, #6018, #6074, #6088, #6154, #6177, #6180, #6203, #6658
git-svn-id: http://code.djangoproject.com/svn/django/trunk@7477 bcc190cf-cafb-0310-a4f2-bffc1f526a37
2008-04-27 10:50:16 +08:00
# existing fields, m2m fields, m2m related objects or related
# objects
2007-08-16 14:06:55 +08:00
if f . rel . to not in models . get_models ( ) :
2008-08-19 22:17:24 +08:00
e . add ( opts , " ' %s ' has an m2m relation with model %s , which has either not been installed or is abstract. " % ( f . name , f . rel . to ) )
2007-12-03 02:10:07 +08:00
# it is a string and we could not find the model it refers to
# so skip the next section
if isinstance ( f . rel . to , ( str , unicode ) ) :
continue
2008-08-24 00:56:41 +08:00
# Check that the field is not set to unique. ManyToManyFields do not support unique.
if f . unique :
e . add ( opts , " ManyToManyFields cannot be unique. Remove the unique argument on ' %s ' . " % f . name )
2009-11-03 22:02:49 +08:00
if f . rel . through is not None and not isinstance ( f . rel . through , basestring ) :
from_model , to_model = cls , f . rel . to
if from_model == to_model and f . rel . symmetrical and not f . rel . through . _meta . auto_created :
e . add ( opts , " Many-to-many fields with intermediate tables cannot be symmetrical. " )
seen_from , seen_to , seen_self = False , False , 0
for inter_field in f . rel . through . _meta . fields :
rel_to = getattr ( inter_field . rel , ' to ' , None )
if from_model == to_model : # relation to self
if rel_to == from_model :
seen_self + = 1
if seen_self > 2 :
e . add ( opts , " Intermediary model %s has more than "
" two foreign keys to %s , which is ambiguous "
" and is not permitted. " % (
f . rel . through . _meta . object_name ,
from_model . _meta . object_name
)
)
2008-07-29 20:41:08 +08:00
else :
2009-11-03 22:02:49 +08:00
if rel_to == from_model :
if seen_from :
e . add ( opts , " Intermediary model %s has more "
" than one foreign key to %s , which is "
" ambiguous and is not permitted. " % (
f . rel . through . _meta . object_name ,
from_model . _meta . object_name
)
)
else :
seen_from = True
elif rel_to == to_model :
if seen_to :
e . add ( opts , " Intermediary model %s has more "
" than one foreign key to %s , which is "
" ambiguous and is not permitted. " % (
f . rel . through . _meta . object_name ,
rel_to . _meta . object_name
)
)
else :
seen_to = True
if f . rel . through not in models . get_models ( include_auto_created = True ) :
e . add ( opts , " ' %s ' specifies an m2m relation through model "
" %s , which has not been installed. " % ( f . name , f . rel . through )
)
signature = ( f . rel . to , cls , f . rel . through )
if signature in seen_intermediary_signatures :
e . add ( opts , " The model %s has two manually-defined m2m "
" relations through the model %s , which is not "
" permitted. Please consider using an extra field on "
" your intermediary model instead. " % (
cls . _meta . object_name ,
f . rel . through . _meta . object_name
)
)
2008-07-29 20:41:08 +08:00
else :
2009-11-03 22:02:49 +08:00
seen_intermediary_signatures . append ( signature )
seen_related_fk , seen_this_fk = False , False
for field in f . rel . through . _meta . fields :
if field . rel :
if not seen_related_fk and field . rel . to == f . rel . to :
seen_related_fk = True
elif field . rel . to == cls :
seen_this_fk = True
if not seen_related_fk or not seen_this_fk :
e . add ( opts , " ' %s ' has a manually-defined m2m relation "
" through model %s , which does not have foreign keys "
" to %s and %s " % ( f . name , f . rel . through . _meta . object_name ,
f . rel . to . _meta . object_name , cls . _meta . object_name )
)
elif isinstance ( f . rel . through , basestring ) :
e . add ( opts , " ' %s ' specifies an m2m relation through model %s , "
" which has not been installed " % ( f . name , f . rel . through )
)
2008-08-24 00:56:41 +08:00
2007-12-03 02:10:07 +08:00
rel_opts = f . rel . to . _meta
2007-08-16 14:06:55 +08:00
rel_name = RelatedObject ( f . rel . to , cls , f ) . get_accessor_name ( )
rel_query_name = f . related_query_name ( )
Merged the queryset-refactor branch into trunk.
This is a big internal change, but mostly backwards compatible with existing
code. Also adds a couple of new features.
Fixed #245, #1050, #1656, #1801, #2076, #2091, #2150, #2253, #2306, #2400, #2430, #2482, #2496, #2676, #2737, #2874, #2902, #2939, #3037, #3141, #3288, #3440, #3592, #3739, #4088, #4260, #4289, #4306, #4358, #4464, #4510, #4858, #5012, #5020, #5261, #5295, #5321, #5324, #5325, #5555, #5707, #5796, #5817, #5987, #6018, #6074, #6088, #6154, #6177, #6180, #6203, #6658
git-svn-id: http://code.djangoproject.com/svn/django/trunk@7477 bcc190cf-cafb-0310-a4f2-bffc1f526a37
2008-04-27 10:50:16 +08:00
# If rel_name is none, there is no reverse accessor (this only
# occurs for symmetrical m2m relations to self). If this is the
# case, there are no clashes to check for this field, as there are
# no reverse descriptors for this field.
2007-08-16 14:06:55 +08:00
if rel_name is not None :
for r in rel_opts . fields :
if r . name == rel_name :
e . add ( opts , " Accessor for m2m field ' %s ' clashes with field ' %s . %s ' . Add a related_name argument to the definition for ' %s ' . " % ( f . name , rel_opts . object_name , r . name , f . name ) )
if r . name == rel_query_name :
e . add ( opts , " Reverse query name for m2m field ' %s ' clashes with field ' %s . %s ' . Add a related_name argument to the definition for ' %s ' . " % ( f . name , rel_opts . object_name , r . name , f . name ) )
Merged the queryset-refactor branch into trunk.
This is a big internal change, but mostly backwards compatible with existing
code. Also adds a couple of new features.
Fixed #245, #1050, #1656, #1801, #2076, #2091, #2150, #2253, #2306, #2400, #2430, #2482, #2496, #2676, #2737, #2874, #2902, #2939, #3037, #3141, #3288, #3440, #3592, #3739, #4088, #4260, #4289, #4306, #4358, #4464, #4510, #4858, #5012, #5020, #5261, #5295, #5321, #5324, #5325, #5555, #5707, #5796, #5817, #5987, #6018, #6074, #6088, #6154, #6177, #6180, #6203, #6658
git-svn-id: http://code.djangoproject.com/svn/django/trunk@7477 bcc190cf-cafb-0310-a4f2-bffc1f526a37
2008-04-27 10:50:16 +08:00
for r in rel_opts . local_many_to_many :
2007-08-16 14:06:55 +08:00
if r . name == rel_name :
e . add ( opts , " Accessor for m2m field ' %s ' clashes with m2m field ' %s . %s ' . Add a related_name argument to the definition for ' %s ' . " % ( f . name , rel_opts . object_name , r . name , f . name ) )
if r . name == rel_query_name :
e . add ( opts , " Reverse query name for m2m field ' %s ' clashes with m2m field ' %s . %s ' . Add a related_name argument to the definition for ' %s ' . " % ( f . name , rel_opts . object_name , r . name , f . name ) )
for r in rel_opts . get_all_related_many_to_many_objects ( ) :
if r . field is not f :
if r . get_accessor_name ( ) == rel_name :
e . add ( opts , " Accessor for m2m field ' %s ' clashes with related m2m field ' %s . %s ' . Add a related_name argument to the definition for ' %s ' . " % ( f . name , rel_opts . object_name , r . get_accessor_name ( ) , f . name ) )
if r . get_accessor_name ( ) == rel_query_name :
e . add ( opts , " Reverse query name for m2m field ' %s ' clashes with related m2m field ' %s . %s ' . Add a related_name argument to the definition for ' %s ' . " % ( f . name , rel_opts . object_name , r . get_accessor_name ( ) , f . name ) )
for r in rel_opts . get_all_related_objects ( ) :
if r . get_accessor_name ( ) == rel_name :
e . add ( opts , " Accessor for m2m field ' %s ' clashes with related field ' %s . %s ' . Add a related_name argument to the definition for ' %s ' . " % ( f . name , rel_opts . object_name , r . get_accessor_name ( ) , f . name ) )
if r . get_accessor_name ( ) == rel_query_name :
e . add ( opts , " Reverse query name for m2m field ' %s ' clashes with related field ' %s . %s ' . Add a related_name argument to the definition for ' %s ' . " % ( f . name , rel_opts . object_name , r . get_accessor_name ( ) , f . name ) )
# Check ordering attribute.
if opts . ordering :
for field_name in opts . ordering :
if field_name == ' ? ' : continue
if field_name . startswith ( ' - ' ) :
field_name = field_name [ 1 : ]
if opts . order_with_respect_to and field_name == ' _order ' :
continue
Merged the queryset-refactor branch into trunk.
This is a big internal change, but mostly backwards compatible with existing
code. Also adds a couple of new features.
Fixed #245, #1050, #1656, #1801, #2076, #2091, #2150, #2253, #2306, #2400, #2430, #2482, #2496, #2676, #2737, #2874, #2902, #2939, #3037, #3141, #3288, #3440, #3592, #3739, #4088, #4260, #4289, #4306, #4358, #4464, #4510, #4858, #5012, #5020, #5261, #5295, #5321, #5324, #5325, #5555, #5707, #5796, #5817, #5987, #6018, #6074, #6088, #6154, #6177, #6180, #6203, #6658
git-svn-id: http://code.djangoproject.com/svn/django/trunk@7477 bcc190cf-cafb-0310-a4f2-bffc1f526a37
2008-04-27 10:50:16 +08:00
# Skip ordering in the format field1__field2 (FIXME: checking
# this format would be nice, but it's a little fiddly).
if ' _ ' in field_name :
continue
2007-08-16 14:06:55 +08:00
try :
opts . get_field ( field_name , many_to_many = False )
except models . FieldDoesNotExist :
e . add ( opts , ' " ordering " refers to " %s " , a field that doesn \' t exist. ' % field_name )
# Check unique_together.
for ut in opts . unique_together :
for field_name in ut :
try :
f = opts . get_field ( field_name , many_to_many = True )
except models . FieldDoesNotExist :
e . add ( opts , ' " unique_together " refers to %s , a field that doesn \' t exist. Check your syntax. ' % field_name )
else :
if isinstance ( f . rel , models . ManyToManyRel ) :
e . add ( opts , ' " unique_together " refers to %s . ManyToManyFields are not supported in unique_together. ' % f . name )
Merged the queryset-refactor branch into trunk.
This is a big internal change, but mostly backwards compatible with existing
code. Also adds a couple of new features.
Fixed #245, #1050, #1656, #1801, #2076, #2091, #2150, #2253, #2306, #2400, #2430, #2482, #2496, #2676, #2737, #2874, #2902, #2939, #3037, #3141, #3288, #3440, #3592, #3739, #4088, #4260, #4289, #4306, #4358, #4464, #4510, #4858, #5012, #5020, #5261, #5295, #5321, #5324, #5325, #5555, #5707, #5796, #5817, #5987, #6018, #6074, #6088, #6154, #6177, #6180, #6203, #6658
git-svn-id: http://code.djangoproject.com/svn/django/trunk@7477 bcc190cf-cafb-0310-a4f2-bffc1f526a37
2008-04-27 10:50:16 +08:00
if f not in opts . local_fields :
e . add ( opts , ' " unique_together " refers to %s . This is not in the same model as the unique_together statement. ' % f . name )
2007-08-16 14:06:55 +08:00
return len ( e . errors )