将 django RawQueryset 中的字段值转换为不同的 django 字段类型

Convert the value of a field in a django RawQueryset to a different django field type

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

问题是:我没有在我的应用程序中使用标准 uuid,我使用的是 SmallUUIDs(压缩的 UUID。)它们作为原生 [=19] 存储在数据库中=]s 然后在 python 中转换为较短的字符串。所以我需要以某种方式将作为 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 反向过程中使用。

我最好的后续步骤是什么?

您当前方法的几个问题:

  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)

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