1. 程式人生 > >Django中的查詢操作

Django中的查詢操作

查詢是資料庫操作中一個非常重要的技術。查詢一般就是使用filter、exclude以及get三個方法來實現。我們可以在呼叫這些方法的時候傳遞不同的引數來實現查詢需求。在ORM層面,這些查詢條件都是使用field+__+condition的方式來使用的。以下將那些常用的查詢條件來一一解釋。

準備工作:

新建一個專案,配置settings檔案連線至mysql資料庫,
新建一個front的app,將app新增至settings中,然後在article中新建一個模型

from django.db import models

# Create your models here.
class Article(models.Model): title = models.CharField(max_length=200) content = models.TextField() class Meta: # 自定義資料表名稱 db_table = 'article'

然後makemigrations後在migrate,然後我們開啟navicat,找到article這個表,我們手動新增幾條資訊進去,以便後面測試。例如,我手動新增的4條資訊
在這裡插入圖片描述

查詢條件

1. exact:

使用方法,在屬性名後面
在front這個app中的views中新增一個函式:

from django.shortcuts import render
from django.http import HttpResponse
from . import models

# Create your views here.
def index(request):
    article1 = models.Article.objects.filter(id__exact = 1)
    article2 = models.Article.objects.get(id__exact=1)
    print(article1)
    print(article2)
    return
HttpResponse('success')

然後執行專案,我們可以發現打印出來的東西是不一樣的,用filter方法返回的是一個QuerySet的物件,get返回的就是一個我們能直接使用的物件,
在這裡插入圖片描述

到這裡我們可以發現,以前我們使用這兩個方法獲取物件的時候,直接使用的時get(id=1)filter(id=1)就直接得到了物件,為什麼還需要加一個__exact呢。
起始__exact是預設的選項,在我們使用get(id=1)就是相當於get(id__exact=1),所以我們以後也不用寫__exact,但是我們也需要了解一下。

我們可以通過query這個屬性來檢視翻譯為sql語言的原始碼

def index(request):
    article1 = models.Article.objects.filter(id__exact = 1)
    article2 = models.Article.objects.get(id__exact=1)
    print(article1.query)
    print(article2.query)
    return HttpResponse('success')

然後執行專案會發現報錯了,是因為我們使用get()返回的物件是沒有query這個屬性的,filter()返回的才有這個屬性,所以我們只需要將print(article2.query)這句程式碼註釋掉就行了。
然後我們就能看到在控制檯看到article1 = models.Article.objects.filter(id__exact = 1)這句程式碼翻譯為sql原生語句的程式碼了

SELECT `article`.`id`, `article`.`title`, `article`.`content` FROM `article` WHERE `article`.`id` = 1

2. iexact:

iexact和exact的功能是差不多一樣的,我們可以使用query檢視一下sql原始碼,修改views中的程式碼

def index(request):
    article1 = models.Article.objects.filter(id__exact = 1)
    article2 = models.Article.objects.filter(id__iexact = 1)
    print(article1.query)
    print(article2.query)
    return HttpResponse('success')

執行專案檢視sql原始碼
在這裡插入圖片描述
可以看到只是後面的=變成了LIKE了,這就是iexact和exact的區別,所以exact和iexact的區別實際上就是LIKE和=的區別,在大部分collation=utf8_general_ci情況下都是一樣的(collation是資料庫的排序規則)。所以我們可以預設iexact和exact一樣的。

總結:

  • LIKE和=:大部分情況下都是等價的,只有少數情況下是不等價的。
  • exict和iexact:他們的區別其實就是LIKE和=的區別,因為exact會被翻譯成=,而iexact會被翻譯成LIKE。
  • 因為field__exact=xxx其實等價於filed=xxx,因此我們直接使用filed=xxx就可以了,並且因為大部分情況exactiexact又是等價的,因此我們以後直接使用field=xxx就可以了。
  • query可以用來檢視這個ORM查詢語句最終被翻譯成的SQL語句。但是query只能被用在QuerySet物件上,不能用在普通的ORM模型上。因此如果你的查詢語句是通過get來獲取資料的,那麼就不能使用query,因為get返回的是滿足條件的ORM模型,而不是QuerySet。如果你是通過filter等其他返回QuerySet的方法查詢的,那麼就可以使用query

3. contains和icontains:

查詢某個字串是否在指定的欄位中

這兩個的用法99%的情況下都是一樣的,區別只在與
contains大小寫敏感,而icontains大小寫不敏感。
示例程式碼:修改index中的程式碼

def index(request):
    # article1 = models.Article.objects.filter(id__exact = 1)
    # article2 = models.Article.objects.filter(id__iexact = 1)
    # print(article1.query)
    # print(article2.query)

    article1 = models.Article.objects.filter(id__contains=1)
    article2 = models.Article.objects.filter(id__icontains = 1)
    print(article1.query)
    print(article2.query)

    return HttpResponse('success')

