django學習(三)

AndreasZhou發表於2020-09-06

1.單表操作和測試環境的準備

我們先對單表查詢做一個總結和回顧,並進行進一步的學習和交流。我們在我們的應用的models.py檔案下面書寫user類。如下所示,然後用資料庫遷移,在mysql資料庫中生成表。然後進行資料庫表的單表查詢的操作。

# models.py檔案
class User(models.Model):
    username = models.CharField(max_length=32, verbose_name='使用者名稱')
    password = models.CharField(max_length=32, verbose_name='密碼')
    age = models.IntegerField(verbose_name='年齡')
    info = models.CharField(max_length=256, verbose_name='使用者描述')
    salary = models.DecimalField(max_digits=8, decimal_places=2, verbose_name='使用者的薪水')
    register_time = models.DateField(default='2020-8-28',verbose_name='註冊時間')
    
     '''
    這裡補充一個知識點:DateField和DateTimeField有兩個重要的引數
    這兩個引數分別是:
    auto_now:每次運算元據的時候,該欄位會自動將當前時間更新
    auto_now_add:在建立資料的時候會自動將當前建立時間記錄下來,之後只要不修改,那麼時間一直不變。
    '''
        
    def __str__(self):
        return '%s' % self.username

我們之前學了單表操作的知識,在這裡我們鞏固一下,並且進行更深入一步的學習。我們之前測試我們的models檔案是否正常,資料庫遷移是否正確,我們都是採用的是前後端互動的技術實現測試的。但是我們知道django的orm語句是比較多的,我們每次對於資料庫的操作,orm語句的操作都需要前後端來測試的話,那麼這個是十分的麻煩。在這裡我們在學習單表操作的同時,也引進了關於測試環境準備的知識點,那麼以後我們寫好的.py檔案的程式碼,我們可以放到我們的測試環境中,即test.py檔案中進行測試。指令碼程式碼無論是寫在自己應用下的test.py中,還是自己單獨開設.py檔案都可以。

測試環境的準備:去manager.py中copy這個檔案的前面四行程式碼,然後自己寫兩行。測試檔案是通用的一個功能,不一定是在應用下的test.py檔案,自己也可以新建測試的.py檔案,例如mytest.py檔案。

在這個程式碼塊下面就可以測試django裡面的單個py檔案了。所有的程式碼都要寫在測試環境的下面,因為測試環境還沒有準備好,我們必須放在測試環境的下面,等待測試環境準備好之後,我們才可以開始測試。所有的程式碼必須等待環境準備好之後才可以開始書寫。

# 測試環境的搭建
import os
def main():
    os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'model_mysite.settings')
    import django
    django.setup()
if __name__ == '__main__':
    main()

現在我們在我們的測試環境下來測試、使用、學習和擴充套件我們的單表查詢。

 	# 這個匯入包的操作必須放在測試環境之後,即django.setup()之後,不然會出現報錯。
    from app01 import models
    # 單表操作--增
    # 用create()方法增加
    models.User.objects.create(username='zhouqian', password='123456', age=22, info='可甜、可憐、可愛、可傻', salary=12500,register_time='2020-6-26')
    
    # 用類的方式增加,save()方法
    user_obj = models.User(username='AndreasZhou', password='456789', age=22, info='可騷、可變態、博多、倉老師', salary=22500,register_time='2020-6-28')
    user_obj.save()

    # 單表操作--刪
    models.User.objects.filter(id=1).delete()
    # 查詢出某一個資料,然後將這個資料刪除,直接呼叫deldete()方法

    # 單表操作--改
    # 批量的更新操作。這個其實是所謂的批量的更新的操作,但是因為我們平常用的id值唯一的主鍵,所以我們這裡更新的其實也是一個資料
    models.User.objects.filter(id=2).update(username='ZhouQian')
    # 單獨的更新操作
    user_obj = models.User.objects.filter(id=3).first()
    user_obj.username = 'ANDREASZHOU'
    user_obj.save()

    # 單表操作--查
    res = models.User.objects.filter(id=2)  # <QuerySet [<User: User object (2)>]>  類似於列表套物件的形式 <QuerySet [<User: ZhouQian>]>
    print(res)

    user_obj = models.User.objects.filter(id=2).first()
    print(user_obj) # User object (2) ZhouQian

    res = models.User.objects.all()
    print(res)  # <QuerySet [<User: User object (2)>, <User: User object (3)>]> <QuerySet [<User: ZhouQian>, <User: ANDREASZHOU>]>
    
    '''
    在這裡我們需要補充一個知識點就是pk的使用,pk會自動查詢到當前表的主鍵欄位,指代的就是當前表的主鍵欄位,用了pk之後,你就不需要指代當前表的主鍵欄位到底叫什麼了。uid,pid,sid...。所以我們在平時的使用的時候,我們用pk用的比較的頻繁。所以我們後面按照主鍵查詢,我們就按照pk來使用就好了。
    '''

