Ticket #10208: custom-modeladmin-form.diff

File custom-modeladmin-form.diff, 10.3 KB (added by Alex Gaynor, 15 years ago)
  • django/contrib/admin/options.py

    diff --git a/django/contrib/admin/options.py b/django/contrib/admin/options.py
    index c87d903..82e0b70 100644
    a b class BaseModelAdmin(object):  
    108108
    109109        # If we've got overrides for the formfield defined, use 'em. **kwargs
    110110        # passed to formfield_for_dbfield override the defaults.
    111         for klass in db_field.__class__.mro(): 
    112             if klass in self.formfield_overrides: 
    113                 kwargs = dict(self.formfield_overrides[klass], **kwargs) 
    114                 return db_field.formfield(**kwargs) 
     111        for klass in db_field.__class__.mro():
     112            if klass in self.formfield_overrides:
     113                kwargs = dict(self.formfield_overrides[klass], **kwargs)
     114                return db_field.formfield(**kwargs)
    115115
    116116        # For any other type of field, just call its formfield() method.
    117117        return db_field.formfield(**kwargs)
    class ModelAdmin(BaseModelAdmin):  
    336336            exclude = []
    337337        else:
    338338            exclude = list(self.exclude)
     339        # if exclude is an empty list we pass None to be consistant with the
     340        # default on modelform_factory
    339341        defaults = {
    340342            "form": self.form,
    341343            "fields": fields,
    342             "exclude": exclude + kwargs.get("exclude", []),
     344            "exclude": (exclude + kwargs.get("exclude", [])) or None,
    343345            "formfield_callback": curry(self.formfield_for_dbfield, request=request),
    344346        }
    345347        defaults.update(kwargs)
    class ModelAdmin(BaseModelAdmin):  
    437439        # want *any* actions enabled on this page.
    438440        if self.actions is None:
    439441            return []
    440            
     442
    441443        actions = []
    442        
     444
    443445        # Gather actions from the admin site first
    444446        for (name, func) in self.admin_site.actions:
    445447            description = getattr(func, 'short_description', name.replace('_', ' '))
    446448            actions.append((func, name, description))
    447        
    448         # Then gather them from the model admin and all parent classes, 
     449
     450        # Then gather them from the model admin and all parent classes,
    449451        # starting with self and working back up.
    450452        for klass in self.__class__.mro()[::-1]:
    451453            class_actions = getattr(klass, 'actions', [])
    452454            # Avoid trying to iterate over None
    453455            if not class_actions:
    454                 continue 
     456                continue
    455457            actions.extend([self.get_action(action) for action in class_actions])
    456        
     458
    457459        # get_action might have returned None, so filter any of those out.
    458460        actions = filter(None, actions)
    459        
     461
    460462        # Convert the actions into a SortedDict keyed by name
    461463        # and sorted by description.
    462464        actions.sort(lambda a,b: cmp(a[2].lower(), b[2].lower()))
    class ModelAdmin(BaseModelAdmin):  
    464466            (name, (func, name, desc))
    465467            for func, name, desc in actions
    466468        ])
    467        
     469
    468470        return actions
    469471
    470472    def get_action_choices(self, request, default_choices=BLANK_CHOICE_DASH):
    class ModelAdmin(BaseModelAdmin):  
    488490        if callable(action):
    489491            func = action
    490492            action = action.__name__
    491            
     493
    492494        # Next, look for a method. Grab it off self.__class__ to get an unbound
    493495        # method instead of a bound one; this ensures that the calling
    494496        # conventions are the same for functions and methods.
    495497        elif hasattr(self.__class__, action):
    496498            func = getattr(self.__class__, action)
    497        
     499
    498500        # Finally, look for a named method on the admin site
    499501        else:
    500502            try:
    501503                func = self.admin_site.get_action(action)
    502504            except KeyError:
    503505                return None
    504            
     506
    505507        if hasattr(func, 'short_description'):
    506508            description = func.short_description
    507509        else:
    class ModelAdmin(BaseModelAdmin):  
    664666        data = request.POST.copy()
    665667        data.pop(helpers.ACTION_CHECKBOX_NAME, None)
    666668        data.pop("index", None)
    667        
     669
    668670        # Use the action whose button was pushed
    669671        try:
    670672            data.update({'action': data.getlist('action')[action_index]})
    class ModelAdmin(BaseModelAdmin):  
    673675            # POST data, so by deleting action it'll fail the validation check
    674676            # below. So no need to do anything here
    675677            pass
    676        
     678
    677679        action_form = self.action_form(data, auto_id=None)
    678680        action_form.fields['action'].choices = self.get_action_choices(request)
    679681
    class ModelAdmin(BaseModelAdmin):  
    877879        app_label = opts.app_label
    878880        if not self.has_change_permission(request, None):
    879881            raise PermissionDenied
    880        
     882
    881883        # Check actions to see if any are available on this changelist
    882884        actions = self.get_actions(request)
    883        
     885
    884886        # Remove action checkboxes if there aren't any actions available.
    885887        list_display = list(self.list_display)
    886888        if not actions:
    class ModelAdmin(BaseModelAdmin):  
    888890                list_display.remove('action_checkbox')
    889891            except ValueError:
    890892                pass
    891        
     893
    892894        try:
    893895            cl = ChangeList(request, self.model, list_display, self.list_display_links, self.list_filter,
    894896                self.date_hierarchy, self.search_fields, self.list_select_related, self.list_per_page, self.list_editable, self)
    class ModelAdmin(BaseModelAdmin):  
    901903            if ERROR_FLAG in request.GET.keys():
    902904                return render_to_response('admin/invalid_setup.html', {'title': _('Database error')})
    903905            return HttpResponseRedirect(request.path + '?' + ERROR_FLAG + '=1')
    904                
     906
    905907        # If the request was POSTed, this might be a bulk action or a bulk edit.
    906908        # Try to look up an action first, but if this isn't an action the POST
    907909        # will fall through to the bulk edit check, below.
  • django/forms/models.py

    diff --git a/django/forms/models.py b/django/forms/models.py
    index 010d3bf..8d59dfd 100644
    a b def modelform_factory(model, form=ModelForm, fields=None, exclude=None,  
    345345                       formfield_callback=lambda f: f.formfield()):
    346346    # HACK: we should be able to construct a ModelForm without creating
    347347    # and passing in a temporary inner class
    348     class Meta:
    349         pass
    350     setattr(Meta, 'model', model)
    351     setattr(Meta, 'fields', fields)
    352     setattr(Meta, 'exclude', exclude)
     348    attrs = {}
     349    if fields is not None:
     350        attrs['fields'] = fields
     351    if exclude is not None:
     352        attrs['exclude'] = exclude
     353    attrs['model'] = model
     354    parent = (object,)
     355    if hasattr(form, 'Meta'):
     356        parent = (form.Meta, object)
     357    meta = type('Meta', parent, attrs)
    353358    class_name = model.__name__ + 'Form'
    354     return ModelFormMetaclass(class_name, (form,), {'Meta': Meta,
     359    return ModelFormMetaclass(class_name, (form,), {'Meta': meta,
    355360                              'formfield_callback': formfield_callback})
    356361
    357362
  • tests/regressiontests/modeladmin/models.py

    diff --git a/tests/regressiontests/modeladmin/models.py b/tests/regressiontests/modeladmin/models.py
    index 3a7d3f0..eeb2563 100644
    a b class Band(models.Model):  
    88    name = models.CharField(max_length=100)
    99    bio = models.TextField()
    1010    sign_date = models.DateField()
    11    
     11
    1212    def __unicode__(self):
    1313        return self.name
    1414
    displayed because you forgot to add it to fields/fielsets  
    118118
    119119# Using `exclude`.
    120120
    121 >>> class BandAdmin(ModelAdmin): 
    122 ...     exclude = ['bio'] 
    123 >>> ma = BandAdmin(Band, site) 
    124 >>> ma.get_form(request).base_fields.keys() 
     121>>> class BandAdmin(ModelAdmin):
     122...     exclude = ['bio']
     123>>> ma = BandAdmin(Band, site)
     124>>> ma.get_form(request).base_fields.keys()
    125125['name', 'sign_date']
    126126
    127127# You can also pass a tuple to `exclude`.
    128  
    129 >>> class BandAdmin(ModelAdmin): 
    130 ...     exclude = ('bio',) 
    131 >>> ma = BandAdmin(Band, site) 
    132 >>> ma.get_form(request).base_fields.keys() 
     128
     129>>> class BandAdmin(ModelAdmin):
     130...     exclude = ('bio',)
     131>>> ma = BandAdmin(Band, site)
     132>>> ma.get_form(request).base_fields.keys()
    133133['name', 'sign_date']
    134  
     134
    135135# Using `fields` and `exclude`.
    136136
    137 >>> class BandAdmin(ModelAdmin): 
    138 ...     fields = ['name', 'bio'] 
    139 ...     exclude = ['bio'] 
    140 >>> ma = BandAdmin(Band, site) 
    141 >>> ma.get_form(request).base_fields.keys() 
     137>>> class BandAdmin(ModelAdmin):
     138...     fields = ['name', 'bio']
     139...     exclude = ['bio']
     140>>> ma = BandAdmin(Band, site)
     141>>> ma.get_form(request).base_fields.keys()
    142142['name']
    143143
    144144If we specify a form, it should use it allowing custom validation to work
    properly. This won't, however, break any of the admin widgets or media.  
    147147>>> from django import forms
    148148>>> class AdminBandForm(forms.ModelForm):
    149149...     delete = forms.BooleanField()
    150 ...     
     150...
    151151...     class Meta:
    152152...         model = Band
    153153
    blank=True for the model field. Finally, the widget should have the  
    262262>>> list(cmafa.base_fields['transport'].widget.choices)
    263263[('', u'None'), (1, 'Plane'), (2, 'Train'), (3, 'Bus')]
    264264
     265>>> class AdminConcertForm(forms.ModelForm):
     266...     class Meta:
     267...         model = Concert
     268...         exclude = ('transport',)
     269
     270>>> class ConcertAdmin(ModelAdmin):
     271...     form = AdminConcertForm
     272
     273>>> ma = ConcertAdmin(Concert, site)
     274>>> ma.get_form(request).base_fields.keys()
     275['main_band', 'opening_band', 'day']
     276
    265277>>> band.delete()
    266278
    267279# ModelAdmin Option Validation ################################################
    Traceback (most recent call last):  
    371383...
    372384ImproperlyConfigured: Both fieldsets and fields are specified in ValidationTestModelAdmin.
    373385
    374 >>> class ValidationTestModelAdmin(ModelAdmin): 
    375 ...     fieldsets = [(None, {'fields': ['name', 'name']})] 
    376 >>> validate(ValidationTestModelAdmin, ValidationTestModel) 
    377 Traceback (most recent call last): 
    378 ... 
     386>>> class ValidationTestModelAdmin(ModelAdmin):
     387...     fieldsets = [(None, {'fields': ['name', 'name']})]
     388>>> validate(ValidationTestModelAdmin, ValidationTestModel)
     389Traceback (most recent call last):
     390...
    379391ImproperlyConfigured: There are duplicate field(s) in ValidationTestModelAdmin.fieldsets
    380392
    381393>>> class ValidationTestModelAdmin(ModelAdmin):
Back to Top