def get_formset(self, request, obj=None): if self.declared_fieldsets: fields = flatten_fieldsets(self.declared_fieldsets) else: fields = None if self.exclude is None: exclude = [] else: exclude = list(self.exclude) exclude.extend(self.get_readonly_fields(request, obj)) exclude = exclude or None defaults = { "ct_field": self.ct_field, "fk_field": self.ct_fk_field, "form": self.form, "formfield_callback": self.formfield_for_dbfield, "formset": self.formset, "extra": self.extra, "can_delete": self.can_delete, "can_order": False, "fields": fields, "max_num": self.max_num, "exclude": exclude } return generic_inlineformset_factory(self.model, **defaults)
def get_readonly_fields(self, request, obj=None): from django.contrib.admin.options import modelform_factory, flatten_fieldsets from django.utils.functional import curry if not hasattr(self, "_readonly_fields"): if self.declared_fieldsets: fields = flatten_fieldsets(self.declared_fieldsets) else: fields = None if self.exclude is None: exclude = [] else: exclude = list(self.exclude) exclude.extend(self.readonly_fields) # if exclude is an empty list we pass None to be consistant with the # default on modelform_factory exclude = exclude or None defaults = { "form": self.form, "fields": fields, "exclude": exclude, "formfield_callback": curry(self.formfield_for_dbfield, request=request), } self._readonly_fields = modelform_factory(self.model, **defaults).base_fields return self._readonly_fields.keys()
def get_formset(self, request, obj=None, **kwargs): if 'fields' in kwargs: fields = kwargs.pop('fields') else: fields = flatten_fieldsets(self.get_fieldsets(request, obj)) exclude = [*(self.exclude or []), *self.get_readonly_fields(request, obj)] if self.exclude is None and hasattr(self.form, '_meta') and self.form._meta.exclude: # Take the custom ModelForm's Meta.exclude into account only if the # GenericInlineModelAdmin doesn't define its own. exclude.extend(self.form._meta.exclude) exclude = exclude or None can_delete = self.can_delete and self.has_delete_permission(request, obj) defaults = { 'ct_field': self.ct_field, 'fk_field': self.ct_fk_field, 'form': self.form, 'formfield_callback': partial(self.formfield_for_dbfield, request=request), 'formset': self.formset, 'extra': self.get_extra(request, obj), 'can_delete': can_delete, 'can_order': False, 'fields': fields, 'min_num': self.get_min_num(request, obj), 'max_num': self.get_max_num(request, obj), 'exclude': exclude, **kwargs, } if defaults['fields'] is None and not modelform_defines_fields(defaults['form']): defaults['fields'] = ALL_FIELDS return generic_inlineformset_factory(self.model, **defaults)
def get_formset(self, request, obj=None, **kwargs): if self.declared_fieldsets: fields = flatten_fieldsets(self.declared_fieldsets) else: fields = None if self.exclude is None: exclude = [] else: exclude = list(self.exclude) exclude.extend(self.get_readonly_fields(request, obj)) if self.exclude is None and hasattr(self.form, '_meta') and self.form._meta.exclude: # Take the custom ModelForm's Meta.exclude into account only if the # GenericInlineModelAdmin doesn't define its own. exclude.extend(self.form._meta.exclude) exclude = exclude or None can_delete = self.can_delete and self.has_delete_permission(request, obj) defaults = { "ct_field": self.ct_field, "fk_field": self.ct_fk_field, "form": self.form, "formfield_callback": partial(self.formfield_for_dbfield, request=request), "formset": self.formset, "extra": self.extra, "can_delete": can_delete, "can_order": False, "fields": fields, "max_num": self.max_num, "exclude": exclude } defaults.update(kwargs) return generic_inlineformset_factory(self.model, **defaults)
def _group_fieldsets(self, fieldsets): # Fieldsets are not grouped by default. The function is activated by # setting TranslationAdmin.group_fieldsets to True. If the admin class # already defines a fieldset, we leave it alone and assume the author # has done whatever grouping for translated fields they desire. if self.group_fieldsets is True: flattened_fieldsets = flatten_fieldsets(fieldsets) # Create a fieldset to group each translated field's localized fields fields = sorted((f for f in self.opts.get_fields() if f.concrete)) untranslated_fields = [ f.name for f in fields if ( # Exclude the primary key field f is not self.opts.auto_field # Exclude non-editable fields and f.editable # Exclude the translation fields and not hasattr(f, 'translated_field') # Honour field arguments. We rely on the fact that the # passed fieldsets argument is already fully filtered # and takes options like exclude into account. and f.name in flattened_fieldsets) ] # TODO: Allow setting a label fieldsets = ([( '', { 'fields': untranslated_fields }, )] if untranslated_fields else []) temp_fieldsets = {} for orig_field, trans_fields in self.trans_opts.fields.items(): trans_fieldnames = [ f.name for f in sorted(trans_fields, key=lambda x: x.name) ] if any(f in trans_fieldnames for f in flattened_fieldsets): # Extract the original field's verbose_name for use as this # fieldset's label - using gettext_lazy in your model # declaration can make that translatable. label = self.model._meta.get_field( orig_field).verbose_name.capitalize() temp_fieldsets[orig_field] = ( label, { 'fields': trans_fieldnames, 'classes': ('mt-fieldset', ) }, ) fields_order = unique(f.translated_field.name for f in self.opts.fields if hasattr(f, 'translated_field') and f.name in flattened_fieldsets) for field_name in fields_order: fieldsets.append(temp_fieldsets.pop(field_name)) assert not temp_fieldsets # cleaned return fieldsets
def get_readonly_fields(self, request, obj=None): if obj is not None: if not request.user.is_superuser: if request.user.username == obj.username or not obj.is_admin: return self.readonly_fields + ('is_superuser',) else: return self.readonly_fields + tuple(flatten_fieldsets(self.get_fieldsets(request, obj))) return self.readonly_fields
def get_readonly_fields(self, request, obj=None): if obj is not None: if not request.user.is_superuser: if request.user.username == obj.username or not obj.is_admin: return self.readonly_fields + ('is_superuser', ) else: return self.readonly_fields + tuple( flatten_fieldsets(self.get_fieldsets(request, obj))) return self.readonly_fields
def get_readonly_fields(self, request, obj=None): user = request.user if user.is_staff and not user.is_superuser: if self.fieldsets: return flatten_fieldsets(self.fieldsets or []) else: return list( set([field.name for field in self.opts.local_fields] + [field.name for field in self.opts.local_many_to_many])) return super().get_readonly_fields(request, obj)
def get_formset(self, request, obj=None, **kwargs): if 'fields' in kwargs: fields = kwargs.pop('fields') else: fields = flatten_fieldsets(self.get_fieldsets(request, obj)) exclude = [ *(self.exclude or []), *self.get_readonly_fields(request, obj) ] if self.exclude is None and hasattr( self.form, '_meta') and self.form._meta.exclude: exclude.extend(self.form._meta.exclude) exclude = exclude or None can_delete = self.can_delete and self.has_delete_permission( request, obj) queryset = self.model.objects.none() if obj: queryset = self.get_form_queryset(obj) defaults = { 'form': self.form, 'formfield_callback': partial(self.formfield_for_dbfield, request=request), 'formset': self.formset, 'extra': self.get_extra(request, obj), 'can_delete': can_delete, 'can_order': False, 'fields': fields, 'min_num': self.get_min_num(request, obj), 'max_num': self.get_max_num(request, obj), 'exclude': exclude, 'queryset': queryset, **kwargs, } if defaults['fields'] is None and not modelform_defines_fields( defaults['form']): defaults['fields'] = ALL_FIELDS return nonrelated_inlineformset_factory( self.model, save_new_instance=self.save_new_instance, **defaults)
def get_formset(self, request, obj=None, **kwargs): if 'fields' in kwargs: fields = kwargs.pop('fields') else: fields = flatten_fieldsets(self.get_fieldsets(request, obj)) if self.exclude is None: exclude = [] else: exclude = list(self.exclude) exclude.extend(self.get_readonly_fields(request, obj)) if (self.exclude is None and hasattr(self.form, '_meta') and self.form._meta.exclude): # Take the custom ModelForm's Meta.exclude into account only if the # GenericInlineModelAdmin doesn't define its own. exclude.extend(self.form._meta.exclude) exclude = exclude or None can_delete = (self.can_delete and self.has_delete_permission(request, obj)) defaults = { "model_field": self.model_field, "parent_model_field": self.parent_model_field, "form": self.form, "formfield_callback": partial(self.formfield_for_dbfield, request=request), "formset": self.formset, "extra": self.get_extra(request, obj), "can_delete": can_delete, "can_order": False, "fields": fields, "min_num": self.get_min_num(request, obj), "max_num": self.get_max_num(request, obj), "exclude": exclude } defaults.update(kwargs) if (defaults['fields'] is None and not modelform_defines_fields(defaults['form'])): defaults['fields'] = ALL_FIELDS return arbitrary_inlineformset_factory(self.model, self.parent_model, **defaults)
def get_formset(self, request, obj=None, **kwargs): if "fields" in kwargs: fields = kwargs.pop("fields") else: fields = flatten_fieldsets(self.get_fieldsets(request, obj)) exclude = [ *(self.exclude or []), *self.get_readonly_fields(request, obj) ] if (self.exclude is None and hasattr(self.form, "_meta") and self.form._meta.exclude): # Take the custom ModelForm's Meta.exclude into account only if the # GenericInlineModelAdmin doesn't define its own. exclude.extend(self.form._meta.exclude) exclude = exclude or None can_delete = self.can_delete and self.has_delete_permission( request, obj) defaults = { "ct_field": self.ct_field, "fk_field": self.ct_fk_field, "form": self.form, "formfield_callback": partial(self.formfield_for_dbfield, request=request), "formset": self.formset, "extra": self.get_extra(request, obj), "can_delete": can_delete, "can_order": False, "fields": fields, "min_num": self.get_min_num(request, obj), "max_num": self.get_max_num(request, obj), "exclude": exclude, **kwargs, } if defaults["fields"] is None and not modelform_defines_fields( defaults["form"]): defaults["fields"] = ALL_FIELDS return generic_inlineformset_factory(self.model, **defaults)
def _group_fieldsets(self, fieldsets): # Fieldsets are not grouped by default. The function is activated by # setting TranslationAdmin.group_fieldsets to True. If the admin class # already defines a fieldset, we leave it alone and assume the author # has done whatever grouping for translated fields they desire. if not self.declared_fieldsets and self.group_fieldsets is True: flattened_fieldsets = flatten_fieldsets(fieldsets) # Create a fieldset to group each translated field's localized fields untranslated_fields = [ f.name for f in self.opts.fields if ( # Exclude the primary key field f is not self.opts.auto_field # Exclude non-editable fields and f.editable # Exclude the translation fields and not hasattr(f, "translated_field") # Honour field arguments. We rely on the fact that the # passed fieldsets argument is already fully filtered # and takes options like exclude into account. and f.name in flattened_fieldsets ) ] # TODO: Allow setting a label fieldsets = [("", {"fields": untranslated_fields})] if untranslated_fields else [] temp_fieldsets = {} for orig_field, trans_fields in self.trans_opts.fields.items(): trans_fieldnames = [f.name for f in sorted(trans_fields, key=lambda x: x.name)] if any(f in trans_fieldnames for f in flattened_fieldsets): # Extract the original field's verbose_name for use as this # fieldset's label - using ugettext_lazy in your model # declaration can make that translatable. label = self.model._meta.get_field(orig_field).verbose_name temp_fieldsets[orig_field] = (label, {"fields": trans_fieldnames, "classes": ("mt-fieldset",)}) fields_order = unique( f.translated_field.name for f in self.opts.fields if hasattr(f, "translated_field") and f.name in flattened_fieldsets ) for field_name in fields_order: fieldsets.append(temp_fieldsets.pop(field_name)) assert not temp_fieldsets # cleaned return fieldsets
def get_formset(self, request, obj=None): if self.declared_fieldsets: fields = flatten_fieldsets(self.declared_fieldsets) else: fields = None defaults = { "ct_field": self.ct_field, "fk_field": self.ct_fk_field, "form": self.form, "formfield_callback": self.formfield_for_dbfield, "formset": self.formset, "extra": self.extra, "can_delete": True, "can_order": False, "fields": fields, } return generic_inlineformset_factory(self.model, **defaults)
def get_readonly_fields(self, request, obj=None): read_only, created = Group.objects.get_or_create( name=self.read_only_group_name ) if obj and read_only in request.user.groups.all(): if self.declared_fieldsets: fieldsets = self.declared_fieldsets self.remove_from_fieldsets(fieldsets, self.remove_fields_if_read_only) fields = flatten_fieldsets(fieldsets) else: fields = list(set( [field.name for field in self.opts.local_fields] + [field.name for field in self.opts.local_many_to_many] )) return fields return self.readonly_fields
def validate_fieldsets(self, cls, model): " Validate that fieldsets is properly formatted and doesn't contain duplicates. " from django.contrib.admin.options import flatten_fieldsets if cls.fieldsets: # default value is None check_isseq(cls, 'fieldsets', cls.fieldsets) for idx, fieldset in enumerate(cls.fieldsets): check_isseq(cls, 'fieldsets[%d]' % idx, fieldset) if len(fieldset) != 2: raise ImproperlyConfigured("'%s.fieldsets[%d]' does not " "have exactly two elements." % (cls.__name__, idx)) check_isdict(cls, '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)) self.check_field_spec(cls, model, fieldset[1]['fields'], "fieldsets[%d][1]['fields']" % 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__)
def get_readonly_fields(self, request, obj=None): read_only, created = Group.objects.get_or_create( name=self.read_only_group_name) if obj and read_only in request.user.groups.all(): if self.declared_fieldsets: fieldsets = self.declared_fieldsets self.remove_from_fieldsets(fieldsets, self.remove_fields_if_read_only) fields = flatten_fieldsets(fieldsets) else: fields = list( set([field.name for field in self.opts.local_fields] + [field.name for field in self.opts.local_many_to_many])) return fields return self.readonly_fields
def validate_base(cls, model): opts = model._meta # fields if cls.fields: # default value is None check_isseq(cls, 'fields', cls.fields) validate_fields_spec(cls, model, opts, cls.fields, 'fields') 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_isseq(cls, 'fieldsets', cls.fieldsets) for idx, fieldset in enumerate(cls.fieldsets): check_isseq(cls, 'fieldsets[%d]' % idx, fieldset) if len(fieldset) != 2: raise ImproperlyConfigured("'%s.fieldsets[%d]' does not " "have exactly two elements." % (cls.__name__, idx)) check_isdict(cls, '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)) validate_fields_spec(cls, model, opts, fieldset[1]['fields'], "fieldsets[%d][1]['fields']" % 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__) # 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__)
def _group_fieldsets(self, fieldsets): # Fieldsets are not grouped by default. The function is activated by # setting TranslationAdmin.group_fieldsets to True. If the admin class # already defines a fieldset, we leave it alone and assume the author # has done whatever grouping for translated fields they desire. if not self.declared_fieldsets and self.group_fieldsets is True: flattened_fieldsets = flatten_fieldsets(fieldsets) # Create a fieldset to group each translated field's localized fields untranslated_fields = [ f.name for f in self.opts.fields if ( # Exclude the primary key field f is not self.opts.auto_field # Exclude non-editable fields and f.editable # Exclude the translation fields # TODO: I already miss localized_fieldnames_rev here ;) and f not in [ k for l in [list(j) for i in self.trans_opts.fields.items() for j in i[1:]] for k in l] # Honour field arguments. We rely on the fact that the # passed fieldsets argument is already fully filtered # and takes options like exclude into account. and f.name in flattened_fieldsets ) ] # TODO: Allow setting a label fieldsets = [('', {'fields': untranslated_fields},)] if untranslated_fields else [] for orig_field, trans_fields in self.trans_opts.fields.items(): trans_fieldnames = [f.name for f in sorted(trans_fields, key=lambda x: x.name)] if any(f in trans_fieldnames for f in flattened_fieldsets): # Extract the original field's verbose_name for use as this # fieldset's label - using ugettext_lazy in your model # declaration can make that translatable. label = self.model._meta.get_field(orig_field).verbose_name fieldsets.append((label, { 'fields': trans_fieldnames, 'classes': ('mt-fieldset',) })) return fieldsets
def get_formset(self, request, obj=None): setattr(self.form, '_magic_user', request.user) # magic variable passing to form setattr(self, '_magic_user', request.user) # magic variable if self.declared_fieldsets: fields = flatten_fieldsets(self.declared_fieldsets) else: fields = None defaults = { "ct_field": self.ct_field, "fk_field": self.ct_fk_field, "form": self.form, "formfield_callback": self.formfield_for_dbfield, "formset": self.formset, "extra": self.extra, "max_num": self.max_num, "can_delete": True, "can_order": False, "fields": fields, } return generic_inlineformset_factory(self.model, **defaults)
def get_form(self, request, obj=None, **kwargs): """ Returns a Form class for use in the admin add view. This is used by add_view and change_view. """ if 'fields' in kwargs: fields = kwargs.pop('fields') else: fields = flatten_fieldsets(self.get_fieldsets(request, obj)) if self.exclude is None: exclude = [] else: exclude = list(self.exclude) exclude.extend(self.get_readonly_fields(request, obj)) if self.exclude is None and hasattr(self.form, '_meta') and self.form._meta.exclude: # Take the custom ModelForm's Meta.exclude into account only if the # ModelAdmin doesn't define its own. exclude.extend(self.form._meta.exclude) # if exclude is an empty list we pass None to be consistent with the # default on modelform_factory exclude = exclude or None defaults = { "form": self.form, "fields": fields, "exclude": exclude, "formfield_callback": partial(self.formfield_for_dbfield, request=request), } defaults.update(kwargs) if defaults['fields'] is None and not modelform_defines_fields(defaults['form']): defaults['fields'] = None try: return documentform_factory(self.model, **defaults) except FieldError as e: raise FieldError('%s. Check fields/fieldsets/exclude attributes of class %s.' % (e, self.__class__.__name__))
def validate_base(cls, model): opts = model._meta # raw_id_fields if hasattr(cls, 'raw_id_fields'): check_isseq(cls, 'raw_id_fields', cls.raw_id_fields) for idx, field in enumerate(cls.raw_id_fields): f = get_field(cls, model, opts, '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_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)): raise ImproperlyConfigured( 'There are duplicate field(s) in %s.fields' % cls.__name__) # fieldsets if cls.fieldsets: # default value is None check_isseq(cls, 'fieldsets', cls.fieldsets) for idx, fieldset in enumerate(cls.fieldsets): check_isseq(cls, 'fieldsets[%d]' % idx, fieldset) if len(fieldset) != 2: raise ImproperlyConfigured("'%s.fieldsets[%d]' does not " "have exactly two elements." % (cls.__name__, idx)) check_isdict(cls, '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)) 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__) # 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): raise ImproperlyConfigured("%s.form does not inherit from " "BaseModelForm." % cls.__name__) # filter_vertical if hasattr(cls, 'filter_vertical'): check_isseq(cls, 'filter_vertical', cls.filter_vertical) for idx, field in enumerate(cls.filter_vertical): f = get_field(cls, model, opts, '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_isseq(cls, 'filter_horizontal', cls.filter_horizontal) for idx, field in enumerate(cls.filter_horizontal): f = get_field(cls, model, opts, '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_isdict(cls, 'radio_fields', cls.radio_fields) for field, val in cls.radio_fields.items(): f = get_field(cls, model, opts, '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_isdict(cls, 'prepopulated_fields', cls.prepopulated_fields) for field, val in cls.prepopulated_fields.items(): f = get_field(cls, model, opts, '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_isseq(cls, "prepopulated_fields['%s']" % field, val) for idx, f in enumerate(val): get_field(cls, model, opts, "prepopulated_fields['%s'][%d]" % (field, idx), f)
def validate_base(cls, model): opts = model._meta # raw_id_fields if hasattr(cls, 'raw_id_fields'): check_isseq(cls, 'raw_id_fields', cls.raw_id_fields) for idx, field in enumerate(cls.raw_id_fields): f = get_field(cls, model, opts, '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_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)): raise ImproperlyConfigured('There are duplicate field(s) in %s.fields' % cls.__name__) # fieldsets if cls.fieldsets: # default value is None check_isseq(cls, 'fieldsets', cls.fieldsets) for idx, fieldset in enumerate(cls.fieldsets): check_isseq(cls, 'fieldsets[%d]' % idx, fieldset) if len(fieldset) != 2: raise ImproperlyConfigured("'%s.fieldsets[%d]' does not " "have exactly two elements." % (cls.__name__, idx)) check_isdict(cls, '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)) 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__) # 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): raise ImproperlyConfigured("%s.form does not inherit from " "BaseModelForm." % cls.__name__) # filter_vertical if hasattr(cls, 'filter_vertical'): check_isseq(cls, 'filter_vertical', cls.filter_vertical) for idx, field in enumerate(cls.filter_vertical): f = get_field(cls, model, opts, '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_isseq(cls, 'filter_horizontal', cls.filter_horizontal) for idx, field in enumerate(cls.filter_horizontal): f = get_field(cls, model, opts, '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_isdict(cls, 'radio_fields', cls.radio_fields) for field, val in cls.radio_fields.items(): f = get_field(cls, model, opts, '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_isdict(cls, 'prepopulated_fields', cls.prepopulated_fields) for field, val in cls.prepopulated_fields.items(): f = get_field(cls, model, opts, '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_isseq(cls, "prepopulated_fields['%s']" % field, val) for idx, f in enumerate(val): get_field(cls, model, opts, "prepopulated_fields['%s'][%d]" % (field, idx), f)
def validate_base(cls, model): opts = model._meta # raw_id_fields if hasattr(cls, 'raw_id_fields'): check_isseq(cls, 'raw_id_fields', cls.raw_id_fields) for idx, field in enumerate(cls.raw_id_fields): f = get_field(cls, model, opts, '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_isseq(cls, 'fields', cls.fields) for field in cls.fields: check_formfield(cls, model, opts, '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_isseq(cls, 'fieldsets', cls.fieldsets) for idx, fieldset in enumerate(cls.fieldsets): check_isseq(cls, 'fieldsets[%d]' % idx, fieldset) if len(fieldset) != 2: raise ImproperlyConfigured("'%s.fieldsets[%d]' does not " "have exactly two elements." % (cls.__name__, idx)) check_isdict(cls, '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_formfield(cls, model, opts, "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_isseq(cls, 'filter_vertical', cls.filter_vertical) for idx, field in enumerate(cls.filter_vertical): f = get_field(cls, model, opts, '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_isseq(cls, 'filter_horizontal', cls.filter_horizontal) for idx, field in enumerate(cls.filter_horizontal): f = get_field(cls, model, opts, '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_isdict(cls, 'radio_fields', cls.radio_fields) for field, val in cls.radio_fields.items(): f = get_field(cls, model, opts, '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_isdict(cls, 'prepopulated_fields', cls.prepopulated_fields) for field, val in cls.prepopulated_fields.items(): f = get_field(cls, model, opts, '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_isseq(cls, "prepopulated_fields['%s']" % field, val) for idx, f in enumerate(val): get_field(cls, model, opts, "prepopulated_fields['%s'][%d]" % (field, idx), f)
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__) # 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)) for field in flatten_fieldsets(cls.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 get_readonly_fields(self, request, obj=None): if obj: return 'model', else: return set(flatten_fieldsets(self.get_fieldsets(request, obj))) - set(('model', 'admin_site'))
def get_formset(self, request, obj=None, **kwargs): """Returns a BaseInlineFormSet class for use in admin add/change views.""" if 'fields' in kwargs: fields = kwargs.pop('fields') else: fields = flatten_fieldsets(self.get_fieldsets(request, obj)) if self.exclude is None: exclude = [] else: exclude = list(self.exclude) exclude.extend(self.get_readonly_fields(request, obj)) if self.exclude is None and hasattr(self.form, '_meta') and self.form._meta.exclude: # Take the custom ModelForm's Meta.exclude into account only if the # InlineModelAdmin doesn't define its own. exclude.extend(self.form._meta.exclude) # if exclude is an empty list we use None, since that's the actual # default exclude = exclude or None can_delete = self.can_delete and self.has_delete_permission(request, obj) defaults = { "form": self.form, "formset": self.formset, "embedded_name": self.parent_field_name, "fields": fields, "exclude": exclude, "formfield_callback": partial(self.formfield_for_dbfield, request=request), "extra": self.get_extra(request, obj, **kwargs), "max_num": self.get_max_num(request, obj, **kwargs), "can_delete": can_delete, } defaults.update(kwargs) base_model_form = defaults['form'] class DeleteProtectedModelForm(base_model_form): def hand_clean_DELETE(self): """ We don't validate the 'DELETE' field itself because on templates it's not rendered using the field information, but just using a generic "deletion_field" of the InlineModelAdmin. """ if self.cleaned_data.get(DELETION_FIELD_NAME, False): collector = NestedObjects() collector.collect([self.instance]) if collector.protected: objs = [] for p in collector.protected: objs.append( # Translators: Model verbose name and instance representation, suitable to be an item in a list _('%(class_name)s %(instance)s') % { 'class_name': p._meta.verbose_name, 'instance': p} ) params = {'class_name': self._meta.model._meta.verbose_name, 'instance': self.instance, 'related_objects': get_text_list(objs, _('and'))} msg = _("Deleting %(class_name)s %(instance)s would require " "deleting the following protected related objects: " "%(related_objects)s") raise ValidationError(msg, code='deleting_protected', params=params) def is_valid(self): result = super(DeleteProtectedModelForm, self).is_valid() self.hand_clean_DELETE() return result defaults['form'] = DeleteProtectedModelForm if defaults['fields'] is None and not modelform_defines_fields(defaults['form']): defaults['fields'] = None return embeddedformset_factory(self.model, self.parent_model, **defaults)
def get_formset(self, request, obj=None, **kwargs): # noqa: C901 """ Copied from the Django InlineModelAdmin but instead of the inlineformset_factory we use the non_related_inlineformset_factory. """ if 'fields' in kwargs: fields = kwargs.pop('fields') else: fields = flatten_fieldsets(self.get_fieldsets(request, obj)) exclude = [*(self.exclude or []), *self.get_readonly_fields(request, obj)] if self.exclude is None and hasattr(self.form, '_meta') and self.form._meta.exclude: exclude.extend(self.form._meta.exclude) exclude = exclude or None can_delete = self.can_delete and self.has_delete_permission(request, obj) queryset = self.model.objects.none() if obj: queryset = self.get_form_queryset(obj) defaults = { 'form': self.form, 'formfield_callback': partial(self.formfield_for_dbfield, request=request), 'formset': self.formset, 'extra': self.get_extra(request, obj), 'can_delete': can_delete, 'can_order': False, 'fields': fields, 'min_num': self.get_min_num(request, obj), 'max_num': self.get_max_num(request, obj), 'exclude': exclude, 'queryset': queryset, **kwargs, } base_model_form = defaults['form'] can_change = self.has_change_permission(request, obj) if request else True can_add = self.has_add_permission(request, obj) if request else True class DeleteProtectedModelForm(base_model_form): def hand_clean_DELETE(self): """ We don't validate the 'DELETE' field itself because on templates it's not rendered using the field information, but just using a generic "deletion_field" of the InlineModelAdmin. """ if self.cleaned_data.get(DELETION_FIELD_NAME, False): using = router.db_for_write(self._meta.model) collector = NestedObjects(using=using) if self.instance._state.adding: return collector.collect([self.instance]) if collector.protected: objs = [] for p in collector.protected: objs.append( # Translators: Model verbose name and instance representation, # suitable to be an item in a list. _('%(class_name)s %(instance)s') % { 'class_name': p._meta.verbose_name, 'instance': p} ) params = { 'class_name': self._meta.model._meta.verbose_name, 'instance': self.instance, 'related_objects': get_text_list(objs, _('and')), } msg = _("Deleting %(class_name)s %(instance)s would require " "deleting the following protected related objects: " "%(related_objects)s") raise ValidationError(msg, code='deleting_protected', params=params) def is_valid(self): result = super().is_valid() self.hand_clean_DELETE() return result def has_changed(self): # Protect against unauthorized edits. if not can_change and not self.instance._state.adding: return False if not can_add and self.instance._state.adding: return False return super().has_changed() defaults['form'] = DeleteProtectedModelForm if defaults['fields'] is None and not modelform_defines_fields(defaults['form']): defaults['fields'] = ALL_FIELDS return non_related_inlineformset_factory(self.model, save_new_instance=self.save_new_instance, **defaults)
def validate_base(cls, model): opts = model._meta # raw_id_fields if hasattr(cls, 'raw_id_fields'): check_isseq(cls, 'raw_id_fields', cls.raw_id_fields) for idx, field in enumerate(cls.raw_id_fields): f = get_field(cls, model, opts, '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_isseq(cls, 'fields', cls.fields) validate_fields_spec(cls, model, opts, cls.fields, 'fields') 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_isseq(cls, 'fieldsets', cls.fieldsets) for idx, fieldset in enumerate(cls.fieldsets): check_isseq(cls, 'fieldsets[%d]' % idx, fieldset) if len(fieldset) != 2: raise ImproperlyConfigured("'%s.fieldsets[%d]' does not " "have exactly two elements." % (cls.__name__, idx)) check_isdict(cls, '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)) validate_fields_spec(cls, model, opts, fieldset[1]['fields'], "fieldsets[%d][1]['fields']" % 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__) # 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): raise ImproperlyConfigured("%s.form does not inherit from " "BaseModelForm." % cls.__name__) # filter_vertical if hasattr(cls, 'filter_vertical'): check_isseq(cls, 'filter_vertical', cls.filter_vertical) for idx, field in enumerate(cls.filter_vertical): f = get_field(cls, model, opts, '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_isseq(cls, 'filter_horizontal', cls.filter_horizontal) for idx, field in enumerate(cls.filter_horizontal): f = get_field(cls, model, opts, '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_isdict(cls, 'radio_fields', cls.radio_fields) for field, val in cls.radio_fields.items(): f = get_field(cls, model, opts, '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_isdict(cls, 'prepopulated_fields', cls.prepopulated_fields) for field, val in cls.prepopulated_fields.items(): f = get_field(cls, model, opts, '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_isseq(cls, "prepopulated_fields['%s']" % field, val) for idx, f in enumerate(val): get_field(cls, model, opts, "prepopulated_fields['%s'][%d]" % (field, idx), f)
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__) # 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)) for field in flatten_fieldsets(cls.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)