2.必知必會13條(重點)

1)all():查詢所有

res = models.User.objects.all() # 得到的是一個查詢集queryset,類似於列表裡面套資料物件
print(res)  # <QuerySet [<User: User object (2)>, <User: User object (3)>]> <QuerySet [<User: ZhouQian>, <User: ANDREASZHOU>]>

2)filter():帶有過濾條件的查詢

res = models.User.objects.filter(id=2)  # <QuerySet [<User: User object (2)>]>  類似於列表套物件的形式 <QuerySet [<User: ZhouQian>]>
print(res)# 獲取的也是queryset,類似於列表套資料物件
    
user_obj = models.User.objects.filter(id=2).first()
print(user_obj) # User object (2) ZhouQian  列印出來的直接是一個物件。因為這裡顯示zhouqian表示的是呼叫了類中的__str__方法了。

3)get():直接拿資料物件,但是條件不存在那麼會直接報錯,不推薦使用。

user_obj = models.User.objects.get(id=2)
print(user_obj) # 得到的是User object(2)  ZhouQian

user_obj = models.User.objects.get(id=4) # app01.models.DoesNotExist: User matching query does not exist.  查詢不存在,所以這裡報錯,其實我們的get方法是不推薦使用的,完全被filter方法給替代了。
print(user_obj)
    
'''
get方法返回的直接就是當前的資料物件,但是該方法不推薦使用,一旦資料不存在該方法會直接報錯。
而filter則不會報錯,所以我們還是用filter
'''

4)first():拿到queryset裡面的第一個元素,即第一個資料物件(queryset是列表套資料物件)

res = models.User.objects.filter(id=2)  # <QuerySet [<User: User object (2)>]>  類似於列表套物件的形式 <QuerySet [<User: ZhouQian>]>
print(res)

user_obj = models.User.objects.filter(id=2).first()
print(user_obj) # User object (2) ZhouQian

5)last():拿到queryset裡面的最後一個元素,即最後一個資料物件(queryset是列表套資料物件)

res = models.User.objects.filter(id=2)  # <QuerySet [<User: User object (2)>]>  類似於列表套物件的形式 <QuerySet [<User: ZhouQian>]>
print(res)

user_obj = models.User.objects.filter(id=2).last()
print(user_obj) # User object (2) ZhouQian

user_obj = models.User.objects.all().last()
print(user_obj) # ANDREASZHOU

6)values():可以指定的獲取的資料欄位 select name,age from ..... 類似於列表套字典

res = models.User.objects.values('name','age')
# 類似於sql語句中的select name,age from User; 
print(res)
# 列印出來的結果是一個queryset,這裡的queryset我們可以看做是列表套字典的形式,返回結果我們是需要記憶的。記住這是一個返回結果queryset,列表裡面套字典的形式,字典的關鍵字是name和age。
# select username,password from User;
res = models.User.objects.values('username','password')
print(res)

7)values_list():可以指定的獲取的資料欄位 select name,age from ..... 類似於列表套元組

res = models.User.objects.values('name','age')
print(res)
# 列印出來的結果是一個queryset,這裡的queryset我們可以看做是列表套元組的形式,返回結果我們是需要記憶的。記住返回的是一個queryset,裡面是列表套元組的形式。
# select username,password from User;
res = models.User.objects.values('username','password')
print(res)
print(res.query)
# 在這裡我們補充一個知識點
'''
檢視內部封裝的sql語句
上述檢視sql語句的方式,只能用於QuerySet物件
只有queryset物件才能夠點選query檢視內部的sql語句
'''

8)distinct():去重

res = models.User.objects.values('username','age').distinct()
print(res)
select distinct username,age from User;
"""
去重一定要是一模一樣的資料,如果帶有主鍵那麼肯定不一樣,你在往後的查詢中一定不要忽略主鍵
"""

9)order_by():排序

res = models.User.objects.order_by('age') # 預設是升序
print(res)

