Search code examples
pythondjangomodels

How to Call django model field name from a list?


django models.py

class mymodel(models.Model):
   date_check   = models.DateField()
   item_1   = models.NullBooleanField()
   item_2   = mod`enter code here`els.NullBooleanField()
   item_3   = models.NullBooleanField()
   item_4   = models.NullBooleanField()
   item_5   = models.NullBooleanField()

Task:

>>> from .models import mymodel
>>> 
>>> a_list = ['item_1', 'item_2', 'item_3', 'item_4', 'item_5', 'item_5', ]
>>> a_year = 2018
>>> param1 = {}
>>> param2 = {}
>>> param3 = {}
>>> for item in a_list :
>>>    param1[item] = mymodel.objects.filter(date_check__year = a_year, item=True).count()
>>>    param2[item] = mymodel.objects.filter(date_check__year = a_year, item=False).count()
>>>    param3[item] = mymodel.objects.filter(date_check__year = a_year, item=None).count()
.....error here

how do we call field name from list?


Solution

  • Here item is interpreted as an identifier, so the name of the parameter is item. The fact that there is a variable with the same name and a certain value, is not relevant.

    We can however fix this by constructing a dictionary that maps item (so here it will be replaced with the corresponding value) to True, False, and None. We the can use this dictionary as the named parameters dictionary* by using two consecutive asterisks as prefix:

    for item in a_list :
        param1[item] = mymodel.objects.filter(date_check__year=a_year, **{item: True}).count()
        param2[item] = mymodel.objects.filter(date_check__year=a_year, **{item: False}).count()
        param3[item] = mymodel.objects.filter(date_check__year=a_year, **{item: None}).count()

    Note that this will hoever result in multiple queries. We can reduce the amount of querying by for example aggregating over a selection, like:

    qs = mymodel.objects.filter(date_check__year=a_year)
    
    for item in a_list :
        data = qs.aggregate(
            p1=Count('pk', filter=Q(**{item: True}))
            p2=Count('pk', filter=Q(**{item: False}))
            p3=Count('pk', filter=Q(**{item: None}))
        )
        param1[item] = data['p1'] or 0
        param2[item] = data['p2'] or 0
        param3[item] = data['p3'] or 0

    Here we thus count three things per query: the number of Trues, Falses and Nones. We can actually extend this logic, and even count everything in a single query, which is typically more efficient (this is not per se true, but making a query typically results in some overhead to construct a query, interpret the query at the database side, serializing results, etc.).