Как фильтровать модели 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.

Что я не знаю, так это комбинирование поиска полей и функций агрегации. Поэтому, учитывая приведенные выше модели, я хотел бы:

  1. Получите все объекты недвижимости, которые имеют не менее 3 помещений с именем 'Television'.
  2. Получите все объекты недвижимости, которые имеют по крайней мере 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)

вы можете сделать то же самое для второго запроса.

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