如何使用另一个查询集注释查询集

问题描述 投票:1回答:1

现在我正在尝试构建使用带有条件相关查询的注释的复杂查询集。

我有以下型号:

class MenuItemCategory(CreateUpdateModel):
    name = models.CharField(max_length=255, blank=True, null=True)

class MenuItem(CreateUpdateModel):
    category = models.ForeignKey(MenuItemCategory, blank=True, null=True)
    name = models.CharField(max_length=255, blank=True, null=True)

class LineItem(models.Model):
    order = models.ForeignKey(Orders, blank=True, null=True)
    menu_item = models.ForeignKey(MenuItems, blank=True, null=True)
    price = models.DecimalField(max_digits=10, decimal_places=2)
    quantity = models.DecimalField(max_digits=10, decimal_places=3)
    amount = models.DecimalField(max_digits=10, decimal_places=2)

class Order(CreateUpdateModel):
    waiter = models.ForeignKey(Employees, blank=True, null=True)
    guests_count = models.IntegerField(blank=True, null=True, default=0)
    closed_at = models.DateTimeField(blank=True, null=True, db_index=True)                       

class Employees(CreateUpdateModel):
    restaurant = models.ForeignKey(Restaurants, blank=True, null=True)
    name = models.CharField(max_length=255, blank=True, null=True)

我的目标是使用以下方案构建json:

[
    {
        employee_name: 'Jane',    
        menu_item_categories: [
            {
                name: 'Drinks',
                line_items_quantity: 10, //times when this waiter brings any item from this category to the customer at the period
                amount: 49.00, // price of all drinks sold by this waiter at the period
                menu_items: [
                    name: 'Vodka',
                    amount: 1.00, 
                    line_items_quantity: 4, # times when this item has been ordered for this waiter at the period
                ]
            }
        ],
        visits: 618,
        guests: 813,
        cycle_time: 363
    }
]

使用以下序列化程序:

class EmployeeSerializer(serializers.ModelSerializer):
    name = serializers.CharField(max_length=255)
    visits = serializers.SerializerMethodField()
    guests = serializers.SerializerMethodField()
    cycle_time = serializers.SerializerMethodField()
    menu_item_categories = serializers.SerializerMethodField()

    def get_visits(self, obj):
        # works

    def get_guests(self, obj):
        # works

    def get_cycle_time(self, obj):
        # works

    def get_menu_item_categories(self, obj):
        qs = MenuItemCategories.objects.annotate(
            line_items_quantity=Count('menuitems__lineitems__order',
                                      filter=Q(
                                          menuitems__lineitems__order__closed_at__range=self.context.get('period'),
                                          menuitems__lineitems__order__waiter=obj)
                                      ),
            amount=Sum('menuitems__lineitems__amount',
                       filter=Q(
                           menuitems__lineitems__order__closed_at__range=self.context.get('period'),
                           menuitems__lineitems__order__waiter=obj)
                       ),
            menu_items=Subquery(
                MenuItems.objects.filter(
                    lineitems__order__closed_at__range=self.context.get('period'),
                    lineitems__order__waiter=obj
                ).annotate(amount=Sum('lineitems__amount', filter=Q(lineitems__order__closed_at__range=self.context.get('period'),
                                                                    lineitems__order__waiter=obj)))
            )
        )
        return MenuItemCategorySerializer(qs, many=True).data

但是当我尝试建立menu_item_categories值时 - 它给了我一个错误:subquery must return only one column。据我所知,我的目标是使用自定义子查询注释类别queryset,我的麻烦是我不明白子查询是如何工作的,或者我使用不正确的工具包来构建orm查询。那么,我如何使用orm查询和这个序列化器构建这个json?

UPD

当前查询是

SELECT 
  "menu_item_categories"."id", "menu_item_categories"."created_at",
  "menu_item_categories"."updated_at", "menu_item_categories"."restaurant_id", 
  "menu_item_categories"."name", "menu_item_categories"."is_active",
COUNT("line_items"."order_id") AS "line_items_quantity", 
  (SELECT 
      U0."id", U0."created_at", U0."updated_at", 
      U0."restaurant_id", U0."category_id", U0."name", 
      SUM(U1."amount") AS "amount"
  FROM "menu_items" 
  U0 INNER JOIN "line_items" U1 
  ON (U0."id" = U1."menu_item_id") 
  INNER JOIN "orders" U2 
  ON (U1."order_id" = U2."id") 
  WHERE (
      U2."waiter_id" = 5 AND U2."closed_at" 
      BETWEEN 2017-12-20 14:19:16+00:00 AND 2017-12-26 14:19:16+00:00)
      GROUP BY U0."id") 
  AS "menu_items", 
  SUM("line_items"."amount") AS "amount" 
  FROM "menu_item_categories" 
  LEFT OUTER JOIN "menu_items" 
  ON ("menu_item_categories"."id" = "menu_items"."category_id") 
  LEFT OUTER JOIN "line_items" 
  ON ("menu_items"."id" = "line_items"."menu_item_id") 
  GROUP BY "menu_item_categories"."id", 
  (
      SELECT 
          U0."id", U0."created_at", 
          U0."updated_at", U0."restaurant_id", 
          U0."category_id", U0."name", SUM(U1."amount"
  ) AS "amount" 
  FROM "menu_items" U0 
  INNER JOIN "line_items" U1 
  ON (U0."id" = U1."menu_item_id") 
  INNER JOIN "orders" U2 
  ON (U1."order_id" = U2."id") 
  WHERE (U2."waiter_id" = 5 
         AND U2."closed_at" 
         BETWEEN 2017-12-20 14:19:16+00:00 
         AND 2017-12-26 14:19:16+00:00) 
  GROUP BY U0."id")
django django-orm
1个回答
1
投票

您无法直接使用单个查询获得这种结构,这仅仅是因为RDBMS的工作方式。但是,您可以从最简单的项目中获得大量冗余信息的大结果,这样您就可以以编程方式对数据进行分组以生成json结构,或者您可以通过迭代查询集来一步完成:

t_range=self.context.get('period')
employees = Employees.objects.filter(order__closed_at__range=t_range) \
    .annotate(
        visits=Count(...),
        guests=Count(...),
        cycle_time=Sum(...),
    )

result = []

for employee in employees:
    menu_item_categories = MenuItemCategory.objects.filter(menuitem__lineitem__order__waiter=employee) \
        .annotate(
            line_items_quantity=Count(...),
            amount=Sum(...),
        )
    _cats = []
    for cat in menu_item_categories:
        menu_items = cat.menuitem_set.filter(order__waiter=employee) \
            .annotate(
                amount=Sum(...),
                line_items_quantity=Count(...),
            )
        _menu_items = []
        for menu_item in menu_items:
            _menu_item = {
                'name': menu_item.name,
                'amount': menu_item.amount,
                'line_items_quantity': menu_item.line_items_quantity,
            }
            _menu_items.append(_menu_item)

        _cats.append({
            'name': cat.name,
            'line_items_quantity': cat.line_items_quantity,
            'amount': cat.amount,
            'menu_items': _menu_items
        })

    result.append({
        'employee_name': employee.name,
        'visits': employee.visits,
        'guests': employee.guests,
        'cycle_time': employee.cycle_time,
        'menu_item_categories': _cats
    })

当然,这将不止一次打到数据库,所以除非你更喜欢这种方法的性能,否则这就行了。

© www.soinside.com 2019 - 2024. All rights reserved.