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