【问题标题】:Get amount of all used ingredients in ordered dishes (Django ORM) to make a shopping list获取订购菜肴(Django ORM)中所有使用成分的数量以制作购物清单
【发布时间】:2021-09-12 23:54:40
【问题描述】:

我有菜肴(食谱)、其中的配料、配料价格、上菜日期、该日期这道菜的订单(全、双、半)。 所以我需要结合格式的成分购物清单: 土豆 5个 柠檬3个

表格:

class IngredientType(models.Model):
    name = models.CharField(max_length=100, verbose_name="Тип ингредиента")
    picture = models.ImageField(verbose_name="Изображение типа ингредиента")

    def __str__(self):
        return f"Тип ингредиента: {self.name}"


class Supplier(models.Model):
    name = models.CharField(max_length=100, verbose_name="Имя поставщика")
    picture = models.ImageField(verbose_name="Изображение поставщика")
    description = models.TextField(verbose_name="Описание поставщика")
    phone = models.CharField(max_length=100, verbose_name="Телефон поставщика")
    site = models.CharField(max_length=100, verbose_name="Сайт поставщика")
    address = models.CharField(max_length=200, verbose_name="Адрес поставщика")

    def __str__(self):
        return f"Поставщик {self.name}"


class Ingredient(models.Model):
    """Ingredient model"""

    name = models.CharField(max_length=255, verbose_name="Имя ингредиента", unique=True)
    measure = models.CharField(max_length=30, verbose_name="Размерность")
    price = models.DecimalField(
        decimal_places=2,
        verbose_name="Цена ингредиента",
        max_digits=10,
        default=0,
    )
    type = models.ForeignKey(
        IngredientType,
        on_delete=models.SET_DEFAULT,
        default=None,
        verbose_name="Тип ингредиента",
        related_name="ingredients",
    )
    supplier = models.ForeignKey(
        Supplier,
        on_delete=models.SET_DEFAULT,
        default=None,
        verbose_name="Поставщик",
        related_name="ingredients",
    )

    def __str__(self):
        return f"Ингредиент: {self.name} ({self.measure})"


class DishType(models.Model):
    name = models.CharField(max_length=100, verbose_name="Тип блюда")
    picture = models.ImageField(verbose_name="Изображение типа блюда")

    def __str__(self):
        return f"Тип блюда {self.name}"


class Dish(models.Model):
    name = models.CharField(
        default="New Dish", verbose_name="Имя блюда", max_length=100
    )
    picture = models.ImageField(verbose_name="Изображение блюда")
    description = models.TextField(verbose_name="Описание блюда")
    price = models.DecimalField(
        decimal_places=2, verbose_name="Цена блюда", max_digits=10
    )
    date_created = models.DateTimeField(auto_now_add=True)
    type = models.ForeignKey(
        DishType,
        on_delete=models.SET_DEFAULT,
        default=None,
        verbose_name="Тип блюда",
        related_name="dishes",
    )
    ingredients = models.ManyToManyField(
        Ingredient, related_name="dishes", through="IngredientAmount"
    )

    def __str__(self):
        return f"{self.name} по цене {self.price}"


class IngredientAmount(models.Model):
    """Support model for Ingredient&Recipe ManyToMany relation"""

    dish = models.ForeignKey(
        Dish, on_delete=models.CASCADE, related_name="amounts", verbose_name="Dish"
    )
    ingredient = models.ForeignKey(
        Ingredient,
        on_delete=models.CASCADE,
        related_name="amounts",
        verbose_name="Ingredient",
    )
    amount = models.DecimalField(
        max_digits=5, decimal_places=1, verbose_name="Сколько штук"
    )

    def __str__(self):
        return f"Количество: {self.ingredient.name} by {self.amount}"


class DishDateLink(models.Model):
    dish = models.ForeignKey(
        Dish, on_delete=models.SET_DEFAULT, default=None, related_name="dish_date_links"
    )
    date = models.DateField(verbose_name="Дата планируемой подачи блюда")
    is_ready = models.BooleanField(default=False)

    def __str__(self):
        return f"{self.dish.name} запланировано на {self.date}. Готовность: {self.is_ready}"


class Transaction(models.Model):
    dish_date_link = models.ForeignKey(
        DishDateLink,
        on_delete=models.SET_DEFAULT,
        default=None,
        verbose_name="Дата и блюдо",
        related_name="transactions",
    )
    amount = models.DecimalField(
        default=0, decimal_places=2, verbose_name="Сумма транзакции", max_digits=19
    )
    serving = models.DecimalField(
        default=1,
        decimal_places=2,
        verbose_name="Размер порции (0.5, 1, 2)",
        max_digits=19,
    )
    user = models.ForeignKey(
        User, on_delete=models.CASCADE, related_name="transactions"
    )
    date_created = models.DateTimeField(auto_now_add=True)

    def save(self, *args, **kwargs):
        self.user.cash -= self.amount
        self.user.save()
        super(Transaction, self).save(*args, **kwargs)

    def delete(self, *args, **kwargs):
        self.user.cash += self.amount
        self.user.save()
        super(Transaction, self).delete(*args, **kwargs)

    def __str__(self):
        return f"{self.user.get_full_name()} заказал {self.dish_date_link.dish.name} на {self.amount}"

我做了这个查询:

    queryset = Ingredient.objects.values("name").annotate(
        sum=F("amounts__amount") * Sum("dishes__dish_date_links__transactions__serving") * F("price")
    ).filter(sum__gt=0).order_by('-sum')

计算正确,但它不会对成分本身进行分组,它们是重复的。 请帮我把它们结合起来。 现在输出:

GET /api/v1/ingredient-sums/
HTTP 200 OK
Allow: GET, POST, HEAD, OPTIONS
Content-Type: application/json
Vary: Accept

[
    {
        "name": "Potato",
        "sum": "600.00"
    },
    {
        "name": "Potato",
        "sum": "500.00"
    },
    {
        "name": "Rice",
        "sum": "90.00"
    }
]

【问题讨论】:

  • 我想你一定已经尝试过.distinct,但它会引发错误。我记得在一个项目中遇到了同样的问题,所以我所做的就是将distinct('id') 放入查询中,然后稍后使用普通 python 对结果进行排序。
  • 我试过queryset = Ingredient.objects.values("name").annotate( sum=Sum("amounts__amount", distinct=True) * Sum("dishes__dish_date_links__transactions__serving") * F("price") ).filter(sum__gt=0).order_by('-sum'),但输出总和不正确[ { "name": "Potato", "sum": "2800.00" }, { "name": "Rice", "sum": "90.00" } ]
  • 你能不能试着把 distinct('id') 放在这里而不是 order_by。但它只有在你不使用 SQLite 时才有效
  • annotate() + distinct(fields) 没有实现。

标签: python django postgresql orm


【解决方案1】:

将 F 放入 Sum 中

    queryset = (
        Ingredient.objects.values("name", "measure", "supplier__name", "price")
        .annotate(
            sum=Sum(F("amounts__amount") * F("dishes__dish_date_links__transactions__serving"))
        )
        .filter(sum__gt=0)
        .order_by("-sum")
    )

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2020-03-08
    • 1970-01-01
    • 2021-02-05
    • 2021-05-11
    • 1970-01-01
    • 2018-04-07
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多