Django ORM 总的来讲仍是很是好用的,可是坑仍是要一个一个踩。在使用 ORM 的同时使用 debug_toolbar 或者 logging 查看数据库查询语句,才能玩得溜啊。python
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 查询到的两列元素打印出来,但只作了一次查询
关系型数据库中常见的关系类型有 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)
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)