Skip to main content
 首页 » 编程设计

python之Django 性能 |在 Django 中更新模型后重新计算所有相关模型中的字段值

2024年09月03日32mq0036

我想了解在更新模型后在所有相关模型中重新计算模型字段值(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() 
 
 
  • 也许我应该使用 total_cost 作为我的 Product 模型类中的属性并使用它。但我不知道如何解决这个问题。
  • 如有任何帮助,我们将不胜感激。提前致谢

请您参考如下方法:

Maybe I should use total_cost as property in my Product model class and use it.

我认为这是个好主意,不要将此值也存储在数据库中。但不会减少计算成本的时间。也许你可以使用 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) 

基本上它会聚合来自数据库的值,因此您不必进行循环和手动计算。此外,它会减少数据库命中率,因此性能应该会提高。


基于此改进的答案 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