Django аннотировать набор запросов по полям от m2m до модели - PullRequest
1 голос
/ 04 августа 2020

У меня есть модели BottleType и OrganisationBottleType. И я хочу аннотировать набор запросов BottleType по полям is_accepted и points из OrganisationBottleType. queryset должен найти OrganisationBottleType, отфильтрованный по типу бутылки и organization, и аннотировать поля is_accepted и points.

(При обнаружении отфильтрованного OrganisationBottleType qs может просто взять первый объект из него, потому что предполагается, что пара fields: organization - bottle_type уникален)

Моя идея - использовать подзапрос в аннотации, но я не могу это сделать правильно.

Буду признателен за совет!

1 Ответ

1 голос
/ 13 августа 2020

Если я вас правильно понял, для каждого типа bottle вы хотите:

  • найти свои баллы
  • проверить, принято ли оно.

Существует два способа решения вашей проблемы:

  1. Получить OrganisationBottleType набор запросов и сопоставить OrganisationBottleType объекты с соответствующими объектами в BottleType в python с помощью .prefetch_related() и Prefetch() объект
  2. Аннотировать BottleType набор запросов с соответствующими объектами из OrganisationBottleType с помощью .annotate() и Subquery()

Оба варианта описаны ниже:

1. Используйте .prefetch_related с Prefetch объектом

Дано:

  • queryset - BottleType queryset
  • organisation_id - идентификатор необходимого организация

Решение:

queryset = queryset.prefetch_related(
    Prefetch(
        'organisation_bottle_types', 
        queryset= OrganisationBottleType.objects.filter(organisation_id=organisation_id)
    )
)

После этого вы можете получить желаемые данные таким образом:

for bottle_type in queryset:
    if bottle_type.organisation_bottle_types.all():
        related_object = bottle_type.organisation_bottle_types.all()[0]
        is_accepted = related_object.is_accepted
        points = related_object.points
    else:
        is_accepted = False
        points = None

2. Используйте SubQuery

Дано:

  • queryset - BottleType queryset
  • organisation_id - id необходимой организации

Решение:

 organisation_bottle_types = OrganisationBottleType.objects.filter(organisation_id=organisation_id, bottle_type=OuterRef('id'))
queryset = queryset.annotate(
    is_accepted=Subquery(organisation_bottle_types.values('is_accepted')[:1])
).annotate(
    points=Subquery(organisation_bottle_types.values('points')[:1], output_field=BooleanField())
)

После того, как вы сможете сделать:

for bottle_type in queryset:
    is_accepted = bottle_type.is_accepted
    points = bottle_type.points

Продолжить:

Лично я 'd go со вторыми параметрами, потому что он будет выполнять все сопоставления logi c на уровне базы данных, а не на уровне кода.

Первый вариант лучше, когда вам нужно сопоставить весь объект, а не всего несколько полей (например, points и is_accepted из вашего вопроса)

...