Buddyshot
Buddyshot

Reputation: 1684

Populate choices in a Django field (models) based on saved data

Let us consider the following model

class MyModel(models.Model):
    stock   = models.IntegerField(blank=True, default=0)
    to_be_filled_later = models.CharField(max_length=20, blank=True)

suppose also that I manually created an entry with stock set to 42 and to_be_filled_later left blank.

When I go to the admin panel (modification) of MyModel and select my new entry, I would like to set to_be_filled_later to some value, but I also want to restrict the set of possible values based on the current value of self.stock, i.e. 42.

Since Field.choices can be set to any iterable, one would be tempted to do something like

class MyModel(models.Model):
    stock   = models.IntegerField(blank=True, default=0)

    def restricted_choices(self):
        # do something based on self.stock
        yield (some_stuff.pk, some_stuff.name)
    to_be_filled_later = models.CharField(max_length=20, 
                                          blank=True, 
                                          choices=restricted_choices(self))

however this won't work because restricted_choices should be called with a reference to self that is not defined in the scope where we create the fields (here in the definition of to_be_filled_later, to be precise).

We could maybe override __init__ but that's not what we want since it would take effect just after every entry creation. What we want is to have a dynamically, instance based choices.

How would you go with this?

Upvotes: 0

Views: 483

Answers (1)

Daniel Roseman
Daniel Roseman

Reputation: 599876

You do want to override __init__, but in the form, not in the model.

class MyModelForm(forms.ModelForm):
    to_be_filled_later = forms.ChoiceField(choices=[])

    class Meta:
        model = MyModel

    def __init__(self, *args, **kwargs):
        super(MyModelForm, self).__init__(*args, **kwargs)
        if self.instance:
            self.fields['to_be_filled_later'].choices = restricted_choices(self.instance.stock)

and assign this form to the model admin for MyModel:

class MyModelAdmin(admin.ModelAdmin):
    model = MyModel
    form = MyModelForm

admin.site.register(MyModel, MyModelAdmin)

Upvotes: 2

Related Questions