res = models.User.objects.order_by('-age') # 現在是降序
print(age)

res = models.User.objects.all().order_by('age') # 升序
print(res)

10)reverse():反轉,先排序,才可以反轉,是建立在排序的基礎上的

res = models.User.objects.all()
res1 = models.User.objects.order_by('age').reverse()
print(res,res1)

11)count():統計數量

res = models.User.objects.count()
print(res)

12)exclude():除了什麼之外,排除在外。

res = models.User.objects.exclude(name='jason')
print(res)

13)exists():判斷是否存在,返回的是布林值,基本用不到因為資料本身就自帶布林值。

res=models.User.objects.filter(pk=10).exists()
print(res)

3.神器的雙下劃線查詢

1.我們要你幫我查出年齡大於35歲的資料

# 我們寫的sql語句是:
select * from user where age >35
res = models.User.objects.filter(age__gt=35)
print(res) # <QuerySet [<User: az>]>  返回的是一個字典,列表裡面套資料物件

2.我們要你幫我查出年齡小於35歲的資料

select * from user where age < 35
res = models.User.objects.filter(age__lt=35)
print(res) # <QuerySet [<User: ZhouQian>, <User: ANDREASZHOU>]>  返回的是一個字典,列表裡面套資料物件。

3.我們要你幫我查出年齡大於等於35歲的資料

select * from user where age >=35;
res = models.User.objects.filter(age__gte=35)----->這裡的g是greater,t是than,e是equal
print(res)  # <QuerySet [<User: ak>, <User: az>]>。返回的是一個queryset,類似於列表裡面套資料物件。

4.我們要你幫我查出年齡小於等於35歲的資料

select * from user where age <=35;
res = models.User.objects.filter(age__lte=35)
print(res)  # <QuerySet [<User: ZhouQian>, <User: ANDREASZHOU>, <User: ak>]>返回的是一個queryset,類似於列表裡面套資料物件。

5.我們要你幫我查出年齡是18或者35或者38

select * from user where age in (18,35,38);
res = models.User.objects.filter(age__in=(18, 35, 38))
print(res)  # <QuerySet [<User: ak>, <User: az>]>

6.我們要你幫我查出年齡在18到48歲之間的,首位都要

res = models.User.objects.filter(age__range=[18, 48])
print(res)  # <QuerySet [<User: ZhouQian>, <User: ANDREASZHOU>, <User: ak>, <User: az>]>返回的是一個queryset,類似於列表裡面套資料物件。

7.我們要你幫我查出名字裡面含有n的資料,模糊查詢,區分大小寫

# 預設是區分大小寫的
res = models.User.objects.filter(username__contains='n')
print(res)  # <QuerySet [<User: ZhouQian>]> 返回的是一個queryset,類似於列表裡面套資料物件。

8.我們要你幫我查出名字裡面含有n的資料,模糊查詢,忽略大小寫

 # 忽略大小寫
res = models.User.objects.filter(username__icontains='n')
print(res)  # <QuerySet [<User: ZhouQian>, <User: ANDREASZHOU>]> 返回的是一個queryset,類似於列表裡面套資料物件。

9.我們要你幫我查出名字以Z為開頭

res = models.User.objects.filter(username__startswith='Z')
print(res)  # <QuerySet [<User: ZhouQian>]> 返回的是一個queryset,類似於列表裡面套資料物件。

10.我們要你幫我查出名字以U為結尾

res = models.User.objects.filter(username__endswith='U')
print(res)  # <QuerySet [<User: ANDREASZHOU>]> 返回的是一個queryset,類似於列表裡面套資料物件。

11.我們要你幫我查出註冊時間的月

res = models.User.objects.filter(register_time__month=8)
print(res)  # <QuerySet [<User: ak>, <User: az>]>

12.我們要你幫我查出註冊時間的年

res = models.User.objects.filter(register_time__year=2020)
print(res)  # <QuerySet [<User: ZhouQian>, <User: ANDREASZHOU>, <User: ak>, <User: az>]>

4.多表查詢

我們在做多表查詢的之前需要做一些準備工作,下面就是我們準備工作要寫的模型類,將寫好的模型類遷移到資料庫中,這樣我們就可以開始做我們的多表的查詢。模型類的程式碼如下所示。

