You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何基于递归ManyToManyField生成Django查询集?解决复合动词组件需求

问题描述

我开发了一个词典应用,单词(Lemma)可由其他单词组成,models.py中的模型定义如下:

class Lemma(models.Model):
    cf = models.CharField(max_length=200) #citation form
    pos = models.ForeignKey(Pos, on_delete=models.CASCADE) #part of speech
    components = models.ManyToManyField("self", symmetrical=False, blank=True) #component Lemma

需要返回两个查询集:

  1. 所有复合动词:存在components关联且pos.term为"verb"的Lemma;
  2. 复合动词的所有唯一名词组件:作为其他Lemma的component_set成员,且自身pos.term为"noun"的Lemma。

当前视图中,查询集1可以轻松实现,但查询集2的实现会得到普通列表,无法使用order_by()排序,views.py相关代码如下:

class CompVerb(generic.ListView):
    model = Lemma
    queryset = Lemma.objects.filter(
        Q(pos__term="verb") & ~Q(components=None)
    ).order_by("cf") #queryset 1, compound verbs

    def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs)

        nouns = self.queryset.values_list("components")
        nouns = set([Lemma.objects.get(pk=l[0]) for l in nouns])
        nouns= [l for l in nouns if l.pos.term == "noun"]
        context["nouns"] = nouns #queryset 2, nouns that are components of compound verbs

        return context 

希望找到更好的方法,让查询集2返回合法的查询集对象。

优化方案

可以通过Django ORM的关联查询直接构造查询集,避免手动遍历和转换,同时保证结果是支持排序的QuerySet对象:

步骤1:优化查询集1

原查询集1中的~Q(components=None)可以简化为components__isnull=False,语义更清晰:

queryset = Lemma.objects.filter(
    pos__term="verb",
    components__isnull=False
).order_by("cf")

步骤2:构造查询集2

利用多对多的反向关联component_set,结合in查询和distinct()去重,直接获取符合条件的名词组件查询集:

def get_context_data(self, **kwargs):
    context = super().get_context_data(**kwargs)
    # 获取复合动词查询集
    compound_verbs = self.queryset
    # 构造唯一名词组件查询集
    noun_components = Lemma.objects.filter(
        pos__term="noun",
        # 反向关联:当前Lemma是复合动词的components成员
        component_set__in=compound_verbs
    ).distinct().order_by("cf")
    context["nouns"] = noun_components
    return context

优势说明

  • 结果是合法的QuerySet对象,支持order_by()、切片等QuerySet操作;
  • 避免了原代码中的N+1查询(多次调用Lemma.objects.get),性能更优;
  • 利用distinct()自动去重,无需手动转集合;
  • 代码更简洁,符合Django ORM的最佳实践。

内容的提问来源于stack exchange,提问作者McBoatface et al.

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.29 09:46:23