Как фильтровать модели Django с помощью поиска по полям, охватывающим отношения и агрегатные функции?
У меня есть набор из четырех моделей, представляющих арендную недвижимость.
В модели Property хранится имя свойства и кто его создал.
class Property(models.Model):
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
creator = models.ForeignKey(User, related_name='creator', on_delete=models.PROTECT)
name = models.CharField(max_length=100)
А Property может иметь множество экземпляров Area через отношение ForeignKey. У Area есть AreaType через отношение ForeignKey, а также множество Amenities через отношение ManyToManyField.
class Area(models.Model):
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
type = models.ForeignKey(AreaType, on_delete=models.PROTECT)
property = models.ForeignKey(Property, on_delete=models.PROTECT)
amenities = models.ManyToManyField(Amenity, null=True, blank=True)
class AreaType(models.Model):
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
name = models.CharField(max_length=100)
class Amenity(models.Model):
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
name = models.CharField(max_length=100)
Я знаком с поиском полей, которые охватывают и не охватывают отношения, а также с фильтрацией через kwargs.
Что я не знаю, так это комбинирование поиска полей и функций агрегации. Поэтому, учитывая приведенные выше модели, я хотел бы:
- Получите все объекты недвижимости, которые имеют не менее 3 помещений с именем 'Television'.
- Получите все объекты недвижимости, которые имеют по крайней мере 2 зоны с именем AreaType 'Bathroom'.
Чтобы получить все объекты с удобствами с названием Television:
Большое спасибо @Waldemar Podsiadło, я неправильно понял вопрос. Он предоставил правильный ответ для подсчета телевизоров.
Property.objects.filter(area_set__amenities__name="Television")\
.annotate(tv_count=models.Count('area_set__amenities')\
.filter(tv_count__gte=3))
Нужно получить все свойства, относящиеся к удобствам с названием Television.
То же самое можно сделать с областями, но давайте воспользуемся коротким путем:
Property.objects.filter(
area_set__type__name="Bathroom",
area_set__type__count__gte=2
)
Вы не определили related_name
, поэтому обратная область будет area_set
.
С этого момента свойства доступны вам как поля, поэтому вы можете просто продолжить фильтрацию на их основе.
Для доступа к полю в запросе отделите его двойным подчеркиванием __
, это также работает с выражениями, такими как __count
и __sum
.
Ссылка на django-docs, чтобы узнать больше о кверисете и фильтрации.
Ссылка на django-docs для получения дополнительной информации о фильтрации.
@nigel239 Я думаю, что вам не нужно использовать _set в фильтрации обратных отношений, вы просто используете имя модели. Кроме того, вы можете сделать больше с помощью Django ORM. Для первого вопроса это выглядит так:
Property.objects.filter(area__amenities__name="Television")
.annotate(tv_ocurance=Count('area__amenities', filter=Q(area__amenities__name="Television")))
.filter(tv_ocurance__gte=3)
вы можете сделать то же самое для второго запроса.