Question

I am scratching my head over this. I have an enumerated data in my db. My model has something like:

class SomeModel(models.Model):
    TWOCHOICE = (
    ('YES', 'YES'),
    ('NO', 'NO'),
    ('DK', 'Don't Know'),
    )

myfield = models.IntegerField(max_length=1, choices=TWOCHOICE)

Now when I want to query this, I CANNOT use:

SomeModel.objects.filter(myfield__in = ['YES', 'NO'])

which looks the most straight forward. I have to use integer values. So I ended up using:

SomeModel.objects.filter(myfield__in = [[t[0] for t in SomeModel.TWOCHOICE].index(i) for i in ['YES', 'NO']])

But that's not even enough. The fields seem to be indexed from 1!!!! so I need something like:

SomeModel.objects.filter(myfield__in = [[t[0] for t in SomeModel.TWOCHOICE].index(i)+1 for i in ['YES', 'NO']])

I just think this way too complicated. Is there a better way to query the enumerated types in Django?

Was it helpful?

Solution

For choice fields for readability Django allows you to specify two values, first is the actual value to be stored in the DB, and the second one will be displayed in forms, etc for readability. For example in

CHOICES = (
    (0, 'No'),
    (1, 'Yes'),
    (2, 'Don\'t Know'),
)

the values which will be stored in the DB would be [0,1,2] however for readability their associated strings will be displayed in forms, etc.

Now as for your problem, the issue here is that you are using an IntegerField for myfield however are trying to filter by a string. Whenever you do filter lookups (filter(...) or exclude(...)), you must provide the values that are related to what is being stored in the database. Therefore whenever you want to filter on an IntegerField you must provide an integer for filtering. I am not sure why Django allowed you to have choices for an IntegerField where the first values are not integers but you should probably change that. If you will use the above example choice values, you can filter for yes or no by

.filter(myfield__in=[0,1])

If you want to filter by the associated string values, there is really no simple method. You have to lookup the stored value related to the string first.

def lookup_choices(choices, values):
    choices = dict(choices)
    rchoices = {choices [k]: k for k in choices}
    return [rchoices[v] for v in values]

.filter(myfield__in=lookup_choices(CHOICES, ['Yes', 'No'])

You however rarely should need to do this because Django will only use associated string values for rendering values (select box) but will always return you the actual values (e.g. form.cleaned_data).

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top