通过JSONField关联的模型的Django ORM JOIN

y4ekin9u  于 2023-02-14  发布在  Go
关注(0)|答案(3)|浏览(138)

如果我有2个通过ForeignKey关联的模型,我可以很容易地得到一个查询集,其中两个模型都使用select_related连接

class Foo(Model):
    data = IntegerField()
    bar = ForeignKey('Bar', on_delete=CASCADE)

class Bar(Model):
    data = IntegerField()

foos_with_joined_bar = Foo.objects.select_related('bar')
for foo in foos_with_joined_bar:
    print(foo.data, foo.bar.data)  # this will not cause extra db queries

我想做同样的事情,但在Foo将其对bar的引用保留在JSONField中的情况下

class Foo(Model):
    data = IntegerField()
    bar = JSONField()  # here can be something like {"bar": 1} where 1 is the ID of Bar

class Bar(Model):
    data = IntegerField()

foos_with_joined_bar = ???

在这种情况下,是否可以使用Django ORM获得foos_with_joined_bar
另外,我们不讨论在JSONField中存储外键的原因,当然最好只使用ForeignKey。

iqxoj9l9

iqxoj9l91#

Import json

bar = Bar.objects.get(pk=json.loads(foo.bar)[“bar”])

您必须解析JSON。

qaxu7uf2

qaxu7uf22#

可以将值从Bar对象注解到Foo对象

from django.db.models import OuterRef, Subquery
b = Bar.objects.filter(id=OuterRef('bar__bar'))
f = Foo.objects.all().annotate(barx=Subquery(b.values('data')[:1]))

for e in f:
    print(e.data, e.barx)

它从JSON字段中提取值并查找匹配的pk

ybzsozfc

ybzsozfc3#

最后,我发现了一个有点棘手的方法来注解Foo与完整的酒吧模型。
TLDR:使用JSONObject用Bar的dict表示注解Foo,然后使用此dict手动创建foo.bar

经理.py

from django.db.models import QuerySet
from django.db.models.functions import JSONObject, Cast
from django.db.models.fields.json import KeyTextTransform
from django.db.models.fields import BigIntegerField

class BarQS(QuerySet):
    def as_json(self):
        return self.values(json=JSONObject(**{f: f for f in self.model.json_fields}))

class FooQS(QuerySet):
    def annotate_bar(self):
        from .models import Bar

        return (
            self
            .annotate(bar_id=Cast(KeyTextTransform("bar", "bar"), BigIntegerField()))
            .annotate(bar=Bar.objects.filter(pk=OuterRef('bar_id')).as_json())
        )

    def foobar_iter(self):
        from .models import Bar

        for foo in self:
            bar_model = Bar(**foo.bar)
            foo.bar = bar_model
            yield foo

型号.py

from django.db.models import Model, IntegerField, JSONField
from .managers import FooQS, BarQS

class Foo(Model):
    data = IntegerField()
    bar = JSONField()

    objects = FooQS.as_manager()

class Bar(Model):
    data = IntegerField()
    data2 = IntegerField()

    objects = BarQS.as_manager()
    json_fields = ('data', 'data2')  # here we define fields translated into JSON

已经完成了,现在我们可以做这样的事情了

foos_with_joined_bar = Foo.objects.annotate_bar().foobar_iter()

for foo in foos_with_joined_bar:
    print(foo.data, foo.bar.data, foo.bar.data2)

相关问题