Django Admin: Using a custom widget for only one model field

55,200

Solution 1

Create a custom ModelForm for your ModelAdmin and add 'widgets' to its Meta class, like so:

class StopAdminForm(forms.ModelForm):
  class Meta:
    model = Stop
    widgets = {
      'approve_ts': ApproveStopWidget(),
    }
    fields = '__all__'

class StopAdmin(admin.ModelAdmin):
  form = StopAdminForm

Done!

Documentation for this is sort of non-intuitively placed in the ModelForm docs, without any mention to it given in the admin docs. See: Creating forms from models

Solution 2

After digging into the admin, model field and form field code, I believe the only way to carry out what I want is by creating a custom model field:

models.py

from django.db import models
from widgets import ApproveStopWidget

class ApproveStopModelField(models.DateTimeField):
    pass

class Stop(models.model):
    # Other fields
    approve_ts = ApproveStopModelField('Approve place', null=True, blank=True)

admin.py

from widgets import ApproveStopWidget
from models import ApproveStopModelField

class StopAdmin(admin.ModelAdmin):
    formfield_overrides = {
        ApproveStopModelField: {'widget': ApproveStopWidget }
    }

It gets the job done.

For the time being, I'll leave the question unanswered because I have the habit of missing the obvious. Perhaps some Django smartypants has a better solution.

Solution 3

Override formfield_for_dbfield like thus:

class VehicleAdmin(admin.ModelAdmin):
    search_fields = ["name", "colour"]

    def formfield_for_dbfield(self, db_field, **kwargs):
        if db_field.name == 'colour':
            kwargs['widget'] = ColourChooserWidget
        return super(VehicleAdmin, self).formfield_for_dbfield(db_field,**kwargs)

(credit to http://www.kryogenix.org/days/2008/03/28/overriding-a-single-field-in-the-django-admin-using-newforms-admin/ )

Solution 4

Django's ModelAdmin.get_changelist_form(self, request, **kwargs) will do the trick for the case of list_editable

class StopAdminForm(forms.ModelForm):
  class Meta:
    model = Stop
    widgets = {
      'approve_ts': ApproveStopWidget(),
    }

class StopAdmin(admin.ModelAdmin):
  form = StopAdminForm

  #just return the ModelForm class StopAdminForm
  def get_changelist_form(self, request, **kwargs):
        return StopAdminForm

Refer to Django Official documentation on this topic

I hope this will help

Share:
55,200
Belmin Fernandez
Author by

Belmin Fernandez

Learning and helping.

Updated on March 11, 2020

Comments

  • Belmin Fernandez
    Belmin Fernandez about 4 years

    I have a DateTimeField field in my model. I wanted to display it as a checkbox widget in the Django admin site. To do this, I created a custom form widget. However, I do not know how to use my custom widget for only this one field.

    The Django documentation explains how to use a custom widget for all fields of a certain type:

    class StopAdmin(admin.ModelAdmin):
        formfield_overrides = {
            models.DateTimeField: {'widget': ApproveStopWidget }
        }
    

    This is not granular enough though. I want to change it for only one field.

  • Mechanical snail
    Mechanical snail over 11 years
    Unfortunately this only seems to work for the main "edit" page, and not on the list display, whereas formfield_overrides works for both.
  • Chris Pratt
    Chris Pratt over 11 years
    And that warranted a downvote? Please take a moment to read "Vote Down" privilege description: stackoverflow.com/privileges/vote-down. I'm pretty positive my answer doesn't qualify as "an answer that is clearly and perhaps dangerously incorrect".
  • Mechanical snail
    Mechanical snail over 11 years
  • yar
    yar over 11 years
    For those wondering how to integrate this with South, the line you would need for this example is: from south.modelsinspector import add_introspection_rule add_introspection_rules([], ["^appname\.models\.ApproveStopModelField"])
  • maciek
    maciek about 9 years
    Starting with Django 1.8 you should add fields = '__all__' to Meta. Vide stackoverflow.com/a/28306347/1161025.
  • alxs
    alxs over 7 years
    Nice. Adding a custom ModelForm for changing just a widget of one field seems like an overkill to me.
  • Sławomir Lenart
    Sławomir Lenart about 7 years
    ... and approve_ts means what? then repeated in next answers. There is no such thing in Django source, either in topic's question.
  • Stephen Blair
    Stephen Blair over 6 years
    Just tried this in Django 1.11.5 and it worked perfectly. My model has multiple TextFields and in the Admin change form I needed some to be regular <textarea> elements and others to be TinyMCE rich text editors to accept HTML instead of plain text. The all or nothing 'formfield_overrides' wasn't useful in this scenario.