Writing a custom admin widget can be a little tricky, due to the way that form data is handled. In order to minimize your trouble, I would highly recommend extending an existing widget if at all possible. It may save you a lot of trouble, since Django has a lot of custom labeling and logic to create, populate, validate, and submit admin forms. Once you have a functional baseline, any custom behavior can overwrite the defaults.
This example comes from a project with a purchase request model. The admin site had a model changeform, which contained information about the requester, the requested item, and so on. We also had a textfield which was a human-readable phrase describing the purchase request. This field needed to have a button near it which would pull information from other parts of the form, and could then be manually edited and submitted to the database normally.
In this example, I will walk you through the creation of a custom formfield widget and how to get it properly plugged into the admin. The widget itself is just a textarea with a button, so all we need to do is take the HTML output from the textarea and append it. The render() method accepts the currently instantiated widget object (self), the name of the form field using the widget (name), the current contents of the html textarea (value), and any attributes passed to it by the widget class; it is responsible for returning a valid HTML string describing the widget, so that is what we will construct.
One thing you need to know is that each purchase request can be associated with an asset. We’re going to want to know which asset the purchase request is linked to, so we start off by creating a purchase request/asset dictionary. Thus:
Now that our widget is defined, all we need to do is link it to an admin field. We do this by setting the formfield widget to PRNotesWidget like so:
# In apps/purchaserequest/fields.py from purchaserequest.widgets import PRNotesWidget class PRNotesField(models.TextField): def formfield(self, **kwargs): kwargs['widget'] = PRNotesWidget return super(PRNotesField, self).formfield(**kwargs)
The field needs to be explicitly specified in a form:
# In apps/purchaserequest/forms.py from purchaserequest.fields import PRNotesField class PRAdminForm(forms.ModelForm): # The form for the purchase request model should # use our custom field accounting_notes = PRNotesField()
And then, of course, we need to make sure we’re using that form in the admin:
# In apps/purchaserequest/admin.py from purchaserequest.forms import PRAdminForm class PRAdmin(admin.ModelAdmin): # The purchase request admin should be using the # custom admin form form = PRAdminForm
You’ll note that I’ve split the admin, form, field, and widget each into files with their respective names. This is only really necessary if you have a large project with lots of custom widgets and fields. However, this structure is preferable both for being prepared for the future, as well as to understand the hierarchy and flow of the app.