Overriding a single field in the Django admin, using newforms-admin

[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”“”

There would be a colour widget here, for value $colour

“”“) 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. [Django]: http://www.djangoproject.com