当我想要定义我的业务逻辑时,我正在努力找到正确的方法来实现这一点,因为我通常都需要一个属性和一个自定义查询集来获得相同的信息。最后,逻辑被复制
首先,在定义类之后,我自然开始为我需要的数据编写一个简单的属性:
class PickupTimeSlot(models.Model):
@property
def nb_bookings(self) -> int:
""" How many times this time slot is booked? """
return self.order_set.validated().count()
然后,我很快意识到,在处理queryset中的许多对象时调用此属性会导致重复查询,并会降低性能(即使使用预取,因为过滤会再次调用)。因此,我解决了编写带有注释的自定义查询集的问题:
class PickupTimeSlotQuerySet(query.QuerySet):
def add_nb_bookings_data(self):
return self.annotate(db_nb_bookings=Count('order', filter=Q(order__status=Order.VALIDATED)))
然后,我遇到了两个问题:
nb_bookings
是不起作用的。这迫使我在使用对象时考虑如何生成数据,调用正确的属性名(比如pickup_slot.nb_bookings
(属性)或pickup_slot.db_nb_bookings
(注释))这对我来说似乎设计得很糟糕,我很确定有办法做得更好。我需要一种总是使用相同的业务逻辑编写pickup_slot.nb_bookings
并有一个性能良好的答案的方法
我正在考虑完全删除该属性,只保留自定义查询集。然后,对于单个对象,将它们包装在QuerySet中,以便能够调用添加注释数据。比如:
pickup_slot = PickupTimeSlot.objects.add_nb_bookings_data().get(pk=pickup_slot.pk)
对我来说,这似乎很不自然。你觉得怎么样
TL;博士
是否需要过滤带注释的字段结果
我在这里看到的注释过程的唯一优点是数据的数据库级过滤功能
我已经进行了一些测试以得出结论,如下所示
环境
模型结构
为了简单和模拟,我遵循下面的模型表示
我已经用随机字符串填充了我的数据库
Reporter
和Article
模型测试用例
Reporter
实例并检索文章计数。我们通常在细节视图中执行此操作我正在使用} -(ipython doc)命令来计算执行时间
Ipython
shell的^{测试用例1
为此,我创建了这些函数,它们从数据库中随机选取实例
结果
测试用例2
我创建了另外两个函数
结果
测试用例3
毫无疑问,注释是唯一的方法
在这里您可以看到,与属性实现相比,注释过程花费了大量的时间
我不认为这里有什么灵丹妙药。但是我在我的项目中使用这种模式来处理这种情况
编码
通过这种方式,我可以始终使用属性,如果它是带注释查询集的一部分,它将使用带注释的值,如果不是,它将计算它。这种方法通过使用所需的值对queryset进行注释,保证我可以完全控制何时使其“更重”。如果我不需要这个,我就使用常规的
PickupTimeSlot.objects. ...
此外,若有许多这样的属性,那个么您可以编写一个decorator来包装属性并简化代码。它将作为
cached_property
装饰器工作,但如果存在,它将使用带注释的值为避免任何重复,一个选项可以是:
优点:计算出的属性透明地添加到任何查询集中;使用它无需采取进一步行动
缺点:即使未使用计算属性,也会产生计算开销
相关问题 更多 >
编程相关推荐