【问题标题】:Should I use django Signals in this case?在这种情况下我应该使用 django Signals 吗?
【发布时间】:2020-03-06 16:18:03
【问题描述】:

我是一个初学者,我发现了很多东西,有时我对所有需要学习的信息和技巧感到不知所措。

我尝试为一家比萨店做一个 django 项目。人们可以在线订购披萨,订购方式很简单:您可以选择一个或多个披萨,还可以选择为您订购的每个披萨添加一些额外内容(添加奶酪、火腿或其他任何东西……),然后将其添加到购物车中。

我的问题是自动计算每个披萨的价格。

基本上这是我的模型文件:

class Pizza(models.Model):

    nom = models.CharField(max_length=30)
    ingrédients = models.CharField(max_length=500)
    prix = models.DecimalField(max_digits=4, decimal_places=2)

nom 表示名称

class Extra(models.Model):

    sup = models.CharField(max_length=30)
    prix = models.DecimalField(max_digits=3, decimal_places=2)

sup 是额外的名称。

class IndividualChoice(models.Model):

    pizzaChosen = models.ForeignKey(‘Pizza’, default="", null=True, on_delete=models.CASCADE)
    extraChosen = models.ManyToManyField(‘Extra’, blank=True)
    panier = models.ForeignKey(‘Cart’, default="", on_delete=models.CASCADE)
    calzone = models.BooleanField(default=False)
    prix = models.DecimalField(max_digits=5, decimal_places=2, default=0)

IndividualChoice 有点奇怪。这是一个存储每个选择的模型,这里的“panier”是指有模型的购物车,但我认为让它出现在这里没有用。

我对 django 信号有所了解,所以我尝试创建一个:

def prix_extra_calcul(sender, instance, action, *args, **kwargs):
    instance.prix = 0
    if action == “post_add” or action == “post_remove” or action == “post_clear”:
        for extra in instance.extraChosen.all():
            instance.prix += extra.prix
        instance.prix += instance.pizzaChosen.prix
        instance.save()

m2m_changed.connect(prix_extra_calcul, sender=IndividualChoice.extraChosen.through)

当我创建一个额外的披萨时效果很好,但如果只更改披萨,m2m 信号将不会被触发。 所以我试图找到另一种解决方案,但它很愚蠢:

def prix_pizza_calcul(sender, instance, *args, **kwargs):
    instance.prix = 0
    for extra in instance.extraChosen.all():
        instance.prix += extra.prix
    instance.prix += instance.pizzaChosen.prix

def prix_extra_calcul(sender, instance, action, *args, **kwargs):
    if action == “post_add” or action == “post_remove” or action == “post_clear”:
    # no need to write code because prix_pizza_calcul will be fired by instance.save()
        instance.save()

pre_save.connect(prix_pizza_calcul, sender=IndividualChoice)
m2m_changed.connect(prix_extra_calcul, sender=IndividualChoice.extraChosen.through)

当我想修改一个选项时,第二种解决方案“效果很好”,但是当我创建一个新的披萨时,我收到了这个错误消息“调用 Python 对象时超出了最大递归深度”。我认为这是因为我正在循环一些未保存的内容。

我完全被这个问题所困扰,我试图解决它好几天。我应该创建一个信号,信号是否适合这种问题?

(成为一名网络开发人员真是太棒了……)

感谢阅读!

【问题讨论】:

    标签: django django-models django-signals


    【解决方案1】:

    我不知道您对数据库规范化的了解,但这里以 IndividualChoice 上的价格显示,您是在复制数据:价格只是对已存储数据的计算。如果避免重复数据,就可以避免数据异常。

    如果你有名字和姓氏,你会存储“全名”吗?如果您的回答是肯定的,请在此处阅读有关@property 的文档:https://docs.djangoproject.com/en/3.0/topics/db/models/#model-methods

    解决方案可能是删除 IndividualChoice 模型上的 DecimalField prix 并添加如下内容:

        @property
        def prix(self):
            "Returns the price of the item"
            price = self.pizzaChosen.prix
            for extra in self.extraChosen.all():
                price += extra.prix
            return price 
    

    【讨论】:

    • 谢谢你的回答,我对数据库规范化知之甚少......我会看看属性,但我认为它不符合我的需要,因为我有另一个模型叫做购物车,我将在其中计算每个 IndividualChoice 价格的总和
    • 您可以在购物车上执行相同的操作(即添加一个属性price,它将计算相关IndividualChoice 的价格)。属性可以像存储字段一样使用,唯一的区别是您可能会在使用大量数据时失去性能,但如果通常少于 100 个比萨饼且每个购物车少于 100 个附加项,您应该没问题。
    • 有效!谢谢美人!有没有另一种方法可以在不损失性能的情况下做到这一点?我的意思是,就我而言,您的解决方案有效,但还有其他解决方案吗?把概念给我,我自己看
    • 你将失去的性能与 Django 延迟加载相关字段的事实有关,因此它会进行多次 SQL 查询来计算最终结果。如果您知道要访问相关字段,则减少查询数量的最佳方法是在查询 Cart 时使用 Prefetchprefetch_related。可以在此处找到带有比萨饼的法语示例makina-corpus.com/blog/metier/2015/…,但如果您搜索它们,您可以找到更多资源。
    【解决方案2】:

    也许你可以尝试使用@receiver 装饰器。 每次 post_save 都会发出一个信号。

    from django.dispatch import receiver
    
    @receiver(post_save, sender=IndividualChoice)
    def prix_extra_calcul(sender, instance, created, **kwargs):
        ...
    

    【讨论】:

    • 对不起,我需要更多的说明,我是个初学者
    猜你喜欢
    • 2011-01-17
    • 2022-07-22
    • 2013-05-16
    • 1970-01-01
    • 1970-01-01
    • 2020-05-15
    • 2010-09-05
    • 2017-05-09
    • 1970-01-01
    相关资源
    最近更新 更多