--- a/web/lib/django/contrib/admin/validation.py Wed May 19 17:43:59 2010 +0200
+++ b/web/lib/django/contrib/admin/validation.py Tue May 25 02:43:45 2010 +0200
@@ -1,13 +1,11 @@
-try:
- set
-except NameError:
- from sets import Set as set # Python 2.3 fallback
-
from django.core.exceptions import ImproperlyConfigured
from django.db import models
-from django.forms.models import BaseModelForm, BaseModelFormSet, fields_for_model, _get_foreign_key
+from django.forms.models import (BaseModelForm, BaseModelFormSet, fields_for_model,
+ _get_foreign_key)
from django.contrib.admin.options import flatten_fieldsets, BaseModelAdmin
from django.contrib.admin.options import HORIZONTAL, VERTICAL
+from django.contrib.admin.util import lookup_field
+
__all__ = ['validate']
@@ -123,6 +121,18 @@
continue
get_field(cls, model, opts, 'ordering[%d]' % idx, field)
+ if hasattr(cls, "readonly_fields"):
+ check_isseq(cls, "readonly_fields", cls.readonly_fields)
+ for idx, field in enumerate(cls.readonly_fields):
+ if not callable(field):
+ if not hasattr(cls, field):
+ if not hasattr(model, field):
+ try:
+ opts.get_field(field)
+ except models.FieldDoesNotExist:
+ raise ImproperlyConfigured("%s.readonly_fields[%d], %r is not a callable or an attribute of %r or found in the model %r."
+ % (cls.__name__, idx, field, cls.__name__, model._meta.object_name))
+
# list_select_related = False
# save_as = False
# save_on_top = False
@@ -149,18 +159,26 @@
validate_inline(inline, cls, model)
def validate_inline(cls, parent, parent_model):
+
# model is already verified to exist and be a Model
if cls.fk_name: # default value is None
f = get_field(cls, cls.model, cls.model._meta, 'fk_name', cls.fk_name)
if not isinstance(f, models.ForeignKey):
raise ImproperlyConfigured("'%s.fk_name is not an instance of "
"models.ForeignKey." % cls.__name__)
+
+ fk = _get_foreign_key(parent_model, cls.model, fk_name=cls.fk_name, can_fail=True)
+
# extra = 3
- # max_num = 0
- for attr in ('extra', 'max_num'):
- if not isinstance(getattr(cls, attr), int):
- raise ImproperlyConfigured("'%s.%s' should be a integer."
- % (cls.__name__, attr))
+ if not isinstance(getattr(cls, 'extra'), int):
+ raise ImproperlyConfigured("'%s.extra' should be a integer."
+ % cls.__name__)
+
+ # max_num = None
+ max_num = getattr(cls, 'max_num', None)
+ if max_num is not None and not isinstance(max_num, int):
+ raise ImproperlyConfigured("'%s.max_num' should be an integer or None (default)."
+ % cls.__name__)
# formset
if hasattr(cls, 'formset') and not issubclass(cls.formset, BaseModelFormSet):
@@ -169,7 +187,6 @@
# exclude
if hasattr(cls, 'exclude') and cls.exclude:
- fk = _get_foreign_key(parent_model, cls.model, can_fail=True)
if fk and fk.name in cls.exclude:
raise ImproperlyConfigured("%s cannot exclude the field "
"'%s' - this is the foreign key to the parent model "
@@ -192,7 +209,22 @@
if cls.fields: # default value is None
check_isseq(cls, 'fields', cls.fields)
for field in cls.fields:
+ if field in cls.readonly_fields:
+ # Stuff can be put in fields that isn't actually a model field
+ # if it's in readonly_fields, readonly_fields will handle the
+ # validation of such things.
+ continue
check_formfield(cls, model, opts, 'fields', field)
+ try:
+ f = opts.get_field(field)
+ except models.FieldDoesNotExist:
+ # If we can't find a field on the model that matches,
+ # it could be an extra field on the form.
+ continue
+ 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))
if cls.fieldsets:
raise ImproperlyConfigured('Both fieldsets and fields are specified in %s.' % cls.__name__)
if len(cls.fields) > len(set(cls.fields)):
@@ -211,11 +243,47 @@
raise ImproperlyConfigured("'fields' key is required in "
"%s.fieldsets[%d][1] field options dict."
% (cls.__name__, idx))
+ for fields in fieldset[1]['fields']:
+ # The entry in fields might be a tuple. If it is a standalone
+ # field, make it into a tuple to make processing easier.
+ if type(fields) != tuple:
+ fields = (fields,)
+ for field in fields:
+ if field in cls.readonly_fields:
+ # Stuff can be put in fields that isn't actually a
+ # model field if it's in readonly_fields,
+ # readonly_fields will handle the validation of such
+ # things.
+ continue
+ check_formfield(cls, model, opts, "fieldsets[%d][1]['fields']" % idx, field)
+ try:
+ f = opts.get_field(field)
+ if isinstance(f, models.ManyToManyField) and not f.rel.through._meta.auto_created:
+ raise ImproperlyConfigured("'%s.fieldsets[%d][1]['fields']' "
+ "can't include the ManyToManyField field '%s' because "
+ "'%s' manually specifies a 'through' model." % (
+ cls.__name__, idx, field, field))
+ except models.FieldDoesNotExist:
+ # If we can't find a field on the model that matches,
+ # it could be an extra field on the form.
+ pass
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:
- check_formfield(cls, model, opts, "fieldsets[%d][1]['fields']" % idx, field)
+
+ # exclude
+ if cls.exclude: # default value is None
+ check_isseq(cls, 'exclude', cls.exclude)
+ for field in cls.exclude:
+ check_formfield(cls, model, opts, 'exclude', field)
+ try:
+ f = opts.get_field(field)
+ except models.FieldDoesNotExist:
+ # If we can't find a field on the model that matches,
+ # it could be an extra field on the form.
+ continue
+ if len(cls.exclude) > len(set(cls.exclude)):
+ raise ImproperlyConfigured('There are duplicate field(s) in %s.exclude' % cls.__name__)
# form
if hasattr(cls, 'form') and not issubclass(cls.form, BaseModelForm):