过滤已由函数填充的自定义Django字段

时间:2018-05-23 09:33:51

标签: django django-models

我不知道这个的正确术语,所以我希望标题不会引起混淆。

我有以下型号。

func updateStatus(image: UIImage, error: Error?, context: UnsafeRawPointer) {
    // Your code here
}

这允许我查询class Material(models.Model): yes = models.IntegerField() no = models.IntegerField() def _votes(self): return int(self.no + self.yes) def _ratio(self): v = self.votes y = self.yes try: return float(y)/v except ZeroDivisionError: return float(0) ratio = property(_ratio) votes = property(_votes) 项并使用每个字段。

Material

到目前为止一切顺利。我想过滤过滤器的值。例如,如果比率大于0.8,我只想选择一个Material.objects.all()[0].yes # returns 5 Material.objects.all()[0].no # returns 3 Material.objects.all()[0].votes # returns 8 Material.objects.all()[0].ratio # returns 0.625 实例。

Material

然而,这样做会返回并错误声称Material.objects.filter(ratio__gt=0.8) # what I'd want to do 不是字段。

ratio

我该如何执行此查询?我假设我需要对模型进行一些更改,因此FieldError: Cannot resolve keyword 'ratio' into field. Choices are: id, no, yes ratio会被注册为实际字段。怎么办?

1 个答案:

答案 0 :(得分:3)

属性和列

属性在模型级别定义。 Python可以处理这些属性,你可以用它做很复杂的事情(例如执行HTTP请求)。数据库不知道有属性,也没有大多数数据库执行非常复杂的功能(通常数据库本身不执行非常复杂的任务)。

这意味着为了过滤,我们可以在之后检索所有值。但这当然是低效的:它意味着我们首先将所有项目加载到内存中,如果过滤器相当严格,我们将做很多工作,只丢掉大量的结果。

将属性翻译为F - 表达式

如果属性相当简单,我们可以写一个与之等效的数据库。例如,您的.vote属性实际上是:

fvotes = F('yes') + F('no')  # total number of votes

其中F(..)是Django用来引用列的对象。

如果对于我们的ratio,我们将始终排除没有votes的值(因为阈值高于零),那么我们可以编写我们的注释,如:

fratio = F('yes') / fvotes  # ratio of the votes

现在我们可以使用以下额外属性注释我们的数据库:

from django.db.models import F

fvotes = F('yes') + F('no')

Material.objects.annotate(
   votes=fvotes,
   fratio=F('yes') / fvotes
).filter(fratio__gt=0.8)

所以在这里我们基本上写了一些查询:

SELECT yes, no, yes + no AS votes, yes / (yes + no) AS ratio
FROM material
WHERE ratio > 0.8

因此,此查询在数据库级别执行,这通常比在Django级别手动执行过滤运行得更快。但如前所述,将Python函数转换为F - 表达式需要一些技巧。此外,一些函数可以不能转换为表达式。例如,大多数数据库无法访问文件系统,无法联系Web服务等。在这种情况下,您必须手动进行过滤。

手动过滤(通常效率较低)

如果您必须手动进行过滤(出于上述原因),我们可以使用Python的filter(..)函数。请注意,此过滤器会返回QuerySet,因此我们无法在其上执行其他.filter(..).first().annotate(..)等功能。在这种情况下,我们可以使用lambda表达式:

filter(lambda x: x.ratio > 0.8, Material.objects.all())

所以在这里我们将所有 Material个对象加载到内存中,让Python手动计算ratio并执行检查。