如何使用 JSONField 的内容向 django 查询集添加预取(或类似内容)?

问题描述 投票:0回答:1

背景

我有一个使用传统关系设置的复杂分析应用程序,其中使用 CRUD 模型更新不同的实体。

但是,我们的系统完全是事件驱动的,管理历史记录并应用尊重该历史记录的数据迁移正变得非常痛苦。

因此,我们正在转向准 noSQL 方法,在该方法中,我使用 JSON 来表示数据,并使用表示历史记录的补丁链接列表来表示数据。

就数据结构而言,这是一个非常优雅的解决方案,并且可以与其他服务很好地配合。就 django 而言,我现在正在努力使用 ORM。

我的数据库是 PostgreSQL。

情况

以前我有类似的东西(伪代码):

class Environment(models.Model):
    id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)

class Analysis(models.Model):
    created = models.DateTime(auto_now_add=True)
    environment = models.ForeignKey(
        "myapp.Environment",
        blank=True,
        null=True,
        related_name="analyses",
    )
    everything_else = models.JSONField()

而我现在将会:

class Environment(models.Model):  
    id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)  
  
class Analysis(models.Model):  
    created = models.DateTime(auto_now_add=True)
    everything = models.JSONField()

其中

everything
可能是一个 JSON 对象,看起来像......

{
  "environment": "2bf94e55-7b47-41ad-b81a-6cce59762160",
  "other_stuff": "lots of stuff"
}

现在,我仍然可以完全获得给定

Environment
Analysis
,因为我有它的 ID,但如果我这样做(再次伪代码):

for analysis in Analysis.objects.filter(created=today).all():
   print(Environment.objects.get(id=analysis.everything['environment'])

我当然会得到一个

N+1
错误,因为我在每次迭代时都在数据库中查询环境。

在一个简单的脚本中,我可以将所有 id 放入一个列表中,然后对它们进行一次查询...但这不适用于所有 djangos 内置函数,例如 API 序列化器方法和管理方法。我真的希望能够修改查询集以获得我想要的。

问题

给定像

Analysis.objects.filter(created=today).all()
这样的查询集,现在它们的 ID 位于 JSONField 中,如何调整此查询集以预取相关环境对象?

python json django postgresql jsonfield
1个回答
0
投票

永远不要使用 JSON 字段,除非你绝对可以证明它的合理性。

JSON 字段是人们依赖的拐杖,因为他们懒得设计正确的建模。

  1. 将 json 数据非规范化到单独的表中。
  2. 使用 django 历史记录/其他一些内置/第三方工具进行历史跟踪。

Json 字段会混淆你的逻辑并使一切变得不可行。这是我第二次参与一个项目,工程师们虽然很优雅或简单,但最终花费了半年甚至更多的时间来修复由此造成的意大利面条。

就预取内容而言,您可以使用注释和子查询,但是您尝试对 json 字段执行的任何操作都将非常昂贵。 具体来说,因为您无法再利用数据库操作,例如联接/索引查找等...

那么,在你的情况下我将如何进行:

如果问题对于当前使用的 JSON 字段来说太复杂,则使用 post_save 信号来填充您维护的表,代表 json 字段的一段。然后,您可以在预取/ORM 操作中使用该表。

© www.soinside.com 2019 - 2024. All rights reserved.