Django queryset filter с обратной ссылкой

Я разработчик C++ и новичок в python, просто изучаю учебники по django.
Я хочу знать, как фильтровать набор запросов по информации о его обратных ссылках.
Ниже приведены мои модели.

# models.py

import datetime
from django.db import models
from django.utils import timezone

class Question(models.Model):
  pub_date = models.DateTimeField('date published')

class Choice(models.Model): 
  question = models.ForeignKey(Question, on_delete=models.CASCADE)

Теперь я хочу получить набор запросов Question, который pub_date является прошедшим с настоящего момента И на который ссылается любой Choice. Второе утверждение вызывает мою проблему.
Ниже приведено то, что я пробовал.

# First method
question_queryset = Question.objects.filter(pub_date__lte=timezone.now())
for q in question_queryset.iterator():
  if Choice.objects.filter(question=q.pk).count() == 0: 
    print(q)
    # It works. Only @Question which is not referenced 
    # by any @Choice is printed. 
    # But how can I exclude @q in @question_queryset?
    
# Second method
question_queryset = Question.objects.filter(pub_date__lte=timezone.now()
  & Choice.objects.filter(question=pk).count()>0) # Not works.
# NameError: name 'pk' is not defined
# How can I use @pk as rvalue in @Question.objects.filter context?

Это потому, что я не знаком с синтаксисом Python? Или сам подход к данным неправильный? Есть ли у вас хорошие идеи для решения моей проблемы без изменения модели?

=======================================

edit: я только что нашел способ для первого метода.

# First method
question_queryset = Question.objects.filter(pub_date__lte=timezone.now())
for q in question_queryset.iterator():
  if Choice.objects.filter(question=q.pk).count() == 0: 
    question_queryset = question_queryset.exclude(pk=q.pk)

Возникает новая проблема: если количество строк @Question равно n, а @Choice - m, то вышеуказанный метод занимает O(n * m) раз, верно? Есть ли способ увеличить производительность? Может быть, проблема в моем способе работы с данными? Или проблема в структуре данных?

Это не то, как должны работать кверисеты. Итерация quueryset - это итерация всех данных в queryset, которые возвращает ваша база данных. Вам не нужно использовать iterate()

question_queryset = Question.objects.filter(pub_date=timezone.now())
for q in question_queryset:
    if Choice.objects.filter(question=q.pk).count() == 0: 
        print(q)

Я не проверял это. Но это должно работать.

question_queryset = Question.objects.filter(pub_date=timezone.now())
for q in question_queryset:
    if Choice.objects.filter(question=q.pk).count() == 0: 
        print(q)

Вот документация о том, как проследить отношения в обратном направлении. Следующий запрос дает тот же результат:

queryset = (Question.objects
                    .filter(pub_date__lte=timezone.now())
                    .annotate(num_choices=Count('choice')) 
                    .filter(num_choices__gt=0))

Вероятно, лучше полагаться на Django ORM, чем писать свой собственный фильтр. Я считаю, что в лучшем сценарии временная сложность будет одинаковой.

В связи с дизайном, этот вид отношений приведет к дублированию в вашей базе данных, разные вопросы иногда имеют один и тот же ответ. Я бы, вероятно, выбрал отношения "многие ко многим".

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