【问题标题】:Django Performance | Recalculating a field value in all related models after updating a model in DjangoDjango 性能 |在Django中更新模型后重新计算所有相关模型中的字段值
【发布时间】:2019-12-27 10:46:09
【问题描述】:

我想了解在更新模型后,在所有相关模型中重新计算模型字段值(total_cost 在我的Product 模型类中)的最佳方法是什么。我写了一段代码,它可以工作,但需要很长时间。我怎样才能加快我的代码?这是模型类

#models.py
class Product(..):
    total_cost = models.FloatField(...)
    materials = models.ManyToManyField(Material, through="MaterialProduct")


    def calculate_cost(self):
        return round(calc_material_price(self.materialproduct_set.all()),2)

    def calc_material_price(self, mylist):
        res = 0
        for m in mylist:
            res += m.material.price * m.material_rate
        return res

class Material(..):
    price = models.FloatField(...)

class MaterialProduct(..):
    product = models.ForeignKey(Product, ..)
    material = models.ForeignKey(Material, ..)
    material_rate = models.FloatField()

  • 还有我的序列化程序示例
class UpdateProductMaterialSerializer(..):
    #...
    def update(self, instance, validated_data):
        #in front-end total cost calculated and I assign it here directly
        instance.total_cost = self.initial_data.get("total_cost")
        instance.save()
        mylist_data = self.initial_data.get("mylist")
        material_list_for_update = []
        for item in mylist_data:
            material = item.get("material")
            material_instance = Material.objects.get(pk=material["id"])
            # I use this list for updating all Products
            material_list_for_update.append(material_instance)

            material_rate = item.get("material_rate")
            mp = MaterialProduct.objects.filter(product=instance, material=material_instance).first()
            mp.material_rate = material_rate
            mp.save()# here my one product is updated successfully


            # TO UPDATE ALL PRODUCTS I call below method
            # But this takes 15 seconds
            update_all_product_by_sent_materials(material_list_for_update)
        return instance

    def update_all_product_by_sent_materials(self, material_list):
        for material in material_list:
            for mp in MaterialProduct.objects.filter(material=material):
                mp.material.total_cost = mp.material.calculate_cost()
                mp.material.save()


  • 也许我应该在我的 Product 模型类中使用 total_cost 作为属性并使用它。但我不知道如何解决这个问题。
  • 任何帮助将不胜感激。提前致谢

【问题讨论】:

    标签: python django django-models django-rest-framework django-queryset


    【解决方案1】:

    也许我应该在我的 Product 模型类中使用 total_cost 作为属性并使用它。

    我认为这是一个好主意,并且不要将此值也存储在 DB 中。但这不会减少计算成本的时间。也许你可以像这样使用aggregation 来减少时间:

    from django.db.models import Sum, F
    ....
    
    @property
    def calculate_cost(self):
        cost = self.materialproduct_set.aggregate(cost=Sum(F('material__price') * F('material_rate')))['cost']
        return round(cost, 2)
    

    基本上,它会汇总数据库中的值,因此您不必进行循环并手动计算。此外,它会减少 DB 命中,因此性能应该会提高。


    基于此SO Answer的改进答案:

    from django.db.models import F, Func, Sum
    
    class Round(Func):
        function = 'ROUND'
        template='%(function)s(%(expressions)s, 2)'
    
    # rest of the code
    
    @property
    def calculate_cost(self):
        cost = self.materialproduct_set.aggregate(cost=Round(Sum(F('material__price') * F('material_rate'))))['cost']
        return cost
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2015-09-14
      • 2017-12-28
      • 1970-01-01
      • 1970-01-01
      • 2020-08-08
      • 2021-02-07
      • 2018-06-19
      • 2013-03-20
      相关资源
      最近更新 更多