【问题标题】:Convert the value of a field in a django RawQueryset to a different django field type将 django RawQueryset 中的字段值转换为不同的 django 字段类型
【发布时间】:2018-05-01 18:51:40
【问题描述】:

我有一个相当复杂的查询,它生成了一个 Django RawQuerySet。此特定查询返回一些不属于RawQuerySet 所基于的模型的字段,因此我使用.annotate(field_name=models.Value('field_name')) 将其作为属性附加到RawQuerySet 中的各个记录。最重要的自定义字段实际上是 uuid,我使用 Django 的 {% url %} 功能来编写 URL。

问题是:我没有在我的应用程序中使用标准的uuids,我使用的是SmallUUIDs(压缩的UUID。)它们作为本机uuidfields 存储在数据库中,然后转换为更短的字符串在蟒蛇。所以我需要以某种方式将作为RawQuerySet 的一部分返回的uuid 转换为SmallUUID,以便在模板中使用以生成URL。

我的代码看起来有点像这样:

query = "SELECT othertable.uuid_field as my_uuid FROM myapp_mymodel
         JOIN othertable o ON myapp_mymodel.x = othertable.x"

MyModel.objects.annotate(
    my_uuid=models.Value('my_uuid'),
).raw(query)

现在这里有一个合乎逻辑的解决方案,models.Value 有一个可选的 kwarg,称为 output_field,使代码如下所示:

MyModel.objects.annotate(
    my_uuid=models.Value('my_uuid', output_field=SmallUUIDField()),
).raw(query)

但它不起作用!该 kwarg 被完全忽略,属性的类型基于从数据库返回的类型,而不是 output_field 中的内容。在我的例子中,我得到一个 uuid 输出,因为 Postgres 正在返回一个 UUID 类型,但是如果我要将查询更改为 SELECT cast othertable.uuid_field as text) as my_uuid 我会以字符串的格式获得属性。在这种情况下,Django(至少版本 1.11.12)似乎并不真正关心 kwarg 中的内容。

以下是我认为的潜在解决方案,排名不分先后:

  1. 以某种方式更改查询的格式(在 Django 或 SQL 中)
  2. 在将生成的 RawQuerySet 传递给视图之前以某种方式更改它
  3. 更改模板中的某些内容以将 UUID 转换为 smalluuid,以便在 URL 反向过程中使用。

接下来我最好的步骤是什么?

【问题讨论】:

  • 由于 SmallUUIDField 不是原生 Django 模型字段类型,您确定它与您要执行的操作兼容吗?如果你想要文本输出,为什么不直接使用TextField
  • 实际上,在这种情况下,output_field 甚至会忽略本机模型字段类型。您可以将output_field 设置为IntField,如果它是来自数据库的字符串,它仍会显示为字符串。事实上,它甚至没有显示为标准的 django“字段”,它只是显示为一个字符串(或者在我的情况下为 uuid。)它确实只是返回数据库返回的内容。我需要 UUID 的缩短版本,这是该字段的自定义功能。

标签: django django-models django-queryset django-postgresql


【解决方案1】:

您当前的方法存在几个问题:

  1. Value() 没有做你认为的那样 - 你的注释实际上只是用值“my_uuid”注释每一行,因为这是你传递给它的。它不会查找该名称的字段(为此,您需要使用 F 表达式)。

  2. 以上第 1 点无论如何都无关紧要,因为只要您使用 raw(),注释就会被忽略 - 这就是为什么您看不到它的任何效果。

底线是尝试注释RawQuerySet 并不容易。它接受了一个 translations 参数,但我想不出一种方法来让它与您正在使用的连接类型一起使用。

我能想到的下一个最佳建议是,您只需在需要时手动将字段转换为 SmallUUID 对象 - 如下所示:

from smalluuid import SmallUUID

objects = MyModel.objects.raw(query)

for o in objects:
    # Take the hex string obtained from the database and convert it to a SmallUUID object.
    # If your database has a built-in UUID type you will need to do 
    # SmallUUID(small=o.my_uuid) instead.
    my_uuid = SmallUUID(hex=o.my_uuid)

(我在循环中执行此操作只是为了说明 - 根据您需要的位置,您可以在模板标签或视图中执行此操作)。

【讨论】:

  • 谢谢!原始查询忽略注释这一事实并没有特别好的文档记录(事实上,与 RawQuerySet 有很多关系的文档也没有特别好的文档。)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-09-13
  • 2012-12-10
  • 1970-01-01
  • 2014-12-29
  • 2011-06-13
  • 2015-03-25
相关资源
最近更新 更多