web/lib/django/contrib/admin/validation.py
changeset 29 cc9b7e14412b
parent 0 0d40e90630ef
equal deleted inserted replaced
28:b758351d191f 29:cc9b7e14412b
     1 try:
       
     2     set
       
     3 except NameError:
       
     4     from sets import Set as set   # Python 2.3 fallback
       
     5 
       
     6 from django.core.exceptions import ImproperlyConfigured
     1 from django.core.exceptions import ImproperlyConfigured
     7 from django.db import models
     2 from django.db import models
     8 from django.forms.models import BaseModelForm, BaseModelFormSet, fields_for_model, _get_foreign_key
     3 from django.forms.models import (BaseModelForm, BaseModelFormSet, fields_for_model,
       
     4     _get_foreign_key)
     9 from django.contrib.admin.options import flatten_fieldsets, BaseModelAdmin
     5 from django.contrib.admin.options import flatten_fieldsets, BaseModelAdmin
    10 from django.contrib.admin.options import HORIZONTAL, VERTICAL
     6 from django.contrib.admin.options import HORIZONTAL, VERTICAL
       
     7 from django.contrib.admin.util import lookup_field
       
     8 
    11 
     9 
    12 __all__ = ['validate']
    10 __all__ = ['validate']
    13 
    11 
    14 def validate(cls, model):
    12 def validate(cls, model):
    15     """
    13     """
   121             # this format would be nice, but it's a little fiddly).
   119             # this format would be nice, but it's a little fiddly).
   122             if '__' in field:
   120             if '__' in field:
   123                 continue
   121                 continue
   124             get_field(cls, model, opts, 'ordering[%d]' % idx, field)
   122             get_field(cls, model, opts, 'ordering[%d]' % idx, field)
   125 
   123 
       
   124     if hasattr(cls, "readonly_fields"):
       
   125         check_isseq(cls, "readonly_fields", cls.readonly_fields)
       
   126         for idx, field in enumerate(cls.readonly_fields):
       
   127             if not callable(field):
       
   128                 if not hasattr(cls, field):
       
   129                     if not hasattr(model, field):
       
   130                         try:
       
   131                             opts.get_field(field)
       
   132                         except models.FieldDoesNotExist:
       
   133                             raise ImproperlyConfigured("%s.readonly_fields[%d], %r is not a callable or an attribute of %r or found in the model %r."
       
   134                                 % (cls.__name__, idx, field, cls.__name__, model._meta.object_name))
       
   135 
   126     # list_select_related = False
   136     # list_select_related = False
   127     # save_as = False
   137     # save_as = False
   128     # save_on_top = False
   138     # save_on_top = False
   129     for attr in ('list_select_related', 'save_as', 'save_on_top'):
   139     for attr in ('list_select_related', 'save_as', 'save_on_top'):
   130         if not isinstance(getattr(cls, attr), bool):
   140         if not isinstance(getattr(cls, attr), bool):
   147                         "inherit from models.Model." % (cls.__name__, idx))
   157                         "inherit from models.Model." % (cls.__name__, idx))
   148             validate_base(inline, inline.model)
   158             validate_base(inline, inline.model)
   149             validate_inline(inline, cls, model)
   159             validate_inline(inline, cls, model)
   150 
   160 
   151 def validate_inline(cls, parent, parent_model):
   161 def validate_inline(cls, parent, parent_model):
       
   162 
   152     # model is already verified to exist and be a Model
   163     # model is already verified to exist and be a Model
   153     if cls.fk_name: # default value is None
   164     if cls.fk_name: # default value is None
   154         f = get_field(cls, cls.model, cls.model._meta, 'fk_name', cls.fk_name)
   165         f = get_field(cls, cls.model, cls.model._meta, 'fk_name', cls.fk_name)
   155         if not isinstance(f, models.ForeignKey):
   166         if not isinstance(f, models.ForeignKey):
   156             raise ImproperlyConfigured("'%s.fk_name is not an instance of "
   167             raise ImproperlyConfigured("'%s.fk_name is not an instance of "
   157                     "models.ForeignKey." % cls.__name__)
   168                     "models.ForeignKey." % cls.__name__)
       
   169 
       
   170     fk = _get_foreign_key(parent_model, cls.model, fk_name=cls.fk_name, can_fail=True)
       
   171 
   158     # extra = 3
   172     # extra = 3
   159     # max_num = 0
   173     if not isinstance(getattr(cls, 'extra'), int):
   160     for attr in ('extra', 'max_num'):
   174         raise ImproperlyConfigured("'%s.extra' should be a integer."
   161         if not isinstance(getattr(cls, attr), int):
   175                 % cls.__name__)
   162             raise ImproperlyConfigured("'%s.%s' should be a integer."
   176 
   163                     % (cls.__name__, attr))
   177     # max_num = None
       
   178     max_num = getattr(cls, 'max_num', None)
       
   179     if max_num is not None and not isinstance(max_num, int):
       
   180         raise ImproperlyConfigured("'%s.max_num' should be an integer or None (default)."
       
   181                 % cls.__name__)
   164 
   182 
   165     # formset
   183     # formset
   166     if hasattr(cls, 'formset') and not issubclass(cls.formset, BaseModelFormSet):
   184     if hasattr(cls, 'formset') and not issubclass(cls.formset, BaseModelFormSet):
   167         raise ImproperlyConfigured("'%s.formset' does not inherit from "
   185         raise ImproperlyConfigured("'%s.formset' does not inherit from "
   168                 "BaseModelFormSet." % cls.__name__)
   186                 "BaseModelFormSet." % cls.__name__)
   169 
   187 
   170     # exclude
   188     # exclude
   171     if hasattr(cls, 'exclude') and cls.exclude:
   189     if hasattr(cls, 'exclude') and cls.exclude:
   172         fk = _get_foreign_key(parent_model, cls.model, can_fail=True)
       
   173         if fk and fk.name in cls.exclude:
   190         if fk and fk.name in cls.exclude:
   174             raise ImproperlyConfigured("%s cannot exclude the field "
   191             raise ImproperlyConfigured("%s cannot exclude the field "
   175                     "'%s' - this is the foreign key to the parent model "
   192                     "'%s' - this is the foreign key to the parent model "
   176                     "%s." % (cls.__name__, fk.name, parent_model.__name__))
   193                     "%s." % (cls.__name__, fk.name, parent_model.__name__))
   177 
   194 
   190 
   207 
   191     # fields
   208     # fields
   192     if cls.fields: # default value is None
   209     if cls.fields: # default value is None
   193         check_isseq(cls, 'fields', cls.fields)
   210         check_isseq(cls, 'fields', cls.fields)
   194         for field in cls.fields:
   211         for field in cls.fields:
       
   212             if field in cls.readonly_fields:
       
   213                 # Stuff can be put in fields that isn't actually a model field
       
   214                 # if it's in readonly_fields, readonly_fields will handle the
       
   215                 # validation of such things.
       
   216                 continue
   195             check_formfield(cls, model, opts, 'fields', field)
   217             check_formfield(cls, model, opts, 'fields', field)
       
   218             try:
       
   219                 f = opts.get_field(field)
       
   220             except models.FieldDoesNotExist:
       
   221                 # If we can't find a field on the model that matches,
       
   222                 # it could be an extra field on the form.
       
   223                 continue
       
   224             if isinstance(f, models.ManyToManyField) and not f.rel.through._meta.auto_created:
       
   225                 raise ImproperlyConfigured("'%s.fields' can't include the ManyToManyField "
       
   226                     "field '%s' because '%s' manually specifies "
       
   227                     "a 'through' model." % (cls.__name__, field, field))
   196         if cls.fieldsets:
   228         if cls.fieldsets:
   197             raise ImproperlyConfigured('Both fieldsets and fields are specified in %s.' % cls.__name__)
   229             raise ImproperlyConfigured('Both fieldsets and fields are specified in %s.' % cls.__name__)
   198         if len(cls.fields) > len(set(cls.fields)):
   230         if len(cls.fields) > len(set(cls.fields)):
   199             raise ImproperlyConfigured('There are duplicate field(s) in %s.fields' % cls.__name__)
   231             raise ImproperlyConfigured('There are duplicate field(s) in %s.fields' % cls.__name__)
   200 
   232 
   209             check_isdict(cls, 'fieldsets[%d][1]' % idx, fieldset[1])
   241             check_isdict(cls, 'fieldsets[%d][1]' % idx, fieldset[1])
   210             if 'fields' not in fieldset[1]:
   242             if 'fields' not in fieldset[1]:
   211                 raise ImproperlyConfigured("'fields' key is required in "
   243                 raise ImproperlyConfigured("'fields' key is required in "
   212                         "%s.fieldsets[%d][1] field options dict."
   244                         "%s.fieldsets[%d][1] field options dict."
   213                         % (cls.__name__, idx))
   245                         % (cls.__name__, idx))
       
   246             for fields in fieldset[1]['fields']:
       
   247                 # The entry in fields might be a tuple. If it is a standalone
       
   248                 # field, make it into a tuple to make processing easier.
       
   249                 if type(fields) != tuple:
       
   250                     fields = (fields,)
       
   251                 for field in fields:
       
   252                     if field in cls.readonly_fields:
       
   253                         # Stuff can be put in fields that isn't actually a
       
   254                         # model field if it's in readonly_fields,
       
   255                         # readonly_fields will handle the validation of such
       
   256                         # things.
       
   257                         continue
       
   258                     check_formfield(cls, model, opts, "fieldsets[%d][1]['fields']" % idx, field)
       
   259                     try:
       
   260                         f = opts.get_field(field)
       
   261                         if isinstance(f, models.ManyToManyField) and not f.rel.through._meta.auto_created:
       
   262                             raise ImproperlyConfigured("'%s.fieldsets[%d][1]['fields']' "
       
   263                                 "can't include the ManyToManyField field '%s' because "
       
   264                                 "'%s' manually specifies a 'through' model." % (
       
   265                                     cls.__name__, idx, field, field))
       
   266                     except models.FieldDoesNotExist:
       
   267                         # If we can't find a field on the model that matches,
       
   268                         # it could be an extra field on the form.
       
   269                         pass
   214         flattened_fieldsets = flatten_fieldsets(cls.fieldsets)
   270         flattened_fieldsets = flatten_fieldsets(cls.fieldsets)
   215         if len(flattened_fieldsets) > len(set(flattened_fieldsets)):
   271         if len(flattened_fieldsets) > len(set(flattened_fieldsets)):
   216             raise ImproperlyConfigured('There are duplicate field(s) in %s.fieldsets' % cls.__name__)
   272             raise ImproperlyConfigured('There are duplicate field(s) in %s.fieldsets' % cls.__name__)
   217         for field in flattened_fieldsets:
   273 
   218             check_formfield(cls, model, opts, "fieldsets[%d][1]['fields']" % idx, field)
   274     # exclude
       
   275     if cls.exclude: # default value is None
       
   276         check_isseq(cls, 'exclude', cls.exclude)
       
   277         for field in cls.exclude:
       
   278             check_formfield(cls, model, opts, 'exclude', field)
       
   279             try:
       
   280                 f = opts.get_field(field)
       
   281             except models.FieldDoesNotExist:
       
   282                 # If we can't find a field on the model that matches,
       
   283                 # it could be an extra field on the form.
       
   284                 continue
       
   285         if len(cls.exclude) > len(set(cls.exclude)):
       
   286             raise ImproperlyConfigured('There are duplicate field(s) in %s.exclude' % cls.__name__)
   219 
   287 
   220     # form
   288     # form
   221     if hasattr(cls, 'form') and not issubclass(cls.form, BaseModelForm):
   289     if hasattr(cls, 'form') and not issubclass(cls.form, BaseModelForm):
   222         raise ImproperlyConfigured("%s.form does not inherit from "
   290         raise ImproperlyConfigured("%s.form does not inherit from "
   223                 "BaseModelForm." % cls.__name__)
   291                 "BaseModelForm." % cls.__name__)