然後執行我們就能檢視到sql語句了
在這裡插入圖片描述
contains:這個判斷條件會使用大小寫敏感,因此在被翻譯成SQL語句的時候,會使用like binary,而like binary就是使用大小寫敏感的。
icontains:這個判斷條件會使用大小寫不敏感,因此在被翻譯成SQL的時候,使用的是like,而likeMySQL層面就是不區分大小寫的。

我們可以看到在1的左右兩邊都有%意思就是在1的左右兩邊都可以與另外的字元,也就是123213這些都拿呢個被查找出來,所以我們一般不對id這個欄位進行containsicontains的判斷,而是對其他欄位。

contains和icontains:在被翻譯成SQL的時候使用的是%1%,就是隻要整個字串中出現了1都能過夠被找到,而iexact沒有百分號,那麼意味著只有完全相等的時候才會被匹配到。這就是containsexiact的區別。

4. in:可以直接指定某個欄位的是否在某個集合中。

修改index中的程式碼:

def index(request):
    # exact和iexact
    # article1 = models.Article.objects.filter(id__exact = 1)
    # article2 = models.Article.objects.filter(id__iexact = 1)
    # print(article1.query)
    # print(article2.query)

    # contains和icontains
    # article1 = models.Article.objects.filter(id__contains=1)
    # article2 = models.Article.objects.filter(id__icontains = 1)
    # print(article1.query)
    # print(article2.query)

    # in
    articles = models.Article.objects.filter(id__in=[1,2,3])
    for article in articles:
        print(article)
    return HttpResponse('success')

我們就能夠將id為1,2,3的資料查找出來,如果沒有的話,就返回一個空的QuerySet列表。
注意: 在這裡articles擁有query屬性,而article沒有。

為了接下來的測試,我們需要在models中新建一個模型,然後時喲個外來鍵。

from django.db import models

# Create your models here.
class category(models.Model):
    name = models.CharField(max_length=100)

    class Meta:
        # 自定義資料表名稱
        db_table = 'category'


class Article(models.Model):
    title = models.CharField(max_length=200)
    content = models.TextField()
    category = models.ForeignKey('category',on_delete=models.CASCADE,null=True)

    class Meta:
        # 自定義資料表名稱
        db_table = 'article'

然後執行makemigrations後再migrate。然後開啟navicat,在category中新增一條資訊,article中的外來鍵也增加資訊。

在這裡插入圖片描述
在這裡插入圖片描述
接下來我們要根據已經知道的文章id來查詢所屬分類。
修改views中的程式碼

def index(request):
    # exact和iexact
    # article1 = models.Article.objects.filter(id__exact = 1)
    # article2 = models.Article.objects.filter(id__iexact = 1)
    # print(article1.query)
    # print(article2.query)

    # contains和icontains
    # article1 = models.Article.objects.filter(id__contains=1)
    # article2 = models.Article.objects.filter(id__icontains = 1)
    # print(article1.query)
    # print(article2.query)

    # in
    # articles = models.Article.objects.filter(id__in=[1,2,3])
    # print(articles.query)
    # for article in articles:
    #     print(article)

    # 查詢id為1,2,3的文章的分類
    categories = models.category.objects.filter(article__id__in = [1,2,3])
    print(categories)
    return HttpResponse('success')

這樣我們就將article的id為[1,2,3]的種類查找了出來,當然,在這個地方article__id我們也是可以使用related_query_name來另外取一個名字的。這裡就不做演示了。
注意:

  1. 反向查詢是將模型名字小寫化。比如article__in。可以通過related_query_name來指定自己的方式,而不使用預設的方式。
  2. 反向引用是將模型名字小寫化,然後再加上_set,比如article_set,可以通過related_name來指定自己的方式,而不是用預設的方式。

我們也可以將article__id__in中的__id去掉,然後就會預設使用__id來查詢。

in不僅僅可以指定列表/元組,還可以為QuerySet

5. gt、gte、lt、lte:

代表的是大於、大於等於、小於、小於等於的條件。
示例程式碼:
修改views中的函式程式碼:

def index(request):
    # exact和iexact
    # article1 = models.Article.objects.filter(id__exact = 1)
    # article2 = models.Article.objects.filter(id__iexact = 1)
    # print(article1.query)
    # print(article2.query)

    # contains和icontains
    # article1 = models.Article.objects.filter(id__contains=1)
    # article2 = models.Article.objects.filter(id__icontains = 1)
    # print(article1.query)
    # print(article2.query)

    # in
    # articles = models.Article.objects.filter(id__in=[1,2,3])
    # print(articles.query)
    # for article in articles:
    #     print(article)

    # 查詢id為1,2,3的文章的分類
    # categories = models.category.objects.filter(articles__in = [1,2,3])
    # print(categories)

    articles = models.Article.objects.filter(id__gt=2)
    print(articles)
    return HttpResponse('success')

這樣我們就能取的article中id>2的文章了
其它幾個可自行嘗試。

