如何在 Django 中向模型添加临时字段?

Posted

技术标签:

【中文标题】如何在 Django 中向模型添加临时字段?【英文标题】:How do I add a temporary field to a model in Django? 【发布时间】:2010-12-25 22:35:51 【问题描述】:

我正在开发一个网络应用程序,用户可以在其中创建多个相关元素,然后将这些元素保存到数据库中。 webapp 不知道数据库中的主键是什么,因此它为每个元素分配一个 UUID。这些 UUID 与数据库中的每个元素一起保存。当 web 应用程序将数据发送到 web 服务器以放置在数据库中时,它使用 JSON 对所有内容进行编码。然后 Web 服务器使用 serializers.deserialize('json', DATA) 反序列化 JSON。但是,某些模型具有外键,它们在 JSON 有效负载中作为对关联元素的 UUID 的引用而不是数据库 ID 的引用发送。例如,我们可能有一个简单的链接对象:

class Link(models.Model):
    uuid = models.CharField(max_length=32)
    source = models.ForeignKey(Node, related_name='source')
    target = models.ForeignKey(Node, related_name='target')

如果源的 id 值为 2,目标的 id 值为 12,则这将被序列化为 JSON,如下所示:

"uuid": "[some long uuid]",
 "source": 2,
 "target": 12

但是,因为在这种情况下,我们不知道源和目标的数据库 id,可能是因为它们还没有设置,所以我能做的最好的就是像这样传递 UUID:

"uuid": "[some long uuid]", 
 "sourceuuid": "[uuid of source]", 
 "targetuuid": "[uuid of target]"

不幸的是,当我在数据上调用serializers.deserialize 时,这会引发FieldDoesNotExist: Link has no field named u'sourceuuid' 的异常。

我想找到一种方法来传递 UUID,然后让数据库在保存适当的部分或在必要时查找它们后填充 id。我不想在数据库中保存sourceuuidtargetuuid,因为保存整数的空间要少得多,而且索引也应该更快。

所以,我正在寻找的是一个临时字段。我可以实例化和引用的一个,但它永远不会保存回数据库。关于如何创建这样的东西有什么想法吗?

更新说明

到目前为止,感谢您的帮助,我知道它们是 Python 对象,我可以将任意字段分配给对象。但是,问题在于serializers.deserialize('json', INDATA) 会抛出错误。

作为参考,这里有一大块反序列化器喜欢的 JSON,它具有带有 ID 的外键:

ser='''["pk": 1, "model": "myapp.link", 
         "fields": "uuid": "aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee",
                    "source": 2,
                    "target": 12]'''
serializer.deserialize('json',ser)

不过,我能提供的是:

ser='''["pk": 1, "model": "myapp.link",
         "fields": "uuid": "aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee",
                    "sourceuuid": "11111111-2222-3333-4444-555555555555",
                    "targetuuid": "66666666-7777-8888-9999-000000000000"]'''
serializer.deserialize('json',ser)
FieldDoesNotExist: Link has no field named u'sourceuuid'

我需要将虚拟字段作为实际字段的原因是因为deserialize 需要实际字段。

【问题讨论】:

【参考方案1】:

为什么不直接使用 UIDS 作为主键?

将primary_key设置为True

class Node(models.Model):
    uuid = models.CharField(max_length=32, primary_key=True)
    [...]


class Link(models.Model):
    uuid = models.CharField(max_length=32, primary_key=True)

【讨论】:

我已经考虑过了,如果我找不到其他方法,我可能会这样做。但是,UUID 在数据库中占用更多空间,并且比 int 索引和搜索更慢。 目前还不清楚您要做什么......也许如果您提供有关您的任务的更多详细信息,将会有一个很好的解决方案【参考方案2】:

您可以为非物化 django 模型定义一个外键,该模型存储您需要的信息。

为此,在定义了外键(或 OnetoOne Key)的新模型中,只需放置

class _meta:
    managed = False

【讨论】:

这是最接近的,但不是您期望的方式。非物化 Django 模型的 ForeignKey 仍会在引用表中添加一列。但是,如果我使用 sourceuuidtargetuuid 作为新字段创建一个完整的非物化子类,我可以到达某个地方。我在一个新的 LinkJSON 类中反序列化,然后映射 UUID,然后将字段复制到 Link 类。它不漂亮,但它有点工作。如果 Python 有一种简单的方法将类型转换为基类,那就更好了。 我接受这个答案,因为它最接近我想要做的。我必须做一些时髦的子类化,然后创建 JSONSerializer 的子类来处理所有事情,但它现在可以工作了。不幸的是,它已成为一个非常具体的解决方案,因此我无法将其全部发布到 ***。【参考方案3】:

我不确定你到底想做什么。但请记住,Django 模型只是 Python 对象——而 Python 对象是完全动态的。所以即使模型上没有定义uuid字段,做obj.uuid = whatever也是完全有效的。

【讨论】:

我已经用更多细节更新了这个问题。我需要它是一个实际的Field,否则deserialize 会抛出异常。【参考方案4】:

您可以尝试创建普通的 python 属性,它们不会保存到数据库中。喜欢:

class Link(models.Model):
    sourceuuid = None

【讨论】:

请参阅我的更新以获得澄清。 deserializer 方法拒绝处理任何不是Field 的数据。所以这个方法会抛出异常。【参考方案5】:

我不知道有这样一个临时字段。会很好:)

但我的建议是:在接收 JSON 并将其转换为对象的视图中,检查属性。您创建一个对象,只有“真实”属性(不是外键或 M2M)。您可以为它们分配任意属性(instance.arbitrary_attribute = something),但您不会获得良好的查找机制(Model.objects.filter(...))。创建完所有对象后,再次循环遍历对象,并为每个 FK 查找具有链接 UUID 的对象,并链接到它(使用 instance.id)。

【讨论】:

我正在努力避免对 JSON 造成任何混乱。那是我可以在代码中引入错误和问题的另一个地方。它会起作用,但这是一个混乱的最后手段。

以上是关于如何在 Django 中向模型添加临时字段?的主要内容,如果未能解决你的问题,请参考以下文章

如何在 Django 中向 ModelForm 添加外键字段?

如何在 Django 中向多对多关系中添加字段?

如何在 django 中向 Case、When 条件添加排序

在 django 中向 QuerySet 添加 ManyToMany 额外字段

如何在 CKeditor 上传中向 POST 值添加字段

如何在 Django 中向 ManyToManyField 添加条目?