class Book(models.Model):
    title = models.CharField(max_length=32, verbose_name='書名')
    price = models.DecimalField(max_digits=8, decimal_places=2, verbose_name='價格')
    publish_date = models.DateField(auto_now_add=True, verbose_name='出版時間')
    publish = models.ForeignKey(to='Publish', verbose_name='書和出版社多對一',on_delete=models.CASCADE)
    author = models.ManyToManyField(to='Author', verbose_name='書和作者多對多')


class Publish(models.Model):
    name = models.CharField(max_length=32, verbose_name='出版社名稱')
    addr = models.CharField(max_length=32, verbose_name='出版社地址')
    email = models.EmailField(verbose_name='聯絡郵箱')
    # EmailField其實和varchar(254)是一樣的。
    # 該欄位型別不是給models看的,而是給我們後面會學到的校驗性元件看的。


class Author(models.Model):
    name = models.CharField(max_length=32, verbose_name='作者姓名')
    age = models.IntegerField(verbose_name='作者年齡')
    author_detail = models.OneToOneField(to='AuthorDetail', verbose_name='作者和作者詳情一對一',on_delete=models.CASCADE)


class AuthorDetail(models.Model):
    phone = models.CharField(max_length=32, verbose_name='手機號')
    # 電話號碼我們用BigIntegerField或者使用直接使用CharField,而不能用IntegerField。
    addr = models.CharField(max_length=32, verbose_name='作者地址')

5.外來鍵的增刪改

我們前面已經建立了表之間的關係,那麼我們現在可以使用orm框架實現外來鍵的增刪改查,因為查是最難的知識點,所以我們將查和增刪改分開講解,我們先講解增刪改,後面再講解查。

1)一對一、一對多外來鍵的增刪改

# 一對一、一對多外來鍵的增
# 增:
models.Book.objects.create(title='白蛇傳', price=255, publish_id=3) # 1.直接寫id的形式增加
publish_obj = models.Publish.objects.filter(pk=2).first() # 2.用物件的形式增加
models.Book.objects.create(title='武當派',price=388,publish=publish_obj)
# 一對一、一對多外來鍵的刪
# 刪:我們設定為級聯更新,級聯刪除
models.Publish.objects.filter(pk=1).delete()
# 一對一、一對多外來鍵的改
# 改:
models.Book.objects.filter(pk=7).update(publish_id=3) # 1.直接寫id的形式更新
publish_obj = models.Publish.objects.filter(pk=1).first() # 2.用物件的形式更新
models.Book.objects.filter(pk=7).update(publish=publish_obj)

2)多對多外來鍵增刪改清空

接下來我們學習多對多外來鍵的增刪改,我們依次用下面的程式碼來寫:

# 多對多外來鍵的增
# 直接寫id的形式增加
book_obj = models.Book.objects.filter(pk=7).first()
print(book_obj.author)  # app01.Author.None  就類似於你已經進入了第三張表的關係了
book_obj.author.add(1) # 書籍id為1的書籍繫結一個主鍵為1的作者

book_obj = models.Book.objects.filter(pk=8).first()
print(book_obj.author)  # app01.Author.None 就類似於你已經進入了第三張表的關係了
book_obj.author.add(1, 2, 3, 4)

# 用物件的形式增加
author_obj = models.Author.objects.filter(pk=2).first()
author_obj1 = models.Author.objects.filter(pk=3).first()
author_obj2 = models.Author.objects.filter(pk=4).first()
book_obj = models.Book.objects.filter(pk=4).first()
book_obj1 = models.Book.objects.filter(pk=5).first()
print(book_obj.author)  # app01.Author.None 就類似於你已經進入了第三張表的關係了
book_obj.author.add(author_obj, author_obj1, author_obj2)
book_obj1.author.add(author_obj)
'''
add()給第三張關係表新增資料,括號內既可以傳數字也可以傳物件,並且支援一個或者多個。
'''
# 多對多外來鍵的刪
# 直接寫id的形式刪除
book_obj = models.Book.objects.filter(pk=5).first()
print(book_obj.author)  # app01.Author.None 類似於進入了第三張表
book_obj.author.remove(2)
# 用物件的形式刪除
author_obj = models.Author.objects.filter(pk=2).first()
author_obj1 = models.Author.objects.filter(pk=3).first()
book_obj = models.Book.objects.filter(pk=4).first()
book_obj.author.remove(author_obj,author_obj1)
'''
remove():括號內既可以傳數字也可以傳物件,並且數字和物件可以有一個或者是多個。
'''
# 多對多外來鍵的改----記住這裡的set方法只可以傳入一個可迭代的物件,不然會報錯。可迭代物件的列表和元組都是可以的。
# 直接寫id的形式修改
book_obj = models.Book.objects.filter(pk=4).first()
book_obj.author.set([2]) # 這個括號內必須是一個可迭代物件,列表和元組都是可以的
book_obj.author.set((3,))
book_obj.author.set([2])
# 用物件的形式修改
book_obj = models.Book.objects.filter(pk=7).first()
author_obj = models.Author.objects.filter(pk=3).first()
author_obj1 = models.Author.objects.filter(pk=4).first()
book_obj.author.set([author_obj, author_obj1]) # 這裡面必須是一個可迭代物件,列表和元組都是可以的。
'''
set():set的括號內必須是一個可迭代的物件,可迭代物件裡面既可以是數字也可以是物件,並且數字和物件可以有一個或者是多個。
'''
# 多對多的清空---清空的clear()方法中不需要傳入任何的引數和物件。我們只要點這個方法就可以。
# 清空:在第三張關係表中清空某個書籍與作者的繫結關係
book_obj = models.Book.objects.filter(pk=7).first()
book_obj.author.clear()
'''
clear():括號內不需要加任何的引數
'''

