Django has been gradually changing the way their automatically-created admin system works to use the newforms-admin code, which makes lots of cool new things possible. However, because newforms-admin is rather new (ha!), it’s not brilliantly documented. One of the things I wanted to do today was to make one field use a custom field-editing widget that I’d created, rather than Django’s default textbox, in the Django admin system. You do that like this. In newforms-admin, you specify admin options for a model by creating an extra ModelAdmin class for it:
class Vehicle(models.Model):
colour = models.CharField()
name = models.CharField()
class VehicleAdmin(admin.ModelAdmin):
search_fields = ["name", "colour"]
admin.site.register(Vehicle, VehicleAdmin)
Imagine that we wanted to build a custom widget to allow people to
choose a colour by clicking on a colour swatch. To do this, you need to
actually create your custom widget. So, in a file custom_widgets.py
,
you create your widget. The easiest way to do this is to subclass one of
the existing widgets (TextInput is a good one here, because that’s a
normal textbox, which is what gets used by default for CharFields) and
then change its render method:
import django.newforms as forms
from string import Template
from django.utils.safestring import mark_safe
class ColourChooserWidget(forms.TextInput):
def render(self, name, value, attrs=None):
tpl = Template(u"""<h1>There would be a colour widget here, for value $colour</h1>""")
return mark_safe(tpl.substitute(colour=value))
There are a few interesting wrinkles in there. First, overriding
render()
changes the HTML that your widget prints when asked to
display itself by the admin system. (I haven’t actually implemented the
widget there; left as an exercise for the reader, that bit.) Second, you
need to call mark_safe()
on the HTML you return, otherwise the admin
will escape it. Third, all input to mark_safe()
must be Unicode, hence
the u"""
at the beginning of the string. The parameter value
already
is Unicode, but any strings you provide must also be explicitly Unicode
strings; otherwise, mark_safe()
fails silently — the string will be
escaped. Fourth, you don’t have to use string.Template
, but it’s
pretty convenient. Once you’ve created your custom widget, you have to
hook it up to your model. In our example, we need to change
VehicleAdmin
:
from custom_widgets import ColourChooserWidget
...
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(ArticleOptions,self).formfield_for_dbfield(db_field,**kwargs)
The formfield_for_dbfield()
function gets called for each of the
fields in your model; for the one we care about (colour
in this
example), override widget
in the kwargs
and then carry on with the
rest of the function, and that hooks it up. That should be it; now, in
the Django admin system, your colour
field will use your
ColourChooserWidget
for editing.