| 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 |
| from django.contrib.admin.options import flatten_fieldsets, BaseModelAdmin |
| from django.contrib.admin.options import HORIZONTAL, VERTICAL |
| |
| 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). |
| """ |
| opts = model._meta |
| _validate_base(cls, model) |
| |
| # currying is expensive, use wrappers instead |
| def _check_istuplew(label, obj): |
| _check_istuple(cls, label, obj) |
| |
| def _check_isdictw(label, obj): |
| _check_isdict(cls, label, obj) |
| |
| def _check_field_existsw(label, field): |
| return _check_field_exists(cls, model, opts, label, field) |
| |
| def _check_attr_existsw(label, field): |
| return _check_attr_exists(cls, model, opts, label, field) |
| |
| # list_display |
| if hasattr(cls, 'list_display'): |
| _check_istuplew('list_display', cls.list_display) |
| for idx, field in enumerate(cls.list_display): |
| 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.list_display[%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)) |
| f = _check_attr_existsw("list_display[%d]" % idx, field) |
| if isinstance(f, models.ManyToManyField): |
| raise ImproperlyConfigured("`%s.list_display[%d]`, `%s` is a " |
| "ManyToManyField which is not supported." |
| % (cls.__name__, idx, field)) |
| |
| # list_display_links |
| if hasattr(cls, 'list_display_links'): |
| _check_istuplew('list_display_links', cls.list_display_links) |
| for idx, field in enumerate(cls.list_display_links): |
| _check_attr_existsw('list_display_links[%d]' % idx, field) |
| if field not in cls.list_display: |
| raise ImproperlyConfigured("`%s.list_display_links[%d]`" |
| "refers to `%s` which is not defined in `list_display`." |
| % (cls.__name__, idx, field)) |
| |
| # list_filter |
| if hasattr(cls, 'list_filter'): |
| _check_istuplew('list_filter', cls.list_filter) |
| for idx, field in enumerate(cls.list_filter): |
| _check_field_existsw('list_filter[%d]' % idx, field) |
| |
| # list_per_page = 100 |
| if hasattr(cls, 'list_per_page') and not isinstance(cls.list_per_page, int): |
| raise ImproperlyConfigured("`%s.list_per_page` should be a integer." |
| % cls.__name__) |
| |
| # search_fields = () |
| if hasattr(cls, 'search_fields'): |
| _check_istuplew('search_fields', cls.search_fields) |
| |
| # date_hierarchy = None |
| if cls.date_hierarchy: |
| f = _check_field_existsw('date_hierarchy', cls.date_hierarchy) |
| if not isinstance(f, (models.DateField, models.DateTimeField)): |
| raise ImproperlyConfigured("`%s.date_hierarchy is " |
| "neither an instance of DateField nor DateTimeField." |
| % cls.__name__) |
| |
| # ordering = None |
| if cls.ordering: |
| _check_istuplew('ordering', cls.ordering) |
| for idx, field in enumerate(cls.ordering): |
| if field == '?' and len(cls.ordering) != 1: |
| raise ImproperlyConfigured("`%s.ordering` has the random " |
| "ordering marker `?`, but contains other fields as " |
| "well. Please either remove `?` or the other fields." |
| % 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 |
| _check_field_existsw('ordering[%d]' % idx, field) |
| |
| # 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): |
| raise ImproperlyConfigured("`%s.%s` should be a boolean." |
| % (cls.__name__, attr)) |
| |
| # inlines = [] |
| if hasattr(cls, 'inlines'): |
| _check_istuplew('inlines', cls.inlines) |
| for idx, inline in enumerate(cls.inlines): |
| if not issubclass(inline, BaseModelAdmin): |
| raise ImproperlyConfigured("`%s.inlines[%d]` does not inherit " |
| "from BaseModelAdmin." % (cls.__name__, idx)) |
| if not inline.model: |
| raise ImproperlyConfigured("`model` is a required attribute " |
| "of `%s.inlines[%d]`." % (cls.__name__, idx)) |
| if not issubclass(inline.model, models.Model): |
| raise ImproperlyConfigured("`%s.inlines[%d].model` does not " |
| "inherit from models.Model." % (cls.__name__, idx)) |
| _validate_base(inline, inline.model) |
| _validate_inline(inline) |
| |
| def _validate_inline(cls): |
| # model is already verified to exist and be a Model |
| if cls.fk_name: # default value is None |
| f = _check_field_exists(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__) |
| # 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)) |
| |
| # formset |
| if hasattr(cls, 'formset') and not issubclass(cls.formset, BaseModelFormSet): |
| raise ImproperlyConfigured("`%s.formset` does not inherit from " |
| "BaseModelFormSet." % cls.__name__) |
| |
| def _validate_base(cls, model): |
| opts = model._meta |
| # currying is expensive, use wrappers instead |
| def _check_istuplew(label, obj): |
| _check_istuple(cls, label, obj) |
| |
| def _check_isdictw(label, obj): |
| _check_isdict(cls, label, obj) |
| |
| def _check_field_existsw(label, field): |
| return _check_field_exists(cls, model, opts, label, field) |
| |
| def _check_form_field_existsw(label, field): |
| return _check_form_field_exists(cls, model, opts, label, field) |
| |
| # raw_id_fields |
| if hasattr(cls, 'raw_id_fields'): |
| _check_istuplew('raw_id_fields', cls.raw_id_fields) |
| for idx, field in enumerate(cls.raw_id_fields): |
| f = _check_field_existsw('raw_id_fields', field) |
| if not isinstance(f, (models.ForeignKey, models.ManyToManyField)): |
| raise ImproperlyConfigured("`%s.raw_id_fields[%d]`, `%s` must " |
| "be either a ForeignKey or ManyToManyField." |
| % (cls.__name__, idx, field)) |
| |
| # fields |
| if cls.fields: # default value is None |
| _check_istuplew('fields', cls.fields) |
| for field in cls.fields: |
| _check_form_field_existsw('fields', field) |
| if cls.fieldsets: |
| raise ImproperlyConfigured('Both fieldsets and fields are specified in %s.' % cls.__name__) |
| if len(cls.fields) > len(set(cls.fields)): |
| raise ImproperlyConfigured('There are duplicate field(s) in %s.fields' % cls.__name__) |
| |
| # fieldsets |
| if cls.fieldsets: # default value is None |
| _check_istuplew('fieldsets', cls.fieldsets) |
| for idx, fieldset in enumerate(cls.fieldsets): |
| _check_istuplew('fieldsets[%d]' % idx, fieldset) |
| if len(fieldset) != 2: |
| raise ImproperlyConfigured("`%s.fieldsets[%d]` does not " |
| "have exactly two elements." % (cls.__name__, idx)) |
| _check_isdictw('fieldsets[%d][1]' % idx, fieldset[1]) |
| if 'fields' not in fieldset[1]: |
| raise ImproperlyConfigured("`fields` key is required in " |
| "%s.fieldsets[%d][1] field options dict." |
| % (cls.__name__, idx)) |
| 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_form_field_existsw("fieldsets[%d][1]['fields']" % idx, field) |
| |
| # 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'): |
| _check_istuplew('filter_vertical', cls.filter_vertical) |
| for idx, field in enumerate(cls.filter_vertical): |
| f = _check_field_existsw('filter_vertical', field) |
| if not isinstance(f, models.ManyToManyField): |
| raise ImproperlyConfigured("`%s.filter_vertical[%d]` must be " |
| "a ManyToManyField." % (cls.__name__, idx)) |
| |
| # filter_horizontal |
| if hasattr(cls, 'filter_horizontal'): |
| _check_istuplew('filter_horizontal', cls.filter_horizontal) |
| for idx, field in enumerate(cls.filter_horizontal): |
| f = _check_field_existsw('filter_horizontal', field) |
| if not isinstance(f, models.ManyToManyField): |
| raise ImproperlyConfigured("`%s.filter_horizontal[%d]` must be " |
| "a ManyToManyField." % (cls.__name__, idx)) |
| |
| # radio_fields |
| if hasattr(cls, 'radio_fields'): |
| _check_isdictw('radio_fields', cls.radio_fields) |
| for field, val in cls.radio_fields.items(): |
| f = _check_field_existsw('radio_fields', field) |
| if not (isinstance(f, models.ForeignKey) or f.choices): |
| raise ImproperlyConfigured("`%s.radio_fields['%s']` " |
| "is neither an instance of ForeignKey nor does " |
| "have choices set." % (cls.__name__, field)) |
| if not val in (HORIZONTAL, VERTICAL): |
| raise ImproperlyConfigured("`%s.radio_fields['%s']` " |
| "is neither admin.HORIZONTAL nor admin.VERTICAL." |
| % (cls.__name__, field)) |
| |
| # prepopulated_fields |
| if hasattr(cls, 'prepopulated_fields'): |
| _check_isdictw('prepopulated_fields', cls.prepopulated_fields) |
| for field, val in cls.prepopulated_fields.items(): |
| f = _check_field_existsw('prepopulated_fields', field) |
| if isinstance(f, (models.DateTimeField, models.ForeignKey, |
| models.ManyToManyField)): |
| raise ImproperlyConfigured("`%s.prepopulated_fields['%s']` " |
| "is either a DateTimeField, ForeignKey or " |
| "ManyToManyField. This isn't allowed." |
| % (cls.__name__, field)) |
| _check_istuplew("prepopulated_fields['%s']" % field, val) |
| for idx, f in enumerate(val): |
| _check_field_existsw("prepopulated_fields['%s'][%d]" |
| % (f, idx), f) |
| |
| def _check_istuple(cls, label, obj): |
| if not isinstance(obj, (list, tuple)): |
| raise ImproperlyConfigured("`%s.%s` must be a " |
| "list or tuple." % (cls.__name__, label)) |
| |
| def _check_isdict(cls, label, obj): |
| if not isinstance(obj, dict): |
| raise ImproperlyConfigured("`%s.%s` must be a dictionary." |
| % (cls.__name__, label)) |
| |
| def _check_field_exists(cls, model, opts, label, field): |
| try: |
| return opts.get_field(field) |
| except models.FieldDoesNotExist: |
| raise ImproperlyConfigured("`%s.%s` refers to " |
| "field `%s` that is missing from model `%s`." |
| % (cls.__name__, label, field, model.__name__)) |
| |
| def _check_form_field_exists(cls, model, opts, label, field): |
| if hasattr(cls.form, 'base_fields'): |
| try: |
| cls.form.base_fields[field] |
| except KeyError: |
| raise ImproperlyConfigured("`%s.%s` refers to field `%s` that " |
| "is missing from the form." % (cls.__name__, label, field)) |
| else: |
| fields = fields_for_model(model) |
| try: |
| fields[field] |
| except KeyError: |
| raise ImproperlyConfigured("`%s.%s` refers to field `%s` that " |
| "is missing from the form." % (cls.__name__, label, field)) |
| |
| def _check_attr_exists(cls, model, opts, label, field): |
| try: |
| return opts.get_field(field) |
| except models.FieldDoesNotExist: |
| if not hasattr(model, field): |
| raise ImproperlyConfigured("`%s.%s` refers to " |
| "`%s` that is neither a field, method or property " |
| "of model `%s`." |
| % (cls.__name__, label, field, model.__name__)) |
| return getattr(model, field) |