django, web development, sql,

Django基于双下划线的多表/跨表查询

DolorHunter DolorHunter Follow Nov 01, 2020 · 16 mins read

Django基于双下划线的多表/跨表查询
Share this

两个月前写了 用Django框架做一个Hello World模板网页Django框架下SQLite3数据库CRUD增删改查的编写. 然后遇到了一些其他事情就停了下来(没想到都已经偷懒两个月了) 如果你是第一次接触 Django 请去看看这篇.

Django系列文章:

之前只学过 SQL 的 CRUD 语句, 虽然在前一篇已经学了一些简单的 Django 语句, 不过对于一些复杂一些的语句还是不太清楚. 这次花了一些时间重温了一下建关联表的规则并学习了一下跨表查询的语句.

刚开始看了一些 Django ORM – 多表实例 的材料, 因此看着头很大. 给了很多查询方法, 最终发现还是 基于双下划线的多表/跨表查询 最通用又简单.

我这次做的课设项目是机场售票系统, 其中一个功能就是购买机票. 购票需要用到用户表(user, 好像也不一定用), 机票表(ticket)和购票表(bookTicket, 记录用户ID和票据ID).

后来误打误撞 user 用的是 Django 的用户系统, 能通过request.user来获得用户信息, 用的是自带的表, 其实也差不多. 以下是机票表(ticket)和购票表(bookTicket).

class TicketItem(models.Model):
    flight_name = models.TextField()
    flight_date = models.TextField()
    flight_capacity = models.IntegerField()
    flight_price = models.TextField()
    ...


class BookTicketItem(models.Model):
    user_id = models.IntegerField()
    ticket_id = models.ForeignKey(TicketItem, on_delete=models.CASCADE)
    book_status = models.TextField()

Django 会自动创建 id, 因此没有写出来. 我计划在购票表(bookTicket)内填写用户ID和票据ID, 再附加一个购票状态. 前两个 ID 对应另外两个表的主键, 因此设置为外键(userId可通过request.user.id获取, 就没设置成ForeignKey). 之前还花了很多时间在思考表是一对一, 一对多还是多对多关系, 后来发现似乎都不是, 数据库还是要好好读一下.

在处理好了表间的关系之后, 接下来就是如何查表了. 我设计了三个关联的功能, 购票, 退票和显示我的票务.

购票需要获取当前用户的ID, 通过前端获得票据的ID, 再写入一个默认的状态, 比较简单; 退票则是把当前票据的状态修改一下, 做一个软删除. 不过, 显示我的票务应该怎么做呢?

从逻辑上来说, 我需要先获得用户ID(user_id), 再通过在 BookTicket 表中查找(select)出该ID的机票号(ticket_id). 再把机票号作为条件, 在机票表中搜索就可以得到结果.

select flight_name, ... from TicketList as t, BookTicketList as bt where
            bt.user_id=request.user.id and bt.ticket_id = t.id

这之中必须要做一次笛卡尔积运算, 不过我简单看了一下似乎没有这个操作. 而且大部分的示例代码里面都有个 .first(), 十分投机.

看了一下午的资料后, 发现了一个写的比较清楚的示例 hellow3 - 执行查询, 看了一下豁然开朗.

Django 有点太过简洁了, 简洁到我有点难以置信. 查询集做跨表查询时,使用双下划线”__”. 这样一来查表就简单多了, 一行就能写完.

all_ticket_items = TicketItem.objects.filter(bookticketitem__user_id=request.user.id, bookticketitem__book_status='1')

其中, bookticketitem 是其他表的类名, 后面要加双下划线; 后面跟的是属性 user_id.

真不敢相信, 一下午就被这一行代码浪费了大多数的时间. 大部分时间都被 菜鸟教程 里的 .first() 误导了. Django 跨表查询还是要用 基于双下划线的多表/跨表查询, 又快又好.

参考资料:

Join Newsletter
Get the latest news right in your inbox. We never spam!
DolorHunter
Written by DolorHunter
Developer & Independenet Blogger