Custom choices in Django admin

Oct. 5, 2012 - Joeri - django - admin - choicefield - ticket - Software

Problem

You have your model with a fixed set of choices. Typically:

models.py

class Issue(models.Model):
    type = models.CharField(max_length=1, choices=(('t', 'task'), ('b', 'bug')))

Now, in your Django admin, you want to show different choices:

admin.py

class IssueAdmin(admin.ModelAdmin):
    def formfield_for_choice_field(self, db_field, request, **kwargs):
        if db_field.name == "type":
            kwargs['choices'] = (
                ('b', 'bug'),
                ('e', 'enhancement'),
            )
        return super(IssueAdmin, self).formfield_for_choice_field(db_field, request, **kwargs)

The above doesn't work when you try to save with choice "enhancement". Why? Because your model field doesn't accept your input (it doesn't contain the enchancement choice). Why, I changed the choices in the ``IssueAdmin``? Yes, well, you changed the choices in the form field (hence the formfield_for... function name) that is based on your model. You didn't change the choices on your model field.

Solution

Here's the code that allows the desired behaviour:

models.py

from django.db import models

class Issue(models.Model):
    type  = models.CharField(max_length=1)

admin.py

from django import forms
from django.contrib import admin
from .models import Issue

class IssueForm(forms.ModelForm):
    class Meta:
        model = Issue
        widgets = {
            'type': forms.Select(),
        }

class IssueAdmin(admin.ModelAdmin):
    form = IssueForm

    # Note the use of formfield_for_dbfield instead of formfield_for_choice_field!
    def formfield_for_dbfield(self, db_field, **kwargs):
        # Add some logic here to base your choices on.
        if db_field.name == 'type':
            kwargs['widget'].choices = (
                ('b', 'bug'),
                ('e', 'enhancement'),
            )
        return super(IssueAdmin, self).formfield_for_dbfield(db_field, **kwargs)

admin.site.register(Issue, IssueAdmin)

You cannot use a ChoiceField in your form and use formfield_for_choice_field. This function actually checks whether your model field has an attribute called choices (and since it's a CharField without any choices it obviously does not have this attribute).

I also described this as answer on a Django ticket which lead me to write this blog post.



Latest Tweets