6.正反向的概念

在我們講解多表查詢的時候,我們在這裡先學一個概念,這個概念就是關於正反向的概念,如果我們弄清楚了這個概念以後,我們的下面的學下就會表的更加的輕鬆。

外來鍵欄位在我手上,那麼我查你就是正向。外來鍵欄位如果不在我手上,那麼我查你就是反向。

  • book 》》》外來鍵欄位在書哪兒(正向)》》》publish

  • publish》》》外來鍵欄位在書哪兒(反向)》》》book

一對一,多對多正反向的判斷也是如此

7.多表查詢--基於物件的跨表查詢(子查詢)

正向查詢按欄位,如果我們發現結果是有多個的時候我們是欄位加上all(),如果發現結果只有一個的時候,我們是欄位即可。

反向查詢按表名小寫,如果發現結果有多個的時候我們是表名小寫加__set,並且還要加上all()。如果是單個的時候我們是直接表名小寫,也不用加__set和all()

1)正向查詢

1.查詢書籍主鍵為1的出版社名稱

書籍查出出版社  正向
book_obj = models.Book.objects.filter(pk=1).first()
res = book_obj.publish
print(res)  # 列印出來的是出版社物件
print(res.name)  # 北京出版社
print(res.addr) # 北京
這個res是一個Publish物件,我們列印呼叫了`__str__`方法,顯示出是物件:北京出版社。

2.查詢書籍主鍵為2的作者

書籍查作者是正向查詢
book_obj = models.Book.objects.fliter(pk=2).first()
res = book_obj.author
print(res) # 列印出來是app01.Author.None
res = book_obj.author.all() # 列印出很多個作者物件

3.查詢作者jason的電話號碼

作者到作者的詳情,所以是正向。所以我們這裡這樣寫:
author_obj = models.Author.objects.filter(name='jason').first()
res=author_obj.author_detail
print(res)  得到的結果是一個AuthorDetail物件。
print(res.phone)
print(res.addr)

上面我們得到了一個特點是:利用orm跨表查詢是比較的簡單。在書寫orm語句的時候跟寫sql語句是一樣的,不要企圖一次性將orm語句寫完,如果比較複雜,就寫一點看一點,分段寫

正向的時候什麼時候需要加.all()呢?當你的結果可能有多個的時候就需要加.all(),如果是一個則直接拿到資料物件。

2)反向查詢

4.查詢出版社是北京出版社出版的書籍

出版社查書   反向
publish_obj = models.Publish.objects.filter(name='北京出版社').first()
res = publish_obj.book_set    
print(res) # app01.Book.None
res = publish_obj.book_set.all()
print(res)   返回的是queryset,我們可以理解為列表裡面套物件

5.查詢作者是jason寫過的書

Author 到 book 是反向查詢
author_obj = models.Author.objects.filter(name='jason').first()
res = author_obj.book_set   app01.Book.None
res = author_obj.book_set.all()

6.查詢手機號是110的作者姓名

authoudetail 到 author 是反向查詢 AuthorDetail-->Book是反向的查詢
author_detail_obj = models.AuthorDetail.objects.filter(phone=110).first()
res = author_detail_obj.author
print(res.name)

基於物件反向查詢的時候的規律

