Fixed #10208: `ModelAdmin` now respects the `exclude` and `field` atributes of custom `ModelForm`s. Thanks, Alex Gaynor.

git-svn-id: http://code.djangoproject.com/svn/django/trunk@10619 bcc190cf-cafb-0310-a4f2-bffc1f526a37
This commit is contained in:
Jacob Kaplan-Moss 2009-04-22 15:48:51 +00:00
parent 71233bcdf3
commit 6c15b5db60
3 changed files with 74 additions and 19 deletions

View File

@ -336,10 +336,12 @@ class ModelAdmin(BaseModelAdmin):
exclude = [] exclude = []
else: else:
exclude = list(self.exclude) exclude = list(self.exclude)
# if exclude is an empty list we pass None to be consistant with the
# default on modelform_factory
defaults = { defaults = {
"form": self.form, "form": self.form,
"fields": fields, "fields": fields,
"exclude": exclude + kwargs.get("exclude", []), "exclude": (exclude + kwargs.get("exclude", [])) or None,
"formfield_callback": curry(self.formfield_for_dbfield, request=request), "formfield_callback": curry(self.formfield_for_dbfield, request=request),
} }
defaults.update(kwargs) defaults.update(kwargs)
@ -1138,12 +1140,14 @@ class InlineModelAdmin(BaseModelAdmin):
exclude = [] exclude = []
else: else:
exclude = list(self.exclude) exclude = list(self.exclude)
# if exclude is an empty list we use None, since that's the actual
# default
defaults = { defaults = {
"form": self.form, "form": self.form,
"formset": self.formset, "formset": self.formset,
"fk_name": self.fk_name, "fk_name": self.fk_name,
"fields": fields, "fields": fields,
"exclude": exclude + kwargs.get("exclude", []), "exclude": (exclude + kwargs.get("exclude", [])) or None,
"formfield_callback": curry(self.formfield_for_dbfield, request=request), "formfield_callback": curry(self.formfield_for_dbfield, request=request),
"extra": self.extra, "extra": self.extra,
"max_num": self.max_num, "max_num": self.max_num,

View File

@ -344,16 +344,34 @@ class ModelForm(BaseModelForm):
def modelform_factory(model, form=ModelForm, fields=None, exclude=None, def modelform_factory(model, form=ModelForm, fields=None, exclude=None,
formfield_callback=lambda f: f.formfield()): formfield_callback=lambda f: f.formfield()):
# HACK: we should be able to construct a ModelForm without creating # Create the inner Meta class. FIXME: ideally, we should be able to
# and passing in a temporary inner class # construct a ModelForm without creating and passing in a temporary
class Meta: # inner class.
pass
setattr(Meta, 'model', model) # Build up a list of attributes that the Meta object will have.
setattr(Meta, 'fields', fields) attrs = {'model': model}
setattr(Meta, 'exclude', exclude) if fields is not None:
attrs['fields'] = fields
if exclude is not None:
attrs['exclude'] = exclude
# If parent form class already has an inner Meta, the Meta we're
# creating needs to inherit from the parent's inner meta.
parent = (object,)
if hasattr(form, 'Meta'):
parent = (form.Meta, object)
Meta = type('Meta', parent, attrs)
# Give this new form class a reasonable name.
class_name = model.__name__ + 'Form' class_name = model.__name__ + 'Form'
return ModelFormMetaclass(class_name, (form,), {'Meta': Meta,
'formfield_callback': formfield_callback}) # Class attributes for the new form class.
form_class_attrs = {
'Meta': Meta,
'formfield_callback': formfield_callback
}
return ModelFormMetaclass(class_name, (form,), form_class_attrs)
# ModelFormSets ############################################################## # ModelFormSets ##############################################################
@ -617,13 +635,6 @@ def inlineformset_factory(parent_model, model, form=ModelForm,
# enforce a max_num=1 when the foreign key to the parent model is unique. # enforce a max_num=1 when the foreign key to the parent model is unique.
if fk.unique: if fk.unique:
max_num = 1 max_num = 1
if fields is not None:
fields = list(fields)
fields.append(fk.name)
else:
# get all the fields for this model that will be generated.
fields = fields_for_model(model, fields, exclude, formfield_callback).keys()
fields.append(fk.name)
kwargs = { kwargs = {
'form': form, 'form': form,
'formfield_callback': formfield_callback, 'formfield_callback': formfield_callback,

View File

@ -37,7 +37,7 @@ class ValidationTestInlineModel(models.Model):
__test__ = {'API_TESTS': """ __test__ = {'API_TESTS': """
>>> from django.contrib.admin.options import ModelAdmin, HORIZONTAL, VERTICAL >>> from django.contrib.admin.options import ModelAdmin, TabularInline, HORIZONTAL, VERTICAL
>>> from django.contrib.admin.sites import AdminSite >>> from django.contrib.admin.sites import AdminSite
None of the following tests really depend on the content of the request, so None of the following tests really depend on the content of the request, so
@ -262,6 +262,46 @@ blank=True for the model field. Finally, the widget should have the
>>> list(cmafa.base_fields['transport'].widget.choices) >>> list(cmafa.base_fields['transport'].widget.choices)
[('', u'None'), (1, 'Plane'), (2, 'Train'), (3, 'Bus')] [('', u'None'), (1, 'Plane'), (2, 'Train'), (3, 'Bus')]
>>> class AdminConcertForm(forms.ModelForm):
... class Meta:
... model = Concert
... exclude = ('transport',)
>>> class ConcertAdmin(ModelAdmin):
... form = AdminConcertForm
>>> ma = ConcertAdmin(Concert, site)
>>> ma.get_form(request).base_fields.keys()
['main_band', 'opening_band', 'day']
>>> class AdminConcertForm(forms.ModelForm):
... extra = forms.CharField()
... class Meta:
... model = Concert
... fields = ['extra', 'transport']
>>> class ConcertAdmin(ModelAdmin):
... form = AdminConcertForm
>>> ma = ConcertAdmin(Concert, site)
>>> ma.get_form(request).base_fields.keys()
['extra', 'transport']
>>> class ConcertInline(TabularInline):
... form = AdminConcertForm
... model = Concert
... fk_name = 'main_band'
>>> class BandAdmin(ModelAdmin):
... inlines = [
... ConcertInline
... ]
>>> ma = BandAdmin(Band, site)
>>> list(ma.get_formsets(request))[0]().forms[0].fields.keys()
['extra', 'transport', 'id', 'DELETE', 'main_band']
>>> band.delete() >>> band.delete()
# ModelAdmin Option Validation ################################################ # ModelAdmin Option Validation ################################################