how to modify choices on admin pages - django

11,011

Solution 1

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

Solution 2

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.

Share:
11,011
shanyu
Author by

shanyu

Mechanical Engineering + MBA + PhD in finance. Python & Javascript.

Updated on July 11, 2022

Comments

  • shanyu
    shanyu almost 2 years

    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?

  • shanyu
    shanyu almost 15 years
    that's a possibility, however not a very-elegant and secure one. changing the state triggers some calculations and modifications of the data to be made, therefore I really don't want to trust users, even if they are admins.
  • Carl Meyer
    Carl Meyer almost 15 years
    No, he's trying to set the choices for the next value of a field based on the current value of that same field - a key difference. So changing the options dynamically client-side is not relevant; in fact it would be very confusing.
  • Jj.
    Jj. almost 15 years
    What happens on the 'add' views for the admin, since there's no self.instance, you can't depend on the instance for filtering, it would be nice to have the request object there
  • Carl Meyer
    Carl Meyer almost 15 years
    Yes, this ModelForm would need to handle the absence of self.instance and set the initial available choices appropriately. I don't know why the request object is relevant, but you do have access to it in ModelAdmin.add_view (code.djangoproject.com/browser/django/trunk/django/contrib/‌​…).
  • Ivan Vučica
    Ivan Vučica almost 12 years
    Is it possible to change choices in model itself? After all, choices ARE initially specified in the model, when creating the field.
  • Carl Meyer
    Carl Meyer almost 12 years
    Yes, you could do a similar thing in an overridden model init method instead. The question just asked about how to do it in the admin.
  • crazy_ljuba
    crazy_ljuba about 8 years
    @CarlMeyer hi, i have a question related to populating the choices inside admin.py , is it possible to have like : form = form1 list_editable = ('status') fields = ( 'field1', 'field2') inside the class. Where field2 choices are the one that needs to be initially changed ? I have created this question question_link and now after researching came to this one :) Thanks
  • crazy_ljuba
    crazy_ljuba about 8 years
    @CarlMeyer Found out what was the problem , nvm