當你的查詢結果可以有多個的時候,我們就必須表名小寫加上_set.all()

當你的查詢結果只有一個的時候,我們就必須是表名小寫,不需要加_set.all()

8.多表查詢--基於雙下劃線的跨表查詢(聯表查詢)

例子1.查詢書籍主鍵為1的出版社名稱(一行程式碼搞定)

select Publish.name from (select * from Book,Publish where publish_id = Publish.id ) as Book_Publish where Book.id =1;

1.查詢jason的手機號和作者姓名

正向

res=models.Author.objects.filter(name='json').values('author_detail__phone',‘name’)
print(res)  # query_set:類似於列表裡面套字典

反向

res = model.AuthorDetail.objects.filter(author__name='jason')   拿作者姓名jason的作者詳情
res = model.AuthorDetail.objects.filter(author__name='jason').values('phone','author__name')
print(res)

2.查詢書籍主鍵為1的出版社名稱和書的名字

正向

# 書籍查出版社是正向
res = models.Book.objects.filter(pk=1).values('publish__name','title')
print(res)

反向

res = models.Publish.objects.filter(book__id=1).values('name','book__title')
print(res)

3.查詢書籍主鍵為1的作者姓名

正向

res = models.Book.objects.filter(pk=1).values('author__name')
print(res) # queryset 類似於列表裡面套字典

反向

res= models.Author.objects.filter(book__pk=1).values('name')
print(res) # queryset 類似於列表裡面套字典

4.查詢書籍主鍵是1的作者的手機號

Book Author AuthorDetail

res = models.Book.objects.filter(pk=1).values('author__author_detail__phone')
print(res)
image-20200828165015961

你只要掌握了正反向的概念以及雙下劃線的查詢,那麼你就可以無限制的跨表查詢,聯表查詢。

9.聚合查詢

在這裡我們先學習一個東西,我們必須明白一個理論的知識。這個理論知識就是:只要是跟資料庫相關的模組基本上都在django.db.models裡面,如果上述沒有那麼應該在django.db裡面。聚合函式涉及的一般是如下:max,min,sum,count,avg,在django中,我們應該使用aggregate()。

from app01 import models
from django.db.models import Max,Min,Count,Sum,Avg

# 1.計算所有書的平均價格
res = models.Book.objects.aggregate(Avg('price'))
print(res)
# 2.上訴的方法統一使用
res=models.Book.objects.aggregate(Max('price'),Min('price'),Sum('price'),Count('pk'),Avg('price'))
print(res) # 返回的是字典
{'price__max': Decimal('388.00'), 'price__min': Decimal('255.00'), 'pk__count': 5, 'price__avg': Decimal('334.800000'), 'price__sum': Decimal('1674.00')}

10.分組查詢

分組在mysql中是group by,在我們的django是用annotate作為分組的使用。一般分組和聚合是在一起使用的。兩者相輔相成,也就是我們平時所說的分組聚合。

MySQL分組查詢有哪些特點呢?分組時候預設只能獲取到分組的依據,組內其他欄位無法直接獲取了。因為MySQL預設是嚴格模式,ONLY_FULL_GROUP_BY

我們看下面的幾個題目來深刻的體會和理解分組查詢的意義和內涵。

# 1.統計每一本書的作者的個數
    # models後面點什麼我們就按照什麼來分組,models.Book.objects.values('price').annotate()按照書的價格分組
    # res = models.Book.objects.annotate(author_number=Count('author__id')).values('title', 'author_number')
    # print(res)<QuerySet [<Book: Book object (3)>, <Book: Book object (4)>, <Book: Book object (5)>, <Book: Book object (7)>, <Book: Book object (8)>]>
    # print(res)
    # < QuerySet[{'title': '小斌張嘎', 'author_number': 0}, {'title': '白蛇傳', 'author_number': 1}, {'title': '武當派',
    #                                                                                         'author_number': 0}, {
    #                'title': '聊齋', 'author_number': 2}, {'title': '鬥破蒼穹', 'author_number': 4}] >

    
# 2.統計每一個出版社賣的最便宜的書的價格
    # res = models.Publish.objects.annotate(book_price=Min('book__price')).values('name', 'book_price')
    # print(res)
    # < QuerySet[{'name': '上海出版社', 'book_price': Decimal('388.00')}, {'name': '杭州出版社', 'book_price': Decimal('255.00')}, {
    #     'name': '北京出版社', 'book_price': Decimal('255.00')}, {'name': '嘉興出版社', 'book_price': None}] >

    
