Почему Get и Filter дают разные результаты?(Джанго) - PullRequest
0 голосов
/ 18 мая 2018

Я занимаюсь разработкой приложения, в котором ученики могут оценивать своих учителей.У меня есть несколько моделей, но важными для этого вопроса являются следующие:

class Professor(models.Model):
    name = models.CharField(max_length=50,null=True)
    categories = models.ManyToManyField(Category, related_name='professors')
    def __str__(self):
        return self.name

class Student(models.Model):
    name = models.CharField(max_length=50,null=True)
    professors = models.ManyToManyField(Professor, related_name='students',through='Studentprofesor' )
    def __str__(self):
        return self.name

class Studentprofesor(models.Model):
    student = models.ForeignKey(Student, on_delete=models.CASCADE)
    professor =  models.ForeignKey(Professor, on_delete=models.CASCADE)
    tested = models.BooleanField(default=False)

Насколько я знал, основное различие между get и filter заключалось в том, что я не мог использовать getкогда было несколько объектов с функциями, которые я искал.Но кроме этого они работали аналогичным образом.get для одного объекта, filter для нескольких объектов.

Однако в этом случае я получаю разные результаты при запуске get и filter.

, если я используюget:

Student.objects.get(name="Mike").professors.all()

Я получаю:

<QuerySet [<Professor: Tom>, <Professor: Jenn>]>

Но если я использую filter:

Student.objects.filter(name="Mike").professors.all()

Я получаю:

AttributeError: 'QuerySet' object has no attribute 'professors'

как будто фильтр не может следовать многим многим отношениям между объектами.

Почему это происходит?

Ответы [ 2 ]

0 голосов
/ 18 мая 2018

Существуют огромные различия между .get(..) и .filter(..).Вкратце: .get(..) получает один экземпляр модели, который удовлетворяет заданным условиям, тогда как .filter(..) фильтрует набор запросов и создает набор запросов, который концептуально содержит экземпляр модели s (!), Который удовлетворяет заданным условиям.

Django's .get(..)

.get означает, что вы стремитесь получить ровно один экземпляр.Так что это означает, что если вы напишите:

Model.objects.get(..)

, результатом будет Model экземпляр (если есть такой экземпляр).В результате мы можем получить атрибуты (например, .professors и т. Д.) От этого единственного объекта.У нас есть гарантии, если вызов будет успешным, что: (1) не существует нескольких объектов, для которых выполняются критерии фильтра;и (2) существует по меньшей мере один элемент, для которого выполняются критерии фильтра.Таким образом, на выходе получается всегда экземпляр модели, а не None или QuerySet.

Функция .get(..) оценивается с нетерпением : мы немедленно выполняемзапрос к базе данных.Если база данных не возвращает записей или двух или более, исключения Model.DoesNotExist и MultipleObjectsReturned соответственно повышаются.

Примечание : поскольку .get(..) действует активно, добавление фильтров и т. Д. Справа от .get(..) не имеет смысла (хорошо, если вы не определили функцию filter наэтот случай, но это тоже не очень хорошая идея).Однако вы можете использовать такие функции, как .values(), values_list, prefetch_related и т. Д. С левой стороны, чтобы изменить тип вывода (и выполнить предварительную выборку определенных частей).Например:

Student.objects<b>.values()</b>.get(name='Mike')

приведет к появлению словаря, содержащего значения этого экземпляра.

Фильтр Django .filter(..)

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

Джанго не оценивает такие .filter(..) с нетерпением .Это означает, что по умолчанию Django не сделает запрос к базе данных для получения записей.Только если вы вызываете, например, len(..) для полученного набора запросов или выполняете итерацию по нему, Django сначала выполнит запрос к базе данных, а затем обработает соответствующий результат.

Поскольку результат .filter(..) является другимQuerySet, мы можем объединить операции вместе.Например, мы можем вызвать дополнительные .filter(..) или .exclude(..), values_list(..) или любую другую функцию, поддерживаемую QuerySet.

Поскольку результат не является экземпляром модели, мы не можем вызватьатрибуты экземпляра модели.Каким должен быть результат Student.objects.filter(..).name, если ни один студент не соответствует критериям?Или что, если существует несколько Student с, соответствующих данному ограничению?

Однако мы можем получить список Professor с, которые обучают одного или нескольких Student с именем 'Mike' с:

# professors with a student called Mike
Professors.objects.filter(students__name="Mike")

Фильтр никогда не вызовет исключение Model.DoesNotExist или MultipleObjectsReturned, посколькувполне допустимо работать с пустыми QuerySet с или QuerySet с несколькими элементами.

0 голосов
/ 18 мая 2018

Bacause filter() возвращает набор запросов (несколько студентов).Но professors является атрибутом одного экземпляра студента.Вы можете использовать first() с filter() для получения одного объекта:

Student.objects.filter(name="Mike").first().professors.all()
...