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

Posted by Chris Bartos on April 13, 2016

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:


...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:

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?

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:

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:

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.

dropdown = forms.ChoiceField(choices=get_available_choices)

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

$ ./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!

Need help with Django REST Framework? Django REST Framework documentation a little confusing?

Join me for my FREE Django REST Framework email course:

Django REST Framework Email Course

You'll get 1 lesson everyday for 7 days.
You'll learn:

  • Serializers
  • Request Methods
  • Endpoints
  • Basic Authentication
  • JQuery Integration
  • AngularJS Integration
Powered by ConvertKit

Similar Posts

What do you do when 'makemigrations' is telling you About your Lack of Default Value
How to Implement Many-To-Many Relationship in Django
Test your Queries before they make it into Production using the Django Shell
How to filter a DateTimeField by today's date in Django
Help! My Models feel Bloated! Why you should write a custom Manager