Django - 对预取QuerySet进行注释?

时间:2018-02-12 12:26:03

标签: django

是否可以对预取查询进行注释/计数?

我在下面的初步查询是基于电路的,然后我意识到,如果一个站点没有任何电路,我就不会有“无”类别,这会将站点显示为Down。

conn_data = Circuits.objects.all() \
    .values('circuit_type__circuit_type') \
    .exclude(active_link=False) \
    .annotate(total=Count('circuit_type__circuit_type')) \
    .order_by('circuit_type__monitor_priority')

所以我改为查询网站并使用预取,现在对于没有活动链接的任何网站,它都有一个空的circuits_set。是否有一种Django方法可以在conn_data中针对各种loops_set创建新的总数?我打算手动遍历所有站点并以这种方式添加总计,但想知道是否有办法在QuerySet中执行此操作?

我的最终结果应该是:

[
    {'circuit_type__circuit_type': 'Fibre', 'total': 63},
    {'circuit_type__circuit_type': 'DSL', 'total': 29},
    {'circuit_type__circuit_type': 'None', 'total': 2}
]

预取查询:

conn_data = SiteData.objects.prefetch_related(
                                Prefetch(
                                'circuits_set',
                                queryset=Circuits.objects.exclude(active_link=False).select_related('circuit_type'),
                                )
                        )

2 个答案:

答案 0 :(得分:0)

我不认为这会奏效。它是否应该起作用值得商榷。我们来看prefetch_related做什么。

  

返回一个QuerySet,它将在一个批处理中自动检索每个指定查找的相关对象。

所以这里发生的是调度两个查询并实现两个列表。然后将这些列表分区在内存中并分组到正确的父记录中。

Count()annotate()是解析为SQL

的DBMS的指令
Select Count(id) from conn_data

由于annotateprefetch_related的工作方式,我认为它们不太可能在一起发挥出色。 prefetch_related只是方便。从实际角度来看,运行两个单独的ORM查询并将它们自己分配给SiteData记录实际上是同样的事情。所以......就像......

#Gets all Circuits counted and grouped by SiteData 

Circuits.objects.values('sitedata_id)'.exclude(active_link=False).select_related('circuit_type').annotate(Count('site_data_id'));

然后,您只需遍历SiteData条记录并指定计数。

答案 1 :(得分:0)

好的,我得到了我想要的东西,可能是一个更好的方法,但它的工作从未如此:

from collections import Counter
import operator

class ConnData(object):
    def __init__(self, priority='', c_type='', count=0 ):
        self.priority = priority
        self.c_type = c_type
        self.count = count
    def __repr__(self):
        return '{} {}'.format(self.__class__.__name__, self.c_type)  
# get all the site data
conn_data = SiteData.objects.exclude(Q(site_type__site_type='Data Centre') | Q(site_type__site_type='Factory')) \
                    .prefetch_related(
                                Prefetch(
                                'circuits_set',
                                queryset=Circuits.objects.exclude(active_link=False).select_related('circuit_type'),
                                )
                        )
# create a list for the conns
conns = []
# add items to list of dictionaries with all required fields
for conn in conn_data:
    try:
        conn_type = conn.circuits_set.all()[0].circuit_type.circuit_type
        prioritiy = conn.circuits_set.all()[0].circuit_type.monitor_priority
        conns.append({'circuit_type' : conn_type, 'priority' : prioritiy})
    except:
        # create category for down sites
        conns.append({'circuit_type' : 'Down', 'priority' : 10})
# crate new list for class data
conn_counts = []
# create counter data
conn_count_data = Counter(((d['circuit_type'], d['priority']) for d in conns))
# loop through counter data and add classes to list
for val, count in conn_count_data.items():
    cc = ConnData()
    cc.priority = val[1]
    cc.c_type = val[0]
    cc.count = count
    conn_counts.append(cc)
# sort the classes by priority
conn_counts = sorted(conn_counts, key=operator.attrgetter('priority'))