【问题标题】:Specify a Django model field as a query expression将 Django 模型字段指定为查询表达式
【发布时间】: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 调用是否成功或错误是什么。

这里的 okerrmsg 字段是作为方法实现的,但我希望能够在 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 中实现吗?

更新:我发现了一些可以部分工作的东西。我可以使用包含okerrmsg 作为注释的默认查询集指定自定义管理器。这允许我在自定义查询中使用它们而无需复制表达式,但不幸的是管理员不接受它们作为字段并抛出 SystemCheckErrors。如果不是那个系统检查,我觉得它应该可以工作。

【问题讨论】:

    标签: python django django-models django-queryset


    【解决方案1】:

    据我所知,使用基本 Django ORM 无法按属性排序,因为这些操作是在 SQL 级别完成的,并且该字段未存储在数据库中。

    有一个非规范化模型的项目,它基本上为您计算并存储这些计算值,这可能会满足您的目的: https://github.com/django-denorm/django-denorm

    所以你会像这样使用它:

        @denormalized(models.CharField, max_length=100)
        def errmsg(self):
           if self.ok():
                return ''
           if self.status_code is not None:
                return f'Error: HTTP status {self.status_code}'
    

    【讨论】:

    • 如果你.annotate()一个带有表达式的查询集来计算数据库中的属性,你可以对这些类型的计算属性进行排序。 denormalized 是解决它的一种方法,但它重复了我希望在这种情况下避免的数据。
    • 是的,我没有在回答中指定它,但我同意你的观点,这不是最佳解决方案,我也更喜欢使用你的方法。我刚刚向 OP 回答了一种能够在模型级别定义它的方法,因为他只想定义一次
    猜你喜欢
    • 2017-09-24
    • 1970-01-01
    • 2015-01-12
    • 2021-04-30
    • 2022-01-19
    • 2012-08-19
    • 1970-01-01
    • 2015-02-19
    • 1970-01-01
    相关资源
    最近更新 更多