I have a ChoiceField Callable that Includes a Queryset but it Breaks my Django app

Sometimes it doesn’t make a lot of sense why some things work in Django and others don’t. For example, a ChoiceField has a choices parameter where you can pass in a set of choices and your form will become a select field in your template.

The Django docs says the following:

choices

...an iterable (e.g., a list or tuple)
   of 2-tuples to use as choices for this field...

Therefore, some people might write the following:

1
dropdown = forms.ChoiceField(choices=[(1, "Option 1"), (2, "Option 2")])

According to the Django docs, this makes a lot of sense. However, what about if you have a function the returns the same thing? Wouldn’t it might makes sense to write it like this?

1
2
3
4
def choice_list():
    return [(1, "Option 1"), (2, "Option 2")]

dropdown = forms.ChoiceField(choices=choice_list())

Yes! This will still work. However, what happens if you have a queryset inside of your choices callable? For example, maybe you have code that looks like this:

1
2
3
4
5
6
def get_available_choices():
    choices = ()
    for choice in Choice.objects.all():
        choices = choices + ((choice.data, choice.data),)

    return choices

If you create your form field like this:

1
dropdown = forms.ChoiceField(choices=get_available_choices())

Suddenly, your app breaks. Everytime you need to do ./manage.py migrate you’ll get errors and unless you know where you look, it’s going to be extremely difficult to find the issue. What do you do? What is going on?

*Django looks at your URLs and parses your views.py files before it attempts to create models. It’s literally trying to call a model that doesn’t yet exist before it tries to create the models.*

Since Django 1.8, the ChoiceField choices parameter allows you to pass in a callable. Therefore, your app won’t immediately try to call your dynamic choices callable until your models have been created and your form is being initialized.

Therefore, if you have a function like get_available_choices like above, you need to make your field look like this. Notice, all I did was remove the parameters of the function call.

1
dropdown = forms.ChoiceField(choices=get_available_choices)

Do you have a horrible error when trying to run makemigrations?

1
2
$ ./manage.py makemigrations
django.db.utils.OperationalError: no such table: my_app_choice

If you do just try removing the parentheses () from your dynamic choices callable from one of your forms and see if that helps!

comments powered by Disqus