【问题标题】:Django - annotate against a prefetch QuerySet?Django - 对预取查询集进行注释?
【发布时间】:2018-02-12 12:26:03
【问题描述】:

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

我在下面的初始查询是基于电路的,然后我意识到,如果一个站点没有任何电路,我将不会有一个“无”类别,它将一个站点显示为 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')

所以我改为查询站点并使用预取,现在对于没有活动链接的任何站点都有一个空的电路集。是否有 Django 方法可以针对 conn_data 中的circuit_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'),
                                )
                        )

【问题讨论】:

    标签: django


    【解决方案1】:

    我认为这行不通。它是否应该工作值得商榷。让我们参考一下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 记录并分配计数。

    【讨论】:

    • 如果我得到了电路,那么这将排除没有任何 active_links 的 site_data。我需要确定一个站点的活动链接何时为零,但如果有意义,我不想在该站点有一个活动链接的情况下获得没有活动链接?
    • 任何不在该列表中但在完整 SiteData 列表中的站点的活动链接为零。
    • TypeError:无法在 .values() 或 .values_list() 之后调用 select_related()
    • 我没有测试那个 ORM,你可能需要把 values() 移到最后。如果你让它工作,请将工作线编辑成这个答案。
    【解决方案2】:

    好的,我得到了我想要的,这可能是一种更好的方法,但它的工作原理永远不会少:

    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'))
    

    【讨论】:

      猜你喜欢
      • 2019-12-30
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-04-03
      • 1970-01-01
      • 2021-10-17
      • 2018-02-06
      • 2018-08-26
      相关资源
      最近更新 更多