在Django中ORM访问数据库查找时,会用到一些带双下划线的操作,类似Entry.objects.filter(id__gt=4)这种。
本文记录了双下划线之后的“方法”的作用和用法。
学习了下官方文档,在这里总结记录下。若有不清晰的,可参考官方文档:Django2.1官方文档
目录
字段查找是指定SQL WHERE子句的内容的方式,他们用在filter(),exclude()和get()这些QuerySet方法。
Django的内置查找如下,也可以编写自定义查找。
exact
为了方便起见,当没有提供查找类型时(比如 Entry.objects.get(id=12) ),查找类型就假定为exact。
精确匹配。如果为匹配提供的值是None,则会被解读为SQL中的NULL值,例如:
Entry.objects.get(id__exact=12)
Entry.objects.get(id__exact=None)
相当于SQL语句:
SELECT ... WHERE id = 12;
SELECT ... WHERE id IS NULL;
MySQL 匹配
在MySQL中,数据库表的名为“collation”的设置来确定exact匹配是否区分大小写。 这是一个数据库设置,而不是Django设置。 可以将MySQL表配置为区分大小写,但需要进行一些权衡。 有关这方面的更多信息,查看Collation settings。
iexact
不区分大小写的精确匹配。例如:
Blog.objects.get(name__iexact='beatles blog')
Blog.objects.get(name__iexact=None)
相当于SQL语句:
SELECT ... WHERE name ILIKE 'beatles blog';
SELECT ... WHERE name IS NULL;
'Beatles Blog', 'beatles blog', 'BeAtLes BLog'等等都会被上面第一个查询匹配到。
contains
区分大小写的包含匹配。例如:
Entry.objects.get(headline__contains='Lennon')
相当于SQL语句:
SELECT ... WHERE headline LIKE '%Lennon%';
这会匹配到‘Lennon honored today’但是不会匹配到‘lennon honored today’。(因为区分大小写)
SQLlite 用户
SQLite不支持区分大小写的 “LIKE”声明,所以对于SQLite来说,contains和icontains是一样的。
icontains
不区分大小写的包含匹配。使用方法见contains。
in
在一个给定的可迭代对象中,通常是一个列表,元组,或queryset,它并不常用,但是接受字符串(可迭代)。例如:
Entry.objects.filter(id__in=[1, 3, 4])
Entry.objects.filter(headline__in='abc')
相当于SQL语句:
SELECT ... WHERE id IN (1, 3, 4);
SELECT ... WHERE headline IN ('a', 'b', 'c');
如果在in操作中传入values()或者value_list()的queryset结果,必须确保这个结果只提取了一个字段。例如:
# 正确操作,只提取了一个“name”
inner_qs = Blog.objects.filter(name__contains='Ch').values('name')
entries = Entry.objects.filter(blog__name__in=inner_qs)
# 错误操作,提取了“name”和“id”两个字段,会抛出TypeError
inner_qs = Blog.objects.filter(name__contains='Ch').values('name', 'id')
entries = Entry.objects.filter(blog__name__in=inner_qs)
性能考虑:
请谨慎使用嵌套查询并了解数据库服务器的性能特征(如果有疑问,请使用基准测试!)。一些数据库后端,尤其是MySQL,不能很好地优化嵌套查询。在这些情况下,提取值列表然后将其传递到第二个查询更有效。也就是说,执行两个查询而不是一个:
values = Blog.objects.filter( name__contains='Cheddar').values_list('pk', flat=True) entries = Entry.objects.filter(blog__in=list(values))
注意:list()会强制执行第一个查询,没有它,会变成嵌套查询,因为QuerySet是懒惰的。
gt
Greater than. (大于)
例如:
Entry.objects.filter(id__gt=4)
# 即
SELECT ... WHERE id > 4;
gte
Greater than or eaual to.(大于等于)
lt
Less than. (小于)
lte
Less than or equal to. (小于等于)
startswith
以。。。开头(区分大小写)
istartswith
以。。。开头(不区分大小写)
endswith
以。。。结尾(区分大小写)
iendswith
以。。。结尾(不区分大小写)
range
范围过滤(包含边界)。例如:
import datetime
start_date = datetime.date(2005, 1, 1)
end_date = datetime.date(2005, 3, 31)
Entry.objects.filter(pub_date__range=(start_date, end_date))
# 相当于SQL
SELECT ... WHERE pun_date BETWEEN '2005-01-01' and '2005-03-31';
只要在SQL中能使用BETWEEN的地方,都能使用range——对时间,数字,甚至字符。
Warning!
Filter一个DateTimeField字段会不包括时间范围的最后一天,因为界限被解析为“给定日期的0点”,如果pub_date是一个DateTImeField类型,上面的Python代码翻译成SQL会是:
SELECT ... WHERE pub_date BETWEEN '2005-01-01 00:00:00' and '2005-03-31 00:00:00';
也就是说,不能混用date和datetime,他们有差别。
date
将datetime类型的字段转换为date,允许链接其他字段查找,得到一个date类型的值。例如:
Entry.objects.filter(pub_date__date=datetime.date(2005, 1, 1))
Entry.objects.filter(pub_date__date__gt=datetime.date(2005, 1, 1))
(此查找不包含等效的SQL代码片段,因为相关查询的实现因不同的数据库引擎而异。)
当USE_TZ为True时,字段将在筛选之前转换为当前时区。
year
对于date和datetime字段,精确地匹配年份。允许链接其他字段查找,筛选时传入一个int型年份。例如:
Entry.objects.filter(pub_date__year=2005)
Entry.objects.filter(pub_date__year__gte=2005)
# 相当于SQL
SELECT ... WHERE pub_date BETWEEN '2005-01-01' AND '2005-12-31';
SELECT ... WHERE pub_date >= '2005-01-01';
(具体的SQL语法取决于具体的数据库引擎。)
当USE_TZ为True时,字段将在筛选之前转换为当前时区。
month
和year作用相同,筛选时传入一个int型月份(1-12)。
Entry.objects.filter(pub_date__month=12)
Entry.objects.filter(pub_date__month__gte=6)
# 相当于SQL
SELECT ... WHERE EXTRACT('month' FROM pub_date) = '12';
SELECT ... WHERE EXTRACT('month' FROM pub_date) >= '6';
day
和year以及month作用相同。
Entry.objects.filter(pub_date__day=3)会筛选出来若干条日期为3的记录,例如一月三号,二月三号等等。
week
对于date和datetime字段,返回周数(1-52 or 53)。
Entry.objects.filter(pub_date__week=52)会筛选出日期为一年中第52周的记录。
week_day
对于date和datetime字段作一个“一周中的哪一天”匹配。
筛选时传入一个int型值代表星期几(1-7, 1代表Sunday,7代表Saturday)。
Entry.objects.filter(pub_date__week_day=2) 这会筛选出所有当天日期是星期一的记录,和月份年份都无关。
quarter(Django2.0新增)
对于date和datetime字段,匹配季度(1-4)。
Entry.objects.filter(pub_date__quarter=2),日期在第二个季度(4月1日至6月30日)的所有记录。
time
将datetime字段转换为time,筛选时传入一个datetime.time值。例如:
# 筛选时间为14:30的记录
Entry.objects.filter(pub_date__time=datetime.time(14, 30))
# 筛选时间在8:00到17:00的记录
Entry.objects.filter(pub_date__time__range=(datetime.time(8), datetime.time(17)))
hour
对于datetime和time字段,匹配具体的“时”,筛选时传入一个int型数字(0-23)。
minute
对于datetime和time字段,匹配具体的“时”,筛选时传入一个int型数字(0-59)。
second
对于datetime和time字段,匹配具体的“秒”,筛选时传入一个int型数字(0-59)。
isnull
True或False,分别对应于IS NULL和IS NOT NULL的SQL查询。
regex
区分大小写的正则表达式匹配。
正则表达式的语法取决于正在使用的数据库,例如SQLite,它没有内置的正则表达式支持,这个功能就有用户定义的REGEXP函数提供,并且正则表达式语法和Python的re模块相同。例如:
Entry.objects.get(title__regex=r'^(An?|The) +')
# 对应SQL
SELECT ... WHERE title REGEXP BINARY '^(An?|The) +'; # MySQL
SELECT ... WHERE REGEXP_LIKE(title, '^(An?|The) +', 'c'); # Oracle
SELECT ... WHERE title ~ '^(An?|The) +'; # PostgreSQL
SELECT ... WHERE title REGEXP '^(An?|The) +'; # SQLite
iregex
不区分大小写的正则表达式匹配。
以上。