shanyu
shanyu

Reputation: 9716

How to modify choices on Django Admin

I have a model that has a field named "state":

class Foo(models.Model):
    ...
    state = models.IntegerField(choices = STATES)
    ...

For every state, possible choices are a certain subset of all STATES. For example:

if foo.state == STATES.OPEN:     #if foo is open, possible states are CLOSED, CANCELED
    ...
if foo.state == STATES.PENDING:  #if foo is pending, possible states are OPEN,CANCELED
    ...

As a result, when foo.state changes to a new state, its set of possible choices changes also.

How can I implement this functionality on Admin add/change pages?

Upvotes: 10

Views: 13761

Answers (5)

By overriding formfield_for_choice_field(), you can modify choices for Django Admin without creating a custom "forms.ModelForm" as shown below:

@admin.register(MyModel)
class MyModelAdmin(admin.ModelAdmin):
    def formfield_for_choice_field(self, db_field, request, **kwargs):
        if db_field.name == "status":
            kwargs['choices'] = (
                ('accepted', 'Accepted'),
                ('denied', 'Denied'),
            )
            if request.user.is_superuser:
                kwargs['choices'] += (('ready', 'Ready for deployment'),)
        return super().formfield_for_choice_field(db_field, request, **kwargs)

Upvotes: -1

slamora
slamora

Reputation: 727

When you create a new admin interface for a model (e.g. MyModelAdmin) there are specific methods for override the default choices of a field. For a generic choice field:

class MyModelAdmin(admin.ModelAdmin):
    def formfield_for_choice_field(self, db_field, request, **kwargs):
        if db_field.name == "status":
            kwargs['choices'] = (
                ('accepted', 'Accepted'),
                ('denied', 'Denied'),
            )
            if request.user.is_superuser:
                kwargs['choices'] += (('ready', 'Ready for deployment'),)
        return super(MyModelAdmin, self).formfield_for_choice_field(db_field, request, **kwargs)

But you can also override choices for ForeignKey and Many to Many relationships.

Upvotes: 19

Daniel Roseman
Daniel Roseman

Reputation: 599470

This seems like a job for some javascript. You want the list of items in a select box to change depending on the value of something else, which is presumably a checkbox or radio button. The only way to make that happen dynamically - without getting the user to save the form and reload the page - would be with javascript.

You can load custom javascript in a model's admin page by using the ModelAdmin's Media class, documented here.

Upvotes: 0

Carl Meyer
Carl Meyer

Reputation: 126531

You need to use a custom ModelForm in the ModelAdmin class for that model. In the custom ModelForm's __init__ method, you can dynamically set the choices for that field:

class FooForm(forms.ModelForm):
    class Meta:
        model = Foo

    def __init__(self, *args, **kwargs):
        super(FooForm, self).__init__(*args, **kwargs)
        current_state = self.instance.state
        ...construct available_choices based on current state...
        self.fields['state'].choices = available_choices

You'd use it like this:

class FooAdmin(admin.ModelAdmin):
    form = FooForm

Upvotes: 13

Jiaaro
Jiaaro

Reputation: 76878

I see what you're trying to do, but why not just display all of them and if the person picks the (already set) current state, just don't change anything?

you could also just build a view with a form to provide this functionality

Upvotes: -1

Related Questions