Аннотируйте набор запросов Django, Используя ‘related_name’ Во многих отношениях (БЕЗ ‘Count’, ‘Avg’, ‘Max’, …)

#python-3.x #django #django-rest-framework #annotations #many-to-many

Вопрос:

У меня в коде есть модели, похожие на следующие:

 class CompanyProject(models.Model):  """ This class holds project related information read in from the 'project'  custom 'manage.py' command.  """   project_number = models.IntegerField(blank=False, null=False, unique=True)  project_worktype = models.CharField(blank=True, max_length=255, null=True)  created_at = models.DateTimeField(auto_now_add=True, blank=False, null=False)  updated_at = models.DateTimeField(auto_now=True, blank=False, null=False)  last_seen = models.DateField(blank=False, null=False)    def get_project_subtypes(self):  subtypes = self.project_subtype.all()  return [ subtype.project_subtype for subtype in subtypes ]   class Meta:  ordering = ['project_number']  class CompanySubType(models.Model):  class CompanySubTypeChoices(models.TextChoices):  G1A = '1A', _('1A')  G1B = '1B', _('1B')  G2A = '2A', _('2A')  G2B = '2B', _('2B')  G3A = '3A', _('3A')  G3B = '3B', _('3B')   company_project = models.ManyToManyField(CompanyProject, related_name='project_subtype')  project_subtype = models.CharField(blank=False, choices=CompanySubTypeChoices.choices, max_length=2, null=False)   class ListEntry(models.Model):   list_project = models.OneToOneField(CompanyProject, on_delete=models.CASCADE, related_name='list_project')  list_reviewer = models.ForeignKey('auth.User', on_delete=models.CASCADE, related_name=' ')   

Я хотел бы вернуть набор аннотированных ListEntry объектов, снабженных списком ВСЕХ подтипов проекта, идентифицированных с ListEntry проектом.

В конце концов, мне нужно будет передать эти аннотированные данные сериализатору Django REST Framework, но сначала мне нужно, чтобы аннотации работали так, как я этого хочу.

Моя проблема в том, что я прекрасно могу комментировать, делая что-то подобное:

 list_entry_qs = ListEntry.objects.prefetch_related('list_project', 'list_reviewer').annotate(subtypes=F('list_pmatt__project_subtype__project_subtype')).all()  

и это работает просто отлично. Проблема, с которой я сталкиваюсь, заключается в том, что набор запросов, возвращаемый этой командой, дублирует list_entry объект, если у него более одной subtype аннотации.

Например, если проект под номером 1234 имеет два подтипа проекта, » 1A » и «3A», я получаю два list_entry объекта: один с subtype аннотацией » 1A «и отдельный list_entry объект для того же проекта с subtype аннотацией «3A».

Я хотел бы, чтобы для проекта 1234 был возвращен ОДИН объект subtype с аннотацией «1A», «2A», который в конечном итоге будет сериализован в объект JSON.

Должен быть простой способ сделать это?

Ответ №1:

Хорошо, потратив на это буквально 8 часов, я, наконец, узнал, как это сделать:

 from django.contrib.postgres.aggregates.general import ArrayAgg  q = ListEntry.objects.prefetch_related('list_pmatt', 'list_reviewer', 'list_pmatt__project_subtype').annotate(my_subtype=ArrayAgg('list_pmatt__project_subtype__project_subtype'))  gt;gt;gt; q[2].my_subtype ['1A', '3A']