请问 Django ORM 如何实现 left join?

2022-04-18 17:02:25 +08:00
 ila
select be.id, be.fullName1, be.workEmail
from employees as be
         left join id_mapping as ci
                   on be.id = ci.user_id
where be.id like '%Steven%'
   or be.fullName1 like '%Steven%'
   or be.workEmail like '%Steven%'
   or ci.new_id like '%Steven%'
group by be.id

2885 次点击
所在节点    Django
10 条回复
lybcyd
2022-04-18 18:31:04 +08:00
django orm 和其他 orm 都不太一样,是不能直接使用 query builder 进行 join 的。后面的 group by 也不方便直接用,不过你这个语句 id 应该是 unique 的,又是非标准的 full group 语法,先忽略掉吧。

像这个语句,如果要尽可能使用 django orm 的推荐做法,应该建一个 IdMapping 的 model ,然后建立一对多或者一对一的关联关系(看你的业务逻辑)。这个关联关系可以直接当做一个 field 获取,也可以在条件里进行 lookup 。假设在 Employee model 里,这个关联关系叫做 mapping ,就可以直接使用 Employee.objects.filter(Q(id__icontains='Steven')|Q(fullName1__icontains='Steven')|Q(workEmail__icontains='Steven')|Q(mapping__new_id__icontains='Steven')).values('id','fullName1', 'workEmail')
来进行查询了。

看看官方文档是怎么定义的:

https://docs.djangoproject.com/zh-hans/4.0/topics/db/models/#many-to-one-relationships

https://docs.djangoproject.com/zh-hans/4.0/topics/db/queries/#lookups-that-span-relationships-1

如果 join 关系实在太复杂,用自带的很难拼接,那就干脆 raw 来实现。ORM 不是万能的,拼接一大堆复杂的条件还不如直接 sql 语句搞定。
roundgis
2022-04-18 18:34:01 +08:00
我用 orm 僅限 inner join

其他用法一律寫 sql

強行用 orm 有時候可讀性反而差
ila
2022-04-18 23:07:34 +08:00
@lybcyd 谢谢你的回复。
之前类似的例子。以为太复杂。
现在看来,还得动手写一遍。
ila
2022-04-18 23:09:32 +08:00
@roundgis 是的。
因为之前用 flask 时,sqlalchemy 很容易实现 left join 。
想在 Django 里也实现,结果看来还得按照官方的教程来,就是复杂些了。
roundgis
2022-04-18 23:32:32 +08:00
@ila django orm 沒法和 sqlalchemy 比

你也可以兩個混用

用 django 來建模 用 sqlalchemy 來做複雜的查詢
neoblackcap
2022-04-19 01:31:24 +08:00
抱歉,Django ORM 没有 Left Join
ila
2022-04-19 09:40:51 +08:00
@neoblackcap 没想到有些复杂.
因为 left join 后数据最后要分页,想用 Django 这套来做.
@roundgis 好吧...
ila
2022-04-19 09:49:42 +08:00
@lybcyd 请问用这种方法应该怎么写呢
[left-join-django-orm]( https://stackoverflow.com/questions/21271835/left-join-django-orm)
仿写了几遍,卡住了,运行不成功


```
from django.db.models.sql.datastructures import Join
from django.db.models.fields.related import ForeignObject
from django.db.models.options import Options
from myapp.models import Ace
from myapp.models import Subject

jf = ForeignObject(
to=Subject,
on_delete=lambda: x,
from_fields=[None],
to_fields=[None],
rel=None,
related_name=None
)

jf.opts = Options(Ace._meta)
jf.opts.model = Ace
jf.get_joining_columns = lambda: (("subj", "name"),)

j=Join(
Subject._meta.db_table, Ace._meta.db_table,
'T1', "LEFT JOIN", jf, True)

q=Ace.objects.filter(version=296)
q.query.join(j)

print q.query
```
ila
2022-04-19 10:16:25 +08:00
@ila 用 Employees.objects.raw 也可以分页.
lybcyd
2022-04-19 12:06:07 +08:00
@ila 这个写法用了好多 orm 的内部结构,有点舍近求远了,不是很建议。

我简单写了个小 demo ,你看看能不能参考一下。

首先是 model 定义,关联关系是在这里定义的。不知道你的完整表结构,我就简单列了语句里出现的字段,其余的你自己补充一下吧。接下来可以进行查询了。

<script src="https://gist.github.com/lybcyd/b83084303657134d7cff52597ae315f9.js"></script>

打印这个 queryset ,可以看到生成的语句,我这里用的是 sqlite:

SELECT "employees"."id", "employees"."fullName1", "employees"."workEmail" FROM "employees" LEFT OUTER JOIN "id_mapping" ON ("employees"."id" = "id_mapping"."user_id") WHERE ("employees"."id" LIKE %Steven% ESCAPE '\' OR "employees"."fullName1" LIKE %Steven% ESCAPE '\' OR "employees"."workEmail" LIKE %Steven% ESCAPE '\' OR "id_mapping"."new_id" LIKE %Steven% ESCAPE '\')

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/847701

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX