Django2.0-db(6)-查詢操作-查詢條件
查詢操作
查詢是資料庫操作中一個非常重要的技術。查詢一般就是使用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))
-
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"))