Django ORM 入坑记录

前言

Django ORM 总的来讲仍是很是好用的,可是坑仍是要一个一个踩。在使用 ORM 的同时使用 debug_toolbar 或者 logging 查看数据库查询语句,才能玩得溜啊。python

Queryset 缓存

Django 的 查询是带有 Lazy 的,并且是有缓存的。Lazy 就在于查询只会在查询结果被调用时才开始进行,查询结果会保存在缓存中,屡次访问同一查询结果里的内容不会产生新的查询。而须要注意的以下:数据库

bids = Bidding.objects.all()
# 我发现以下打印查询结果,分别会进行进行两次数据库查询,一共进行了两次查询操做
bids[0]
bids[1]

# 可是若是咱们先将 bids 转化为 list 类型,或者直接将 bids 作成 for 循环,以下:
bidlist = list(bids)
bidlist[0]
bidlist[1]
# 或者
for bid in bids:
    print bid.id
# 二者都会将 bids 查询到的两列元素打印出来,但只作了一次查询

prefetch_related 和 select_related

关系型数据库中常见的关系类型有 OneToOne, ManyToOne, ManyToMany。公司员工表中的一个员工与其在另一张员工信息表中的关系为 OneToOne,这个员工在职位表中可能存在多个职位数据,这里关系就是 ManyToOne 的了,而 ManyToMany 则能够是卫生值日表,一我的能够同时擦窗户和擦桌子,同时扫地这件事情多是由多我的来作的。在 ManyToMany 的关系中,Django 会自动生成一张额外的表存储对应信息。django

select_related 适用于 ManyToOne 和 OneToOne,用于存在外键时,采用 Join 操做事先提取全部外键对应的信息,避免了 n+1 次查询。缓存

prefetch_related 适用于 ManyToOne 和 ManyToMany,不用一条 SQL 语句解决问题,而是稍微进行屡次查询,采用 IN 操做减小查询次数。详情可见参考资料。函数

prefetch_related 和 select_related 默认是不支持分片的,以下:fetch

# #1
books = Book.objects.filter(writer__id=writer_id).prefetch_related('user', 'city', 'supplier', 'supplier__user')[start:end]
# #2
books = Book.objects.filter(writer__id=writer_id).select_related('user', 'city', 'supplier', 'supplier__user')[start:end]
# #3
books = Book.objects.filter(writer__id=writer_id)[start:end].prefetch_related('user', 'city', 'supplier', 'supplier__user')
# #4
books = Book.objects.filter(writer__id=writer_id)[start:end].select_related('user', 'city', 'supplier', 'supplier__user')

writers = Writer.objects.filter(books__in=books)

上述四种查询都会让 writer 这句代码引发 This version of MySQL doesn't yet support 'LIMIT & IN/ALL/ANY/SOME subquery 的错误,缘由在于全部 books 的赋值语句都不会执行,而是因为 lazy queryset 的缘由,这句查询只会做为一个子查询带入到 Writer 的查询语句里面,而 books 的子查询语句中自带了 limit,正巧子查询不支持 limit,因此就报了错。优化

要解决这个问题,能够先将 books 变成一个现成的结果,好比将其变成一个 book_id 的 list,而后就能够经过如下代码来取出想要的结果啦。debug

writers = Writer.objects.filter(books_id__in=book_id_list)

更新 update 操做

Book.objects.filter(writer__id=writer_id).update(status=Book.OUT_OF_DATE)

会产生两条数据库语句,一条查询,一条更新code

Book.objects.filter(id=book_id).update(status=Book.OUT_OF_DATE)

直接产生一条更新语句,缘由是前者的查询条件中有外键。blog

不一样类型的条件取并集

不一样类型的条件取并集,在带入查询时候比较蛋疼,这里用了一种比较丑陋的实现。

level_type = request.GET.get('level') or None
pay_status = request.GET.get('pay') or None
clearing_status = request.GET.get('clearing') or None
filters = Q()
if level_type:
    filters &= Q(level_type=level_type)
if pay_status:
    filters &= Q(pay_status=pay_status)
if clearing_status:
    filters &= Q(clearing_status=clearing_status)
orders = Order.objects.filter(filters)

参考资料

相关文章
相关标签/搜索