查询条件
查找是数据库操作中一个非常重要的技术。查询一般就是使用filter
、exclude
以及get
三个方法来实现。我们可以在调用这些方法的时候传递不同的参数来实现查询需求。在ORM
层面,这些查询条件都是使用field
+__
+condition
的方式来使用的。
get
返回一个模型对象filter
返回一个QuerySet
- QuerySet.query:
query()
可以用来查看查询语句最终被翻译成的SQL
语句。但是query()
只能被用在QuerySet
对象上,不能用在普通的ORM模型
查询条件
exact
使用精确的=
进行查找。如果提供的是一个None
,那么在SQL
层面就是被解释为NULL
。示例代码如下:
article = Article.objects.get(id__exact=14) # == (id = 14)
article = Article.objects.get(id__exact=None) # == (id = None)
以上的两个查找在翻译为SQL
语句为如下:
select ... from article where id=14;
select ... from article where id IS NULL;
collation排序规则
- 在windows上,mysql的collation无论选择什么都是大小写不敏感
- 在linux上,mysql的collation如果是utf_bin,则大小写敏感
iexact
使用like
进行查找。示例代码如下:
article = Article.objects.filter(title__iexact='hello world')
那么以上的查询就等价于以下的SQL
语句:
select ... from article where title like 'hello world';
注意上面这个sql
语句,因为在MySQL
中,没有一个叫做ilike
的。所以exact
和iexact
的区别实际上就是LIKE
和=
的区别,在大部分collation=utf8_general_ci
情况下都是一样的(collation
是用来对字符串比较的)。
contains
大小写敏感,判断某个字段是否包含了某个数据。示例代码如下:
articles = Article.objects.filter(title__contains='hello')
在翻译成SQL
语句为如下:
select ... where title like binary '%hello%';
binary: 大小写敏感
要注意的是,在使用contains
的时候,翻译成的sql
语句左右两边是有百分号的,意味着使用的是模糊查询。而exact
翻译成sql
语句左右两边是没有百分号的,意味着使用的是精确的查询。
icontains
大小写不敏感的匹配查询。示例代码如下:
articles = Article.objects.filter(title__icontains='hello')
在翻译成SQL
语句为如下:
select ... where title like '%hello%';
in
提取那些给定的field
的值是否在给定的容器中。容器可以为list
、tuple
或者任何一个可以迭代的对象,包括QuerySet
对象。示例代码如下:
articles = Article.objects.filter(id__in=[1,2,3])
以上代码在翻译成SQL
语句为如下:
select ... where id in (1,3,4)
当然也可以传递一个QuerySet
对象进去。示例代码如下:
inner_qs = Article.objects.filter(title__contains='hello') # QuerySet
categories = Category.objects.filter(article__in=inner_qs)
以上代码的意思是获取那些文章标题包含hello
的所有分类。
将翻译成以下SQL
语句,示例代码如下:
select ...from category where article.id in (select id from article where title like '%hello%');
如果要判断相关联的表的字段,也是通过__
来连接。
- 在做关联引用的时候,不需要写
models_set
,直接使用模型的名字的小写化就可以了。比如通过分类去查找相应的文章,那么通过article__id__in iterator
而不是article_set__id__in
的形式。如果使用related__query_name = "xxx"
执指定了外键名,则就直接使用该外键名- 如果查询的字段就是模型的主键,那么可以省略掉该字段,直接写成
article__in
就而无需article__id__in
- 如果查询的字段就是模型的主键,那么可以省略掉该字段,直接写成
gt
gt: greater than
某个field
的值要大于给定的值。示例代码如下:
articles = Article.objects.filter(id__gt=4)
以上代码的意思是将所有id
大于4的文章全部都找出来。
将翻译成以下SQL
语句:
select ... where id > 4;
gte
gte: greater than equal
类似于gt
,是大于等于。
lt
lt: lower than
类似于gt
是小于。
lte
lte: lower than equal
类似于lt
,是小于等于。
startswith
判断某个字段的值是否是以某个值开始的。大小写敏感。示例代码如下:
articles = Article.objects.filter(title__startswith='hello')
以上代码的意思是提取所有标题以hello
字符串开头的文章。
将翻译成以下SQL
语句:
select ... where title like 'hello%'
istartswith
类似于startswith
,但是大小写是不敏感的。
endswith
判断某个字段的值是否以某个值结束。大小写敏感。示例代码如下:
articles = Article.objects.filter(title__endswith='world')
以上代码的意思是提取所有标题以world
结尾的文章。
将翻译成以下SQL
语句:
select ... where title like '%world';
iendswith
类似于endswith
,只不过大小写不敏感。
range
判断某个field
的值是否在给定的区间中。示例代码如下:
from django.utils.timezone import make_aware # 转为清醒时间
from datetime import datetime
"""
make_aware(value, timezone=None, is_sdt=None)
#如果timezone未指定,则使用setting.py中TIME_ZONE设置的时区
return value.replace(tzinfo=timezone)
"""
start_date = make_aware(datetime(year=2018,month=1,day=1))
end_date = make_aware(datetime(year=2018,month=3,day=29,hour=16))
articles = Article.objects.filter(pub_date__range=(start_date,end_date))
以上代码的意思是提取所有发布时间在2018/1/1
到2018/12/12
之间的文章。
将翻译成以下的SQL
语句:
select ... from article where pub_time between '2018-01-01' and '2018-12-12'。
需要注意的是,以上提取数据,不会包含最后一个值。range
右闭合
而且另外一个重点,因为我们在settings.py
中指定了USE_TZ=True
,并且设置了TIME_ZONE='Asia/Shanghai'
,因此我们在提取数据的时候要使用django.utils.timezone.make_aware
先将datetime.datetime
从navie
时间转换为aware
时间。make_aware
会将指定的时间转换为TIME_ZONE
中指定的时区的时间。
date
针对某些date
或者datetime
类型的字段。可以指定date
的范围。并且这个时间过滤,还可以使用链式调用。示例代码如下:
articles = Article.objects.filter(pub_date__date=date(2018,3,29))
以上代码的意思是查找时间为2018/3/29
这一天发表的所有文章。
将翻译成以下的sql
语句:
select ... WHERE DATE(CONVERT_TZ(`front_article`.`pub_date`, 'UTC', 'Asia/Shanghai')) = 2018-03-29
注意,因为默认情况下MySQL
的表中是没有存储时区相关的信息的。因此我们需要下载一些时区表的文件,然后添加到Mysql
的配置路径中。如果你用的是windows
操作系统。那么在http://dev.mysql.com/downloads/timezones.html
下载timezone_2018d_posix.zip - POSIX standard
。然后将下载下来的所有文件拷贝到C:\ProgramData\MySQL\MySQL Server 5.7\Data\mysql
中,如果提示文件名重复,那么选择覆盖即可。
如果用的是linux
或者mac
系统,那么在命令行中执行以下命令:mysql_tzinfo_to_sql /usr/share/zoneinfo | mysql -D mysql -u root -p
,然后输入密码,从系统中加载时区文件更新到mysql
中。
year
根据年份进行查找。示例代码如下:
articles = Article.objects.filter(pub_date__year=2018)
articles = Article.objects.filter(pub_date__year__gte=2017)
以上的代码在翻译成SQL
语句为如下:
select ... where pub_date between '2018-01-01' and '2018-12-31';
select ... where pub_date >= '2017-01-01';
month
同year
,根据月份进行查找。
day
同year
,根据日期进行查找。
week_day
Django 1.11
新增的查找方式。同year
,根据星期几进行查找。1表示星期天,7表示星期六,2-6
代表的是星期一到星期五。
time
根据时间进行查找。示例代码如下:
articles = Article.objects.filter(pub_date__time=datetime.time(12,12,12));
以上的代码是获取每一天中12点12分12秒发表的所有文章。
更多的关于时间的过滤,请参考Django
官方文档:https://docs.djangoproject.com/en/2.0/ref/models/querysets/#range
。
isnull
根据值是否为空进行查找。示例代码如下:
articles = Article.objects.filter(pub_date__isnull=False)
以上的代码的意思是获取所有发布日期不为空的文章。
将来翻译成SQL
语句如下:
select ... where pub_date is not null;
- 如果要具体到秒,比较难以匹配。可以使用区间的方式
range
来查询。- 比如想要获取17时10分27秒-28秒的文章,可以通过以下代码
start_time = date(hour=17, minute=10, second=27)
end_time = date(hour=17, minute=10, second=28)
articles = Article.object.filter(create_time__time__range(start_time, end_time))
- 比如想要获取17时10分27秒-28秒的文章,可以通过以下代码
regex和iregex
大小写敏感和大小写不敏感的正则表达式。示例代码如下:
articles = Article.objects.filter(title__regex=r'^hello')
以上代码的意思是提取所有标题以hello
字符串开头的文章。
将翻译成以下的SQL
语句:
select ... where title regexp binary '^hello';
iregex
是大小写不敏感的。
根据关联的表进行查询
假如现在有两个ORM
模型,一个是Article
,一个是Category
。代码如下:
class Category(models.Model):
"""文章分类表"""
name = models.CharField(max_length=100)
class Article(models.Model):
"""文章表"""
title = models.CharField(max_length=100,null=True)
category = models.ForeignKey("Category",on_delete=models.CASCADE)
比如想要获取文章标题中包含”hello”的所有的分类。那么可以通过以下代码来实现:
categories = Category.object.filter(article__title__contains("hello"))