2017-12-26 4 views
1

Jetzt versuche ich, komplexen queryset zu erstellen, die Anmerkungen mit bedingten verwandten Abfragen verwendet.Wie mit Anmerkungen versehen queryset mit einem anderen queryset

Ich habe folgende Modelle:

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) 

Mein Ziel ist es json mit folgendem Schema aufbauen:

[ 
    { 
     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 
    } 
] 

Mit folgenden Serializer:

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 

Aber wenn ich versuche, Build menu_item_categories Wert - es gibt mir einen Fehler: subquery must return only one column. Nach meinem Verständnis besteht mein Ziel darin, Kategorien von Abfragegruppen mit benutzerdefinierten Unterabfragen zu versehen, und mein Problem ist, dass ich nicht verstehe, wie Unterabfragen funktionieren, oder ob ich falsche Toolkits zum Erstellen von Abfragen verwende. Also, wie kann ich dieses json mit Orm-Abfrage und diesem Serializer erstellen?

UPD

aktuelle Abfrage ist

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

, die eine ziemlich komplexe Abfrage ist. hast du es ausgedruckt, damit du sehen kannst, wie der eigentliche sql ausgeführt wird? – Jason

+0

ja, ich habe es zu der Frage hinzugefügt –

+0

Ich habe vor kurzem die ähnliche Frage beantwortet. Hoffe, du wirst das nützlich finden, sieh dir das an. https://stackoverflow.com/questions/47977653/how-can-i-get-the-custom-nested-data-in-django/47977799?noredirect=1#comment82924834_47977799 – babygame0ver

Antwort

1

Sie nicht diese Art von Struktur direkt mit einer einzigen Abfrage bekommen, nur weil, wie die Arbeit des RDBMS. Sie können jedoch ein großes Ergebnis mit vielen redundanten Informationen erhalten, den ganzen Weg von den einfachsten Elemente so können Sie Gruppendaten programmatisch Ihre json Struktur zu erzeugen, oder Sie können nur tun, dass in einem Schritt durch Ihre querysets iterieren:

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

Sicher, dies wird die Datenbank mehr als einmal getroffen, so dass, wenn Sie Leistung gegenüber diesem Ansatz bevorzugen, dies den Trick tun wird.

Verwandte Themen