2017-12-26 18 views
1

이제 조건부 관련 쿼리와 함께 주석을 사용하는 복잡한 쿼리 세트를 작성하려고합니다.queryset에 다른 쿼리 세트를 주석으로 추가하는 방법

나는 다음과 같은 모델이 : 다음 직렬로

[ 
    { 
     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 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을 구축하는 것입니다

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이 표시됩니다. 내가 아는 한 내 목표는 사용자 지정 하위 쿼리를 사용하여 범주 쿼리 세트에 주석을 추가하는 것이고 내 문제는 하위 쿼리 작동 방법을 이해하지 못하거나 orm 쿼리를 작성하는 데 잘못된 툴킷을 사용한다는 것입니다. 그렇다면 orm 쿼리와이 serializer를 사용하여이 json을 어떻게 구축 할 수 있습니까?

UPD

현재 질의 당신은 단순히 때문에 어떻게 RDBMS의 작품으로, 하나 개의 쿼리와 직접 구조의 종류를 얻을 수

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") 
+1

꽤 복잡한 검색어입니다. 당신은 당신이 실제 SQL이 실행되는 것을 볼 수 있도록 그것을 출력 해 줍니까? – Jason

+0

예, 질문에 추가했습니다. –

+0

나는 최근에 비슷한 질문에 답했습니다. 이 유용한 체크 아웃을 찾을 수 있기를 바랍니다. https://stackoverflow.com/questions/47977653/how-can-i-get-the-custom-nested-data-in-dested-data-in-django/47977799?noredirect=1#comment82924834_47977799 – babygame0ver

답변

1

입니다. 그러나 가장 단순한 항목에서 많은 정보를 얻으면 큰 효과를 얻을 수 있으므로 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 
    }) 

물론이 방법을 사용하면 성능을 선호하지 않는 한 데이터베이스에 두 번 이상 도달하게됩니다. 이렇게하면 트릭을 수행하게됩니다.