Как создать Django queryset на основе значений рекурсивного поля ManyToManyField?

У меня есть приложение-словарь, в котором слова (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. Все составные глаголы: Lemma, которые имеют значения в self.components и где self.pos.term="verb"
  2. Все уникальные компоненты составных глаголов, которые являются существительными: Леммы, которые являются значениями self.component_set() некоторой другой леммы и которые сами имеют self.pos.term="noun".

Я хочу использовать представления для передачи этих двух списков в шаблон.

Я довольно легко справляюсь с кверисетом 1, но все мои решения для кверисета 2 довольно запутанны. Соответствующий класс в моем 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 

Это также оставляет меня с обычным списком для моей переменной verbs, а не с правильным набором queryset, где я могу использовать метод .order_by() для сортировки этого списка в алфавитном порядке по форме цитирования.

Есть ли лучший подход, который вернет объект queryset?

Вы можете сделать запрос в обратном направлении:

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

    def get_context_data(self, **kwargs):
        return super().get_context_data(
            **kwargs,
            nouns=Lemma.objects.filter(
                lemma__in=self.queryset, pos__term='noun'
            ).order_by('cf')
        )

Однако ваша queryset будет дублировать Lemma столько раз, сколько существует связанных components, поэтому вы можете использовать .distinct() [Django-doc], чтобы предотвратить это.


Примечание: В Django представления, основанные на классах (CBV), часто имеют суффикс …View, чтобы избежать столкновения с именами моделей. Поэтому вы можете переименовать класс представления в CompVerbView, вместо CompVerb.

Вернуться на верх