2008-08-02 02:55:43 +08:00
try :
set
except NameError :
from sets import Set as set # Python 2.3 fallback
2008-07-19 07:54:34 +08:00
from django . core . exceptions import ImproperlyConfigured
from django . db import models
2009-05-03 21:39:33 +08:00
from django . forms . models import BaseModelForm , BaseModelFormSet , fields_for_model , _get_foreign_key
2008-07-19 07:54:34 +08:00
from django . contrib . admin . options import flatten_fieldsets , BaseModelAdmin
from django . contrib . admin . options import HORIZONTAL , VERTICAL
2008-08-27 07:06:24 +08:00
__all__ = [ ' validate ' ]
2008-07-19 07:54:34 +08:00
def validate ( cls , model ) :
"""
Does basic ModelAdmin option validation . Calls custom validation
classmethod in the end if it is provided in cls . The signature of the
custom validation classmethod should be : def validate ( cls , model ) .
"""
2008-08-27 07:06:30 +08:00
# Before we can introspect models, they need to be fully loaded so that
# inter-relations are set up correctly. We force that here.
models . get_apps ( )
2008-07-19 07:54:34 +08:00
opts = model . _meta
2008-08-27 07:06:24 +08:00
validate_base ( cls , model )
2008-07-19 07:54:34 +08:00
# list_display
if hasattr ( cls , ' list_display ' ) :
2008-08-27 07:06:24 +08:00
check_isseq ( cls , ' list_display ' , cls . list_display )
2008-07-19 07:54:34 +08:00
for idx , field in enumerate ( cls . list_display ) :
2008-08-15 04:12:19 +08:00
if not callable ( field ) :
if not hasattr ( cls , field ) :
if not hasattr ( model , field ) :
try :
2008-08-15 05:03:44 +08:00
opts . get_field ( field )
2008-08-15 04:12:19 +08:00
except models . FieldDoesNotExist :
2008-08-27 07:06:24 +08:00
raise ImproperlyConfigured ( " %s .list_display[ %d ], %r is not a callable or an attribute of %r or found in the model %r . "
2008-08-15 04:12:19 +08:00
% ( cls . __name__ , idx , field , cls . __name__ , model . _meta . object_name ) )
2008-12-03 23:47:19 +08:00
else :
# getattr(model, field) could be an X_RelatedObjectsDescriptor
2008-08-27 07:06:24 +08:00
f = fetch_attr ( cls , model , opts , " list_display[ %d ] " % idx , field )
2008-08-15 04:12:19 +08:00
if isinstance ( f , models . ManyToManyField ) :
2008-08-27 07:06:24 +08:00
raise ImproperlyConfigured ( " ' %s .list_display[ %d ] ' , ' %s ' is a ManyToManyField which is not supported. "
2008-08-15 04:12:19 +08:00
% ( cls . __name__ , idx , field ) )
2008-07-19 07:54:34 +08:00
# list_display_links
if hasattr ( cls , ' list_display_links ' ) :
2008-08-27 07:06:24 +08:00
check_isseq ( cls , ' list_display_links ' , cls . list_display_links )
2008-07-19 07:54:34 +08:00
for idx , field in enumerate ( cls . list_display_links ) :
2008-08-27 07:06:24 +08:00
fetch_attr ( cls , model , opts , ' list_display_links[ %d ] ' % idx , field )
2008-07-19 07:54:34 +08:00
if field not in cls . list_display :
2008-08-27 07:06:24 +08:00
raise ImproperlyConfigured ( " ' %s .list_display_links[ %d ] ' "
" refers to ' %s ' which is not defined in ' list_display ' . "
2008-07-19 07:54:34 +08:00
% ( cls . __name__ , idx , field ) )
# list_filter
if hasattr ( cls , ' list_filter ' ) :
2008-08-27 07:06:24 +08:00
check_isseq ( cls , ' list_filter ' , cls . list_filter )
2008-07-19 07:54:34 +08:00
for idx , field in enumerate ( cls . list_filter ) :
2008-08-27 07:06:24 +08:00
get_field ( cls , model , opts , ' list_filter[ %d ] ' % idx , field )
2008-07-19 07:54:34 +08:00
# list_per_page = 100
if hasattr ( cls , ' list_per_page ' ) and not isinstance ( cls . list_per_page , int ) :
2008-08-27 07:06:24 +08:00
raise ImproperlyConfigured ( " ' %s .list_per_page ' should be a integer. "
2008-07-19 07:54:34 +08:00
% cls . __name__ )
2009-03-24 04:22:56 +08:00
2009-03-18 04:51:47 +08:00
# list_editable
if hasattr ( cls , ' list_editable ' ) and cls . list_editable :
check_isseq ( cls , ' list_editable ' , cls . list_editable )
2009-03-18 11:03:24 +08:00
for idx , field_name in enumerate ( cls . list_editable ) :
2009-03-18 04:51:47 +08:00
try :
2009-03-18 11:03:24 +08:00
field = opts . get_field_by_name ( field_name ) [ 0 ]
2009-03-18 04:51:47 +08:00
except models . FieldDoesNotExist :
raise ImproperlyConfigured ( " ' %s .list_editable[ %d ] ' refers to a "
Fixed #10389, #10501, #10502, #10540, #10562, #10563, #10564, #10565, #10568, #10569, #10614, #10617, #10619 -- Fixed several typos as well as a couple minor issues in the docs, patches from timo, nih, bthomas, rduffield, UloPe, and sebleier@gmail.com.
git-svn-id: http://code.djangoproject.com/svn/django/trunk@10242 bcc190cf-cafb-0310-a4f2-bffc1f526a37
2009-03-31 15:01:01 +08:00
" field, ' %s ' , not defined on %s . "
2009-03-18 11:03:24 +08:00
% ( cls . __name__ , idx , field_name , model . __name__ ) )
if field_name not in cls . list_display :
2009-03-18 04:51:47 +08:00
raise ImproperlyConfigured ( " ' %s .list_editable[ %d ] ' refers to "
" ' %s ' which is not defined in ' list_display ' . "
2009-03-18 11:03:24 +08:00
% ( cls . __name__ , idx , field_name ) )
if field_name in cls . list_display_links :
2009-03-18 04:51:47 +08:00
raise ImproperlyConfigured ( " ' %s ' cannot be in both ' %s .list_editable ' "
" and ' %s .list_display_links ' "
2009-03-18 11:03:24 +08:00
% ( field_name , cls . __name__ , cls . __name__ ) )
if not cls . list_display_links and cls . list_display [ 0 ] in cls . list_editable :
raise ImproperlyConfigured ( " ' %s .list_editable[ %d ] ' refers to "
" the first field in list_display, ' %s ' , which can ' t be "
2009-03-24 04:22:56 +08:00
" used unless list_display_links is set. "
2009-03-18 11:03:24 +08:00
% ( cls . __name__ , idx , cls . list_display [ 0 ] ) )
if not field . editable :
raise ImproperlyConfigured ( " ' %s .list_editable[ %d ] ' refers to a "
" field, ' %s ' , which isn ' t editable through the admin. "
% ( cls . __name__ , idx , field_name ) )
2008-07-19 07:54:34 +08:00
# search_fields = ()
if hasattr ( cls , ' search_fields ' ) :
2008-08-27 07:06:24 +08:00
check_isseq ( cls , ' search_fields ' , cls . search_fields )
2008-07-19 07:54:34 +08:00
# date_hierarchy = None
if cls . date_hierarchy :
2008-08-27 07:06:24 +08:00
f = get_field ( cls , model , opts , ' date_hierarchy ' , cls . date_hierarchy )
2008-07-19 07:54:34 +08:00
if not isinstance ( f , ( models . DateField , models . DateTimeField ) ) :
2008-08-27 07:06:24 +08:00
raise ImproperlyConfigured ( " ' %s .date_hierarchy is "
2008-07-19 07:54:34 +08:00
" neither an instance of DateField nor DateTimeField. "
% cls . __name__ )
# ordering = None
if cls . ordering :
2008-08-27 07:06:24 +08:00
check_isseq ( cls , ' ordering ' , cls . ordering )
2008-07-19 07:54:34 +08:00
for idx , field in enumerate ( cls . ordering ) :
if field == ' ? ' and len ( cls . ordering ) != 1 :
2008-08-27 07:06:24 +08:00
raise ImproperlyConfigured ( " ' %s .ordering ' has the random "
" ordering marker ' ? ' , but contains other fields as "
" well. Please either remove ' ? ' or the other fields. "
2008-07-19 07:54:34 +08:00
% cls . __name__ )
if field == ' ? ' :
continue
if field . startswith ( ' - ' ) :
field = field [ 1 : ]
# Skip ordering in the format field1__field2 (FIXME: checking
# this format would be nice, but it's a little fiddly).
if ' __ ' in field :
continue
2008-08-27 07:06:24 +08:00
get_field ( cls , model , opts , ' ordering[ %d ] ' % idx , field )
2008-07-19 07:54:34 +08:00
# list_select_related = False
# save_as = False
# save_on_top = False
for attr in ( ' list_select_related ' , ' save_as ' , ' save_on_top ' ) :
if not isinstance ( getattr ( cls , attr ) , bool ) :
2008-08-27 07:06:24 +08:00
raise ImproperlyConfigured ( " ' %s . %s ' should be a boolean. "
2008-07-19 07:54:34 +08:00
% ( cls . __name__ , attr ) )
2009-03-24 04:22:56 +08:00
2008-07-19 07:54:34 +08:00
# inlines = []
if hasattr ( cls , ' inlines ' ) :
2008-08-27 07:06:24 +08:00
check_isseq ( cls , ' inlines ' , cls . inlines )
2008-07-19 07:54:34 +08:00
for idx , inline in enumerate ( cls . inlines ) :
if not issubclass ( inline , BaseModelAdmin ) :
2008-08-27 07:06:24 +08:00
raise ImproperlyConfigured ( " ' %s .inlines[ %d ] ' does not inherit "
2008-07-19 07:54:34 +08:00
" from BaseModelAdmin. " % ( cls . __name__ , idx ) )
if not inline . model :
2008-08-27 07:06:24 +08:00
raise ImproperlyConfigured ( " ' model ' is a required attribute "
" of ' %s .inlines[ %d ] ' . " % ( cls . __name__ , idx ) )
2008-07-19 07:54:34 +08:00
if not issubclass ( inline . model , models . Model ) :
2008-08-27 07:06:24 +08:00
raise ImproperlyConfigured ( " ' %s .inlines[ %d ].model ' does not "
2008-07-19 07:54:34 +08:00
" inherit from models.Model. " % ( cls . __name__ , idx ) )
2008-08-27 07:06:24 +08:00
validate_base ( inline , inline . model )
2009-05-03 21:39:33 +08:00
validate_inline ( inline , cls , model )
2008-08-27 07:06:24 +08:00
2009-05-03 21:39:33 +08:00
def validate_inline ( cls , parent , parent_model ) :
2009-11-13 20:35:05 +08:00
2008-07-19 07:54:34 +08:00
# model is already verified to exist and be a Model
if cls . fk_name : # default value is None
2008-09-10 14:06:04 +08:00
f = get_field ( cls , cls . model , cls . model . _meta , ' fk_name ' , cls . fk_name )
2008-07-19 07:54:34 +08:00
if not isinstance ( f , models . ForeignKey ) :
2008-08-27 07:06:24 +08:00
raise ImproperlyConfigured ( " ' %s .fk_name is not an instance of "
2008-07-19 07:54:34 +08:00
" models.ForeignKey. " % cls . __name__ )
2009-10-24 04:47:29 +08:00
fk = _get_foreign_key ( parent_model , cls . model , fk_name = cls . fk_name , can_fail = True )
2008-07-19 07:54:34 +08:00
# extra = 3
# max_num = 0
for attr in ( ' extra ' , ' max_num ' ) :
if not isinstance ( getattr ( cls , attr ) , int ) :
2008-08-27 07:06:24 +08:00
raise ImproperlyConfigured ( " ' %s . %s ' should be a integer. "
2008-07-19 07:54:34 +08:00
% ( cls . __name__ , attr ) )
# formset
if hasattr ( cls , ' formset ' ) and not issubclass ( cls . formset , BaseModelFormSet ) :
2008-08-27 07:06:24 +08:00
raise ImproperlyConfigured ( " ' %s .formset ' does not inherit from "
2008-07-19 07:54:34 +08:00
" BaseModelFormSet. " % cls . __name__ )
2009-05-03 21:39:33 +08:00
# exclude
if hasattr ( cls , ' exclude ' ) and cls . exclude :
2009-05-11 05:09:38 +08:00
if fk and fk . name in cls . exclude :
2009-05-03 21:39:33 +08:00
raise ImproperlyConfigured ( " %s cannot exclude the field "
" ' %s ' - this is the foreign key to the parent model "
2009-05-11 05:09:38 +08:00
" %s . " % ( cls . __name__ , fk . name , parent_model . __name__ ) )
2009-05-03 21:39:33 +08:00
2008-08-27 07:06:24 +08:00
def validate_base ( cls , model ) :
2008-07-19 07:54:34 +08:00
opts = model . _meta
# raw_id_fields
if hasattr ( cls , ' raw_id_fields ' ) :
2008-08-27 07:06:24 +08:00
check_isseq ( cls , ' raw_id_fields ' , cls . raw_id_fields )
2008-07-19 07:54:34 +08:00
for idx , field in enumerate ( cls . raw_id_fields ) :
2008-08-27 07:06:24 +08:00
f = get_field ( cls , model , opts , ' raw_id_fields ' , field )
2008-07-19 07:54:34 +08:00
if not isinstance ( f , ( models . ForeignKey , models . ManyToManyField ) ) :
2008-08-27 07:06:24 +08:00
raise ImproperlyConfigured ( " ' %s .raw_id_fields[ %d ] ' , ' %s ' must "
2008-07-19 07:54:34 +08:00
" be either a ForeignKey or ManyToManyField. "
% ( cls . __name__ , idx , field ) )
# fields
if cls . fields : # default value is None
2008-08-27 07:06:24 +08:00
check_isseq ( cls , ' fields ' , cls . fields )
2008-07-19 07:54:34 +08:00
for field in cls . fields :
2008-08-27 07:06:24 +08:00
check_formfield ( cls , model , opts , ' fields ' , field )
2009-11-13 20:35:05 +08:00
f = get_field ( cls , model , opts , ' fields ' , field )
if isinstance ( f , models . ManyToManyField ) and not f . rel . through . _meta . auto_created :
raise ImproperlyConfigured ( " ' %s .fields ' can ' t include the ManyToManyField field ' %s ' because ' %s ' manually specifies a ' through ' model. " % ( cls . __name__ , field , field ) )
2008-07-19 07:54:34 +08:00
if cls . fieldsets :
raise ImproperlyConfigured ( ' Both fieldsets and fields are specified in %s . ' % cls . __name__ )
2008-08-02 02:55:43 +08:00
if len ( cls . fields ) > len ( set ( cls . fields ) ) :
raise ImproperlyConfigured ( ' There are duplicate field(s) in %s .fields ' % cls . __name__ )
2008-07-19 07:54:34 +08:00
# fieldsets
if cls . fieldsets : # default value is None
2008-08-27 07:06:24 +08:00
check_isseq ( cls , ' fieldsets ' , cls . fieldsets )
2008-07-19 07:54:34 +08:00
for idx , fieldset in enumerate ( cls . fieldsets ) :
2008-08-27 07:06:24 +08:00
check_isseq ( cls , ' fieldsets[ %d ] ' % idx , fieldset )
2008-07-19 07:54:34 +08:00
if len ( fieldset ) != 2 :
2008-08-27 07:06:24 +08:00
raise ImproperlyConfigured ( " ' %s .fieldsets[ %d ] ' does not "
2008-07-19 07:54:34 +08:00
" have exactly two elements. " % ( cls . __name__ , idx ) )
2008-08-27 07:06:24 +08:00
check_isdict ( cls , ' fieldsets[ %d ][1] ' % idx , fieldset [ 1 ] )
2008-07-19 07:54:34 +08:00
if ' fields ' not in fieldset [ 1 ] :
2008-08-27 07:06:24 +08:00
raise ImproperlyConfigured ( " ' fields ' key is required in "
2008-07-19 07:54:34 +08:00
" %s .fieldsets[ %d ][1] field options dict. "
% ( cls . __name__ , idx ) )
2008-08-02 02:55:43 +08:00
flattened_fieldsets = flatten_fieldsets ( cls . fieldsets )
if len ( flattened_fieldsets ) > len ( set ( flattened_fieldsets ) ) :
raise ImproperlyConfigured ( ' There are duplicate field(s) in %s .fieldsets ' % cls . __name__ )
for field in flattened_fieldsets :
2008-08-27 07:06:24 +08:00
check_formfield ( cls , model , opts , " fieldsets[ %d ][1][ ' fields ' ] " % idx , field )
2008-07-19 07:54:34 +08:00
# form
if hasattr ( cls , ' form ' ) and not issubclass ( cls . form , BaseModelForm ) :
raise ImproperlyConfigured ( " %s .form does not inherit from "
" BaseModelForm. " % cls . __name__ )
# filter_vertical
if hasattr ( cls , ' filter_vertical ' ) :
2008-08-27 07:06:24 +08:00
check_isseq ( cls , ' filter_vertical ' , cls . filter_vertical )
2008-07-19 07:54:34 +08:00
for idx , field in enumerate ( cls . filter_vertical ) :
2008-08-27 07:06:24 +08:00
f = get_field ( cls , model , opts , ' filter_vertical ' , field )
2008-07-19 07:54:34 +08:00
if not isinstance ( f , models . ManyToManyField ) :
2008-08-27 07:06:24 +08:00
raise ImproperlyConfigured ( " ' %s .filter_vertical[ %d ] ' must be "
2008-07-19 07:54:34 +08:00
" a ManyToManyField. " % ( cls . __name__ , idx ) )
# filter_horizontal
if hasattr ( cls , ' filter_horizontal ' ) :
2008-08-27 07:06:24 +08:00
check_isseq ( cls , ' filter_horizontal ' , cls . filter_horizontal )
2008-07-19 07:54:34 +08:00
for idx , field in enumerate ( cls . filter_horizontal ) :
2008-08-27 07:06:24 +08:00
f = get_field ( cls , model , opts , ' filter_horizontal ' , field )
2008-07-19 07:54:34 +08:00
if not isinstance ( f , models . ManyToManyField ) :
2008-08-27 07:06:24 +08:00
raise ImproperlyConfigured ( " ' %s .filter_horizontal[ %d ] ' must be "
2008-07-19 07:54:34 +08:00
" a ManyToManyField. " % ( cls . __name__ , idx ) )
# radio_fields
if hasattr ( cls , ' radio_fields ' ) :
2008-08-27 07:06:24 +08:00
check_isdict ( cls , ' radio_fields ' , cls . radio_fields )
2008-07-19 07:54:34 +08:00
for field , val in cls . radio_fields . items ( ) :
2008-08-27 07:06:24 +08:00
f = get_field ( cls , model , opts , ' radio_fields ' , field )
2008-07-19 07:54:34 +08:00
if not ( isinstance ( f , models . ForeignKey ) or f . choices ) :
2008-08-27 07:06:24 +08:00
raise ImproperlyConfigured ( " ' %s .radio_fields[ ' %s ' ] ' "
2008-07-19 07:54:34 +08:00
" is neither an instance of ForeignKey nor does "
" have choices set. " % ( cls . __name__ , field ) )
if not val in ( HORIZONTAL , VERTICAL ) :
2008-08-27 07:06:24 +08:00
raise ImproperlyConfigured ( " ' %s .radio_fields[ ' %s ' ] ' "
2008-07-19 07:54:34 +08:00
" is neither admin.HORIZONTAL nor admin.VERTICAL. "
% ( cls . __name__ , field ) )
# prepopulated_fields
if hasattr ( cls , ' prepopulated_fields ' ) :
2008-08-27 07:06:24 +08:00
check_isdict ( cls , ' prepopulated_fields ' , cls . prepopulated_fields )
2008-07-19 07:54:34 +08:00
for field , val in cls . prepopulated_fields . items ( ) :
2008-08-27 07:06:24 +08:00
f = get_field ( cls , model , opts , ' prepopulated_fields ' , field )
2008-07-19 07:54:34 +08:00
if isinstance ( f , ( models . DateTimeField , models . ForeignKey ,
models . ManyToManyField ) ) :
2008-08-27 07:06:24 +08:00
raise ImproperlyConfigured ( " ' %s .prepopulated_fields[ ' %s ' ] ' "
2008-07-19 07:54:34 +08:00
" is either a DateTimeField, ForeignKey or "
" ManyToManyField. This isn ' t allowed. "
% ( cls . __name__ , field ) )
2008-08-27 07:06:24 +08:00
check_isseq ( cls , " prepopulated_fields[ ' %s ' ] " % field , val )
2008-07-19 07:54:34 +08:00
for idx , f in enumerate ( val ) :
2008-09-10 14:06:04 +08:00
get_field ( cls , model , opts , " prepopulated_fields[ ' %s ' ][ %d ] " % ( field , idx ) , f )
2008-07-19 07:54:34 +08:00
2008-08-27 07:06:24 +08:00
def check_isseq ( cls , label , obj ) :
2008-07-19 07:54:34 +08:00
if not isinstance ( obj , ( list , tuple ) ) :
2008-09-10 14:06:04 +08:00
raise ImproperlyConfigured ( " ' %s . %s ' must be a list or tuple. " % ( cls . __name__ , label ) )
2008-07-19 07:54:34 +08:00
2008-08-27 07:06:24 +08:00
def check_isdict ( cls , label , obj ) :
2008-07-19 07:54:34 +08:00
if not isinstance ( obj , dict ) :
2008-09-10 14:06:04 +08:00
raise ImproperlyConfigured ( " ' %s . %s ' must be a dictionary. " % ( cls . __name__ , label ) )
2008-07-19 07:54:34 +08:00
2008-08-27 07:06:24 +08:00
def get_field ( cls , model , opts , label , field ) :
2008-07-19 07:54:34 +08:00
try :
return opts . get_field ( field )
except models . FieldDoesNotExist :
2008-08-27 07:06:24 +08:00
raise ImproperlyConfigured ( " ' %s . %s ' refers to field ' %s ' that is missing from model ' %s ' . "
2008-07-19 07:54:34 +08:00
% ( cls . __name__ , label , field , model . __name__ ) )
2008-08-27 07:06:24 +08:00
def check_formfield ( cls , model , opts , label , field ) :
2008-08-28 23:43:04 +08:00
if getattr ( cls . form , ' base_fields ' , None ) :
2008-07-19 07:54:34 +08:00
try :
cls . form . base_fields [ field ]
except KeyError :
2008-08-27 07:06:24 +08:00
raise ImproperlyConfigured ( " ' %s . %s ' refers to field ' %s ' that "
2008-07-19 07:54:34 +08:00
" is missing from the form. " % ( cls . __name__ , label , field ) )
else :
fields = fields_for_model ( model )
try :
fields [ field ]
except KeyError :
2008-08-27 07:06:24 +08:00
raise ImproperlyConfigured ( " ' %s . %s ' refers to field ' %s ' that "
2008-07-19 07:54:34 +08:00
" is missing from the form. " % ( cls . __name__ , label , field ) )
2008-08-27 07:06:24 +08:00
def fetch_attr ( cls , model , opts , label , field ) :
2008-07-19 07:54:34 +08:00
try :
return opts . get_field ( field )
except models . FieldDoesNotExist :
2008-08-27 07:06:24 +08:00
pass
try :
2008-07-19 07:54:34 +08:00
return getattr ( model , field )
2008-08-27 07:06:24 +08:00
except AttributeError :
raise ImproperlyConfigured ( " ' %s . %s ' refers to ' %s ' that is neither a field, method or property of model ' %s ' . "
% ( cls . __name__ , label , field , model . __name__ ) )