# 3.統計不止是一個作者的圖書
    # res = models.Book.objects.annotate(author_num=Count('author__pk')).filter(author_num__gt=1).values('title', 'author_num')
    # print(res) # <QuerySet [{'title': '聊齋', 'author_num': 2}, {'title': '鬥破蒼穹', 'author_num': 4}]>
    

# 4.查詢每個作者出的書的總價格
    # res = models.Author.objects.annotate(book_sum=Sum('book__price')).values('name', 'book_sum')
    # print(res)
    # < QuerySet[{'name': 'AndreasZhou', 'book_sum': Decimal('643.00')}, {'name': '週週', 'book_sum': Decimal('643.00')}, {
    #     'name': '憨憨', 'book_sum': Decimal('643.00')}, {'name': '周乾', 'book_sum': Decimal('388.00')}] >

在這裡我們補充一個知識點,這個知識點就是:只要你的orm語句得出來的結果還是一個QuerySet物件,那麼它就可以繼續無限制的點QuerySet物件封裝的方法。

還有一個知識點就是,如果我想要按照指定的欄位分組該如何處理呢?

models.Book.objects.values('price').annotate(),後續我們在寫專案的時候會使用到這個知識點。

11.F查詢

我們現在來講解一下什麼是F查詢,F查詢的用途是什麼呢?

F查詢:能夠幫助你直接獲取到列表中某個欄位對應的資料

1.查詢賣出數大於庫存數的書籍

from django.db.models import F
res = models.Book.objects.filter(maichu__gt=F('庫存'))
print(res)
# <QuerySet [<Book: Book object (3)>, <Book: Book object (7)>]>

2.將所有書籍的價格提升50塊

# 2.將所有書籍的價格提升50塊
from django.db.models import F
res = models.Book.objects.all().values('title', 'price')
print(res)
res = models.Book.objects.update(price=F('price') + 50)
print(res)

models.Book.objects.update(price=F('price')+50)
F('price'):表示獲取的是原來的價格

3.將所有書的名稱後面加上爆款兩個字(瞭解)

