2017-12-26 13 views
1

今、私は、条件付きの関連クエリで注釈を使用する複雑なクエリーセットを作成しようとしています。クエリーセットに別のクエリーセットで注釈を付ける方法

私は次のモデルがあります:以下のシリアライザで

[ 
    { 
     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のクエリとこのシリアライザでこのjsonを構築できますか?

UPD

現在のクエリはあなたがいうだけの理由でどのRDBMSの仕事のため、1つのクエリで直接構造のようなものを得ることができない

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構造を生成することができます。または、クエリーセットを反復することで1ステップで行うことができます。

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 
    }) 

確かに、これはデータベースに1回以上ヒットします。このアプローチよりもパフォーマンスが良い場合を除き、これはトリックを行います。

関連する問題