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, чем писать свой собственный фильтр. Я считаю, что в лучшем сценарии временная сложность будет одинаковой.
В связи с дизайном, этот вид отношений приведет к дублированию в вашей базе данных, разные вопросы иногда имеют один и тот же ответ. Я бы, вероятно, выбрал отношения "многие ко многим".