get ids group by range django

瘦欲@ 提交于 2020-01-03 03:09:07

问题


I have: Model:

id = PrimaryKey()
value = IntegerField()

I would like to get list of ids group by value range between 1-7 and 8-30 and >30.
What would be the efficient way to do so?
Or shall I write query for each range like:

one= Model.objects.filter(value__gt=0,value__lte=7).values_list(id)
two= Model.objects.filter(value__gt=7,value__lte=30).values_list(id)
three= Model.objects.filter(value__gt=30).values_list(id)

... can I do so via aggregate Count? I want minimum database transactions.

Updated:

Actually am trying to get how many products are sold, If they expires soon, then weekly count else monthly like:

  • Milk expires in 3 days. How many milk packets were sold in past 3 days
  • Bread expires in 7 days. How many bread were sold in past 7 days.

here value is expires_in i.e. in how many days a product expires in
(expiry date - mfg date)

Model is like:

InvoiceBreakup

product_id
name
desctiption
expires_in

回答1:


With groupby

If you need the ids, we can use groupby of itertools for this:

from itertools import groupby
from operator import itemgetter

def rank(itm):
    vl = itm['value']
    if vl <= 7:
        return 0
    elif vl <= 30:
        return 1
    return 2

dat = {
    k: list(map(itemgetter('id'),v))
    for k, v in groupby(
        Model.objects.filter(value__gt=0).values('id', 'value').order_by('value'),
        rank)
}

Now dat will contain three elements: 0 contains the list of elements in the range of 1-7, 1 for elements in the range 8-30 and 2 in are ids with value 30+.

We can thus then obtain the list of ids, with:

dat[0]  # ids with value 1-7
dat[1]  # ids with value 8-30
dat[2]  # ids with value 31+

This will result in a single query, and we let Python do the grouping. This can be slow if the data is huge, but it will already get quite slow, since the deserializing of the elements also will take comparable effort.

Optimizing the checks

We can let the database perform the logic to determine the category, and thus move some work from Python to the database:

from django.db.models import Case, IntegerField, Value

qs = Model.objects.filter(value__gt=0).annotate(
    cat = Case(
        When(value__lt=8, then=Value(0)),
        When(value__lt=31, then=Value(1)),
        default=Value(2),
        output_field=IntegerField(),
    ))
).values('id', 'cat').order_by('cat')

and then construct the dictionary with:

from operator import itemgetter
from itertools import groupby

data = {
    k: list(map(itemgetter('id'), v))
    for k, v in groupby(qs, itemgetter('cat'))
}


来源:https://stackoverflow.com/questions/51928253/get-ids-group-by-range-django

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!