6. startswith、istartswith、endswith、iendswith:

表示以某個值開始,不區分大小寫的以某個值開始、以某個值結束、不區分大小寫的以某個值結束。
例如:

    articles = Article.objects.filter(title__startwith="hello")
    articles = Article.objects.filter(title__iendswith="hello")
    articles = Article.objects.filter(title__endswith="hello")
    articles = Article.objects.filter(title__iendswith="hello")

接下來時對時間的查詢相關操作,所以首先我們應該先去修改一下我們的models,新增一個create_time

from django.db import models

# Create your models here.
class category(models.Model):
    name = models.CharField(max_length=100)

    class Meta:
        # 自定義資料表名稱
        db_table = 'category'


class Article(models.Model):
    title = models.CharField(max_length=200)
    content = models.TextField()
    category = models.ForeignKey('category',on_delete=models.CASCADE,null=True,related_query_name='articles')
    create_time = models.DateTimeField(auto_now_add=True,null=True)

    class Meta:
        # 自定義資料表名稱
        db_table = 'article'

然後在makemigratios後在migrate,如果遇到報錯就將資料庫中的所有表都刪除了,然後再將app下的migrations下除了__init__.py以外 的檔案全部刪除了,然後重新執行makemigrations後在migrate。
然後我們在views中新定義一個函式index1(),然後新增對映。

from datetime import datetime

def index1(request):
    start_time = datetime(year=2018,month=11,day=2,hour=16,minute=0,second=0)
    end_time = datetime(year=2018,month=11,day=2,hour=17,minute=0,second=0)
    articles = models.Article.objects.filter(create_time__range=(start_time,end_time))
    print(articles)
    print(articles.query)

    return HttpResponse('sucess')

這樣,我們就是用range條件查詢到了所需要的資訊。

但是,我們也收到了一個警告,它說我們的時間是一個navie型別的時間,所以我們需要將它轉化為aware的時間。
所以修改程式碼:

from django.utils.timezone import make_aware

def index1(request):
    start_time = make_aware(datetime(year=2018,month=11,day=2,hour=16,minute=0,second=0))
    end_time = make_aware(datetime(year=2018,month=11,day=2,hour=17,minute=0,second=0))
    articles = models.Article.objects.filter(create_time__range=(start_time,end_time))
    print(articles)
    print(articles.query)

    return HttpResponse('sucess')

這樣,就不會有警告生成了。

7. date:

用年月日來進行過濾。
示例程式碼,新增一個index2的函式:

def index2(request):
    articles = models.Article.objects.filter(create_time__date = datetime(year=2018,month=11,day=2))
    print(articles.query)
    print(articles)
    return HttpResponse('success')

然後新增對映,然後我們發現並沒有查找出結果出來,但是我們是剛才新增的資訊,時間也是設定為剛才的,為什麼沒有資訊呢。
原因是因為MySQL預設是沒有儲存時區相關的資訊,但是在執行sql語句時我們發現對時區進行了裝換,所以時不會得到我們想要的結果的。因此我們需要下載一些時區表的檔案,然後新增到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中。

然後對上面的程式碼不需要進行改動,也能執行成功了

8. year/month/day:表示根據年/月/日進行查詢。

示例程式碼:

articles = models.Article.objects.filter(create_time__year=2018)

我們還可以對年進行更復雜的操作,例如

articles = models.Article.objects.filter(pub_date__year__gte=2017)

9. week_day:

根據星期來進行查詢。1表示星期天,7表示星期六,2-6代表的是星期一到星期五。比如要查詢星期三的所有文章,那麼可以通過以下程式碼來實現:

articles = models.Article.objects.filter(create_time__week_day=4)

10.time:

根據時間進行查詢。
如果要具體到秒,一般比較難匹配到,因為資料庫中秒後面還有小數。可以使用區間的方式來進行查詢。區間使用range條件。比如想要獲取17時/10分/27-28秒之間的文章,那麼可以通過以下程式碼來實現:

from datetime import datetime,time

 		start_time = time(hour=17,minute=10,second=27)
        end_time = time(hour=17,minute=10,second=28)
        articles = models.Article.objects.filter(create_time__time__range=(start_time,end_time))

更多的關於時間的過濾,請參考Django官方文件:https://docs.djangoproject.com/en/2.1/ref/models/querysets/#range

11.isnull:

根據值是否為空進行查詢。
示例程式碼:

# 查詢title為空的
articles = models.Article.objects.filter(create_time__isnull=True)
# 查詢title不為空的
articles = models.Article.objects.filter(create_time__isnull=False)

12.regex和iregex:

大小寫敏感和大小寫不敏感的正則表示式。示例程式碼如下

articles = models.Article.objects.filter(title__regex=r"^三國")

當然,我們還可以進行復雜的正則表示式,這裡就不細說了。

想深入學習django的可以看一下這個視訊:超詳細講解Django打造大型企業官網