我想了解在更新模型后在所有相关模型中重新计算模型字段值(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
