文章目录
本文章顺序参照Django官方文档的
Making queries
章节[https://docs.djangoproject.com/en/2.1/topics/db/queries/],举例model使用如下model:
class Book(models.Model):
name = models.CharField(max_length=256)
author = models.ManyToManyField("Author")
publisher = models.ForeignKey("Publisher",on_delete=models.CASCADE,related_name="pub")
publishDate = models.DateField()
description = models.TextField(blank=True,null=True)
def __str__(self):
return "《%s》" % self.name
class Meta():
verbose_name = "书籍"
verbose_name_plural = "书籍"
class Author(models.Model):
name = models.CharField(max_length=64)
age = models.IntegerField(null=True,blank=True)
nationality = models.CharField(max_length=128,verbose_name="国籍")
def __str__(self):
return self.name
class Meta():
verbose_name = "作者"
verbose_name_plural = "作者"
class Publisher(models.Model):
name = models.CharField(max_length=128)
address = models.CharField(max_length=256,null=True,blank=True)
def __str__(self):
return self.name
class Meta():
verbose_name = "出版社"
verbose_name_plural = "出版社"
1. 一般查询
1)查询所有对象
objs = Book.objects.all()
返回的objs为QuerySet;
2) 使用筛选器实现检索特定对象
筛选name
为Book1
的所有Book
的对象:
objs = Book.objects.filter(name = "Book1")
筛选name
不为Book1
的所有Book
的对象;
objs = Book.objects.exclude(name = "Book1")
链接过滤器:
objs = Book.objects.exclude(name = "Book1").filter(description = "")
过滤后的查询集是惟一的;每次优化QuerySet时,都将得到一个全新的QuerySet,而这个QuerySet绝不绑定到前面的QuerySet。每个细化都创建一个单独的、不同的QuerySet,该QuerySet可以存储、使用和重用。
QuerySet是惰性的,只有在被使用时,才会去数据库查询:
obj = Book.objects.exclude(name = "Book1")
obj = obj.objects.filter(description ="")
obj = obj.objects.filter(name = "Book2")
print(obj)
看似三次查询,但实际上print(obj)
之前,查询的结果都不会从数据库中获取,只有执行print(obj)
时,才会去数据库查询,实际就进行了一次数据库取值。
##3)使用get()实施检索单个对象
obj = Book.objects.get(name = "Book1")
不返回QuerySet,而是返回单个对象;
没有符合条件的对象时,会触发Book.DoesNotExist异常
4) 其他QuerySet方法
参见另一个篇博客 [baidu.com]
5) 限制QuerySet
使用Python数组切片语法的一个子集来将查询集限制为一定数量的结果。这相当于SQL的LIMIT
和OFFSET
语句:
返回前5个对象(LIMIT 5):
obj = Book.objects.all()[:5]
返回第6到第10个对象 (OFFSET 5 LIMIT 5):
obj = Book.objects.all()[5:10]
返回前10个对象的每个第二个对象的列表:
obj = Book.objects.all()[:10:2]
注:不支持负数查询,如:
obj = Book.objects.all()[-1] # 错误的
6)字段查询
字段查找是指定SQL中 WHERE
子句的主体的方式,字段可以被指定为QuerySet方法filter()、exclude()和get()的关键字参数。
基本查找关键字参数采用表单**filed__lookuptype = value(双下划线) **
obj = Book.objects.filter(publishDate__lte = "2017-01-01")
转换为SQL语言大致如下:
SELECT * FROM app_book WHERE publishDate <= '2006-01-01';
# app_book中的app指的是Book所在的django的app名字
查找中指定的字段必须是模型字段的名称,但是有一个例外,对于外键,可以指定_id为后缀的字段名,这种情况下,value参数应该包含外部模型的主键的原始值
obj = Book.objects.filter(publishDate__lte = "2017-01-01")
如果传递无效的关键字参数,则查找函数将引发TypeError
错误
详细的字段查找,可以参见另一个篇博客 [baidu.com]
2. 跨模型查询
Django提供了一种强大而直观的方法来“跟踪”查询中的关系,在幕后自动地为您处理SQL连接。要跨越关系,只需跨模型使用相关字段的字段名,用双下划线分隔,直到到达你想要的字段。
#外键查询
obj = Book.objects.filter(publisher__name__contanins="publish")
#ManyToMany字段查询
obj = Book.objects.filter(author__name = "Jim")
#返回Book所有对象中author的name为Jim的对象组成的 QuerySet
也可以进行反向查询
obj = Author.objects.filter(book__name__contanins="Book1")
# name中包含Book1的Book对象中的author对应的对象
obj = Publisher.objects.filter(pub__name = "Jim")
#用pub是应为文章开头的model中,Book中的publisher字段中设置了related_name="pub"
3. F()查询
将一个模型字段的值与同一模型上的另一个字段的值进行比较时,就会用到F()表达式
# 查询Book对象中name字段与description字段的值相等的对象
obj = Book.objects.filter(name = F('description'))
F()也可以跨模型
# 查询Book对象中name字段与外键publisher的name字段的值相等的对象
obj = Book.objects.filter(name = F('publisher__name'))
4. 主键pk快捷查询
Book.objects.get(id__exact=14) # Explicit form
Book.objects.get(id=14) # __exact is implied
Book.objects.get(pk=14) # pk implies id__exact
以上三句是相同的,id是默认主键,也可以在model中自定义主键
5.Caching 和 QuerySets
每个QuerySet都包含一个缓存,以最小化数据库访问。
print([e.name for e in Book.objects.all()])
print([e.publishDate for e in Book.objects.all()])
上述代码,相同的数据库查询将执行两次,使数据库负载加倍;
此外,这两个列表可能不包含相同的数据库记录,因为在两个请求之间可能在瞬间添加或删除了一个条目。
为了避免这个问题,只需保存QuerySet并重用它:
queryset = Book.objects.all()
print([p.name for p in queryset])
print([p.publishDate for p in queryset]) # 重用QuerySet
何时QuerySet不进行缓存:
具体来说,这意味着使用数组切片或索引限制queryset不会填充缓存。
- 在QuerySet对象中反复获取某个索引将每次都查询数据库
queryset = Book.objects.all()
print(queryset[0]) # 查询数据库
print(queryset[0]) # 再次查询数据库
- 如果已经计算了整个QuerySet,那么将检查缓存
queryset = Book.objects.all()
[book for book in queryset] # Queries the database
print(queryset[5]) # 使用缓存
print(queryset[5]) # 使用缓存
6.使用Q对象进行复杂的查找
如果需要执行更复杂的查询(例如,带有OR语句的查询),可以使用Q对象。
Book.objects.get(
Q(name__startswith='Who'),
Q(publishData=date(2005, 5, 2)) | Q(publishData=date(2005, 5, 6))
)
对应SQL语句为:
SELECT * from books WHERE name LIKE 'Who%'
AND (publishData= '2005-05-02' OR publishData= '2005-05-06')
7. 关联查询
注:对应于原生SQL语句的join查询
1) 通过对象执行关联查询
三种关联关系:
- 一对多关系
- 多对多关系
- 一对一关系
由一到多的访问语法:
**以 出版社- 图书 即为一对多的关系 **
- 由一到多的访问语法
# 一对应的模型类对象.多对应的模型类名小写_set
# 例:
pub = Publisher.objects.get(id=1)
b.book_set.all()
- 由多到一的访问语法:
# 多对应的模型类对象.多对应的模型类中的关系类属性名
# 例:
book1 = Book.objects.get(id=1)
book1.publisher
通过模型类执行关联查询
由多模型类条件查询一模型类数据
# 关联模型类名小写__属性名__条件运算符=值
# 如果没有 __条件运算符 部分,表示等于
# 例:
list = Publish.objects.filter(book__title__contains="八")
有一模型类条件查询多模型类数据
# 一模型类关联属性名__一模型类属性名__条件运算符 = 值
# 例如,查询出版社名为“清华大学出版社”的所有书
list = Book.objects.filter(publisher__name="清华大学出版社")
8. 自关联
自关联模型,就是表中的某一列,关联了这个表中的另外一列。最典型的自关联模型就是地区表。省、市、县都在一张表里面。省的pid为null,市的pid为省的id,县的pid为市的id。
例如:
class Area(models.Model):
name = models.CharField(max_length=20, verbose_name='名称')
# 自关联(特殊的一对多): 生成的字段名 parent_id
parent = models.ForeignKey('self', verbose_name='上级行政区划')
class Meta:
db_table = 'tb_areas'
verbose_name = '行政区划'
如果知道一个市,叫a市,想查他属于什么省:
a = Area.objects.get(id=1)
# b就是a市的省份的对象
b = a.parent
如果知道一个省,叫a省,想查他有什么市
c = Area.objects.get(id=1)
# d就是c省的所有市的查询集合
d = c.area__set.all()