V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
ila
V2EX  ›  Django

请问 Django ORM 如何实现 left join?

  •  
  •   ila · 2022-04-18 17:02:25 +08:00 · 3164 次点击
    这是一个创建于 948 天前的主题,其中的信息可能已经有所发展或是发生改变。
    • 原始 sql 语句如下:
    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
    
    
    • 研究了一下午,暂无头绪.先用 raw 运行着 sql 语句.
    • 请各位出手相助.
    10 条回复    2022-04-19 12:06:07 +08:00
    lybcyd
        1
    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
        2
    roundgis  
       2022-04-18 18:34:01 +08:00 via Android
    我用 orm 僅限 inner join

    其他用法一律寫 sql

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

    你也可以兩個混用

    用 django 來建模 用 sqlalchemy 來做複雜的查詢
    neoblackcap
        6
    neoblackcap  
       2022-04-19 01:31:24 +08:00
    抱歉,Django ORM 没有 Left Join
    ila
        7
    ila  
    OP
       2022-04-19 09:40:51 +08:00
    @neoblackcap 没想到有些复杂.
    因为 left join 后数据最后要分页,想用 Django 这套来做.
    @roundgis 好吧...
    ila
        8
    ila  
    OP
       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
        9
    ila  
    OP
       2022-04-19 10:16:25 +08:00
    @ila 用 Employees.objects.raw 也可以分页.
    lybcyd
        10
    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 '\')
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   3440 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 34ms · UTC 10:46 · PVG 18:46 · LAX 02:46 · JFK 05:46
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.