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

时间:2018-05-01 18:51:41

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

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

问题在于:我没有在我的应用程序中使用标准uuid,我使用SmallUUIDs(压缩的UUID)。这些都存储在数据库作为本机uuidfield然后在python中转换为更短的字符串。所以我需要以某种方式将uuid作为RawQuerySet的一部分返回到SmallUUID,以便在模板中使用以生成网址。

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

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反向过程中使用。
  4. 我最近的下一步是什么?

1 个答案:

答案 0 :(得分:1)

您目前的方法存在两个问题:

  1. Value()没有按照您的想法行事 - 您的注释实际上只是用值“my_uuid”注释每一行,因为这是您传递给它的内容。它没有查找该名称的字段(要做到这一点,您需要使用F表达式)。

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

  3. 底线是试图注释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)
    

    (我在循环中这样做只是为了说明 - 根据你需要的地方,你可以在模板标签或视图中做到这一点。)