【发布时间】:2020-09-15 15:18:02
【问题描述】:
如何指定模型字段由数据库表达式而不是表列实现?
我有这个模型:
class URLProbe(models.Model):
url = models.CharField(max_length=MAX_URL_LENGTH)
timestamp = models.DateTimeField(auto_now_add=True)
status_code = models.SmallIntegerField(null=True, blank=True) # The HTTP status code
error = models.TextField(blank=True) # stores non-HTTP errors, e.g. connection failures
def ok(self):
return self.status_code == 200
ok.boolean = True
ok.admin_order_field = Case(
When(status_code__exact=200, then=True),
default=False,
output_field=BooleanField()
)
def errmsg(self):
if self.ok():
return ''
if self.status_code is not None:
return f'Error: HTTP status {self.status_code}'
return self.error
errmsg.admin_order_field = Case(
When(status_code__exact=200, then=Value('')),
When(status_code__isnull=False,
then=Concat(Value('Error: HTTP status '), 'status_code')),
default='error',
output_field=CharField()
)
模型表示尝试检索 url,记录 HTTP 调用是否成功或错误是什么。
这里的 ok 和 errmsg 字段是作为方法实现的,但我希望能够在 Django 管理员中将它们用作常规字段(对它们进行排序/过滤)并对它们进行排序和/或过滤在查询中。这是可能的,但我需要多次定义查询表达式:在admin_order_field 中、在 Django 管理自定义过滤器中以及在我想使用它们的查询中。因此,对于多个表达式以及查询表达式和 python 方法之间都重复了大量代码。
我想做的事
将字段定义为查询表达式,允许任何数据库操作发生而无需进一步配置。比如:
class URLProbe(models.Model):
...
ok = ExpressionField(Case(
When(status_code__exact=200, then=True),
default=False,
output_field=BooleanField()
))
errmsg = ExpressionField(Case(
When(ok__exact=True, then=Value('')),
When(status_code__isnull=False,
then=Concat(Value('Error: HTTP status '), 'status_code')),
default='error',
output_field=CharField()
))
但是我找不到像ExpressionField 这样的东西或做类似事情的方法。这存在吗?这可以在 Django 中实现吗?
更新:我发现了一些可以部分工作的东西。我可以使用包含ok 和errmsg 作为注释的默认查询集指定自定义管理器。这允许我在自定义查询中使用它们而无需复制表达式,但不幸的是管理员不接受它们作为字段并抛出 SystemCheckErrors。如果不是那个系统检查,我觉得它应该可以工作。
【问题讨论】:
标签: python django django-models django-queryset