'''
在操作字串型別的資料的時候,F不能夠直接做到字串的拼接。
'''
from django.db.models.functions import concat
from django.db.models import Value
models.Book.objects.update(title=concat((F('title'),Value('爆款')))

12.Q查詢

在學習Q查詢之前,我們先來說明一個問題,filter預設只是支援使用的是與(and)關係。但是往往這樣一種關係是不能滿足於我們平時的使用。所以我們需要更多的邏輯關係,使得我們更加的操作方便,因此我們在這裡引入了一種查詢方式,這個查詢方式叫做Q查詢。

1.查詢賣出數大於100或者價格小於600的書籍

`res = models.Book.objects.filter(maichu__gt=100,price__lt=600)`

`print(res)`

'''

filter括號內多個引數是and關係,是我們想要的是或,非等關係,那我們怎麼辦呢?

'''

from django.db.models import Q

`res = models.Book.objects.filter(Q(maichu__gt=100,price__lt=600))` Q包裹,逗號分隔,還是and關係

`res = models.Book.objects.filter(Q(maichu__gt=100|price__lt=600))` Q包裹,|分隔,or關係

`res = models.Book.objects.filter(~Q(maichu__gt=100,price__lt=600))`   Q包裹,~分隔,not關係

`print(res)`

2.Q的高階用法 能夠將查詢條件左邊也變成字串的形式,而不再是變數名的形式。使用Q我們可以實現一個簡單的搜尋功能。

q = Q()
q.connector='or'
q.children.append(('maichu__gt',100))
q.children.append(('price__lt',600))
res=models.Book.objects.filter(q) # filter括號內也支援直接放q物件,預設還是and關係
print(res)

13.django中如何開啟事務

事務:我們知道事務有四個特性,這四個特性分別是:

原子性:不可分割的最小單位。

一致性:跟原子性是相輔相成的。

隔離性:事務之間是互相不干擾。

永續性:事務一旦確認是永久有效的。

事務的回滾:rollback

事務的確認:commit

from django.db import transaction
try:
    with transaction.atomic():
        # sql1
        # sql2
        # ...
        # 在with程式碼塊內書寫的所有orm操作都是同一個事務
except Exception as e:
    print(e)
print('執行其他操作')

14.orm中常用欄位以及引數

# 常用欄位
AutoField()
IntegerField()
CharField()
DecimalField()
EmailField()
DateField()
DateTimeField()
TimeField()
OneToOneField()
ManyToManyField()
ForeignField()
# 常用引數
null
unique
verbose_name
default
auto_now
auto_now_add
to
to_field
on_delete
max_length
choices
# 瞭解欄位
SmallIntegerField()
PositiveSmallIntegerField()
PositiveIntegerField()
PositiveIntegerField()
BooleanField()
# 瞭解引數
related_name
through
through_fields
db_table

參考:https://www.cnblogs.com/liuqingzheng/articles/9627915.html

15.choices引數(資料庫欄位設計常見)

'''
使用者表
	姓名
	學歷
	工作經驗
	是否生子
	是否結婚
	客戶來源
	...
針對某個可以列舉完全的可能性欄位,我們該如何儲存?

只要某個欄位的可能性是可以列舉完全的,那麼一般情況下都會採用choices引數
'''
class User(models.Model):
    username = models.CharField(max_length=32,verbose_name='使用者名稱')
    age = models.IntegerField(verbose_name='年齡')
    gender_choices = (
    	(1,'男'),
        (2,'女'),
        (3,'其他'),
    )
    gender = models.IntegerField(choices=gender_choices)
    '''
    改gender欄位存的還是數字,但是如果存的數字在上面元組列舉的範圍之內,那麼可以非常輕鬆的獲取到數字對應的真正內容。
    1.gender欄位存的數字不在上述元組列舉的範圍內容
    2.如果在的話,如何獲取對應的中文資訊
    '''
from app01 import models
models.User.objects.create(username='zhouqian',age=18,gender=1)
models.User.objects.create(username='zhoukun',age=28,gender=2)
models.User.objects.create(username='AndreasZhou',age=20,gender=3)
models.User.objects.create(username='thanzhou',age=18,gender=4)

user_obj = models.User.objects.filter(pk=1).first()
print(user_obj.gender)

user_obj = models.User.objects.filter(pk=1).first()
# 只要是choices引數的欄位,如果想要獲取對應資訊。固定寫法:get_欄位名_display()
print(user_obj.get_gender_display())

# 如果沒有對應關係,那麼欄位是什麼還是展示什麼
user_obj = models.User.objects.filter(pk=4)..first()
print(user_obj.get_gender_display()) # 4
gender_choices=(
	(1,'男'),
    (2,'女'),
    (3,'其他'),
)
gender = models.IntegerField(choices=gender_choices)


scores_choices = (
	('A','優秀'),
    ('B','良好'),
    ('C','及格'),
    ('D','不及格'),
)
# 保證欄位的型別跟列舉出來的元組的第一個資料型別一致即可。
score = models.CharField(choices=scores_choices,null=True)

16.多對多三種建立方式

# 系統自帶
class Book(models.Model):
    name = models.CharField(max_length=32)
    authors = models.ManyToManyField(to='Author')

class Author(models.Model):
    name = models.CharField(max_length=32)

'''
優點:程式碼不需要你寫,非常方便,還支援orm提供操作第三張關係表的方法,add(),remove(),clear(),set()
不足之處:第三張關係表的擴充套件性極差(沒有第三張表,沒有辦法額外新增欄位)
'''
# 純手動
class Book(models.Model):
    name = models.CharField(max_length=32)

class Author(models.Model):
    name = models.CharField(max_length=32)

class Book2Author(models.Model):
    book_id = models.ForeignKey(to='Book')
    author_id = models.ForeignKey(to='Author')
    
'''
優點:第三張表完全取決於你自己進行額外的擴充套件
不足之處:需要你自己手寫程式碼,寫的程式碼比較多。不能夠使用orm提供的簡單的方法,不建議你用該方式。
'''
    
# 半自動
class Book(models.Model):
    name = models.CharField(max_length=32)
    authors = models.ManyToManyField(to='Author',
                                    through='Book2Author'
                                    through_fields=('book','author')
                                    )
    
    
class Author(models.Model):
    name = models.CharField(max_length=32)
    

class Book2Author(models.Models):
    book = models.ForeignKey(to='Book')
    author = models.ForeignKey(to='Author')

    
'''
半自動:可以使用orm的正反向查詢,但是沒發使用add,set,remove,clear這四個方法

'''

總結:需要掌握全自動和半自動,為了擴充套件性更高,一般我們都會採用半自動(寫程式碼給自己留一條後)。

相關文章