Django模型層

阿麗米熱發表於2023-01-01

模型層與ORM

一、初步認識ORM

1.什麼是ORM

物件關係對映

能夠讓不會SQL語句的python程式設計師
使用python物件導向的語法來運算元據庫

優勢>>>:簡單方便快捷
劣勢>>>:效率可能會低

2.概念對接

類 		   表
物件 		   一條條資料
物件點名字	 資料獲取欄位對應值

3.基本操作(增刪改查)

models.UserInfo.objects.create()  # insert into 增
models.UserInfo.objects.filter().delete()  # delete from 刪
models.UserInfo.objects.filter().update()  # update 改
models.UserInfo.objects.filter()  # where 查
from app01 import models


class Info(models.Model):
    # 欄位名 = 欄位型別 + 約束條件
    id = models.AutoField(primary_key=True)
    name = models.CharField(max_length=32)
    age = models.IntergerField()

4.資料庫遷移命令

python38 manage.py makemigrations  # 將操作記錄到小本本上
python38 manage.py migirate  # 將操作同步到資料庫上
'''每次在models.py中與資料相關的程式碼 都需要執行資料庫遷移命令'''

二、ORM常用關鍵字

# 關鍵概覽
1.create()
2.filter()
3.first() last()
4.update()
5.delete()
6.all()
7.values()
8.values_list()
9.distinct()
10.order_by()
11.get()
12.exclude()
13.reverse()
14.count()
15.exists()
# 1.create()  建立資料並直接當前建立的資料物件
res = models.UserInfo.objects.create(name='almra', age=24)
# 2.filter() 根據條件篩選資料 結果是QuerySet [資料物件1,資料物件2]
res = models.UserInfo.objects.filter(name='amira')  # 括號沒有寫預設全部篩選 括號內支援多個條件 預設是and
# 3.first() last()  QuerySet支援索引取值但只是支援正數 並且orm不建議使用索引
res = models.UserInfo.objencts.filter()[1]  # 查詢到列表對應的資料 對應資料不存在會報錯
res = models.UserInfo.objects.filter().first()  # 查詢到列表第一個資料 對應資料不存在不會報錯返回None
# 4.update()  更新資料
res = models.UserInfo.objects.filetr().update()  # 批次更新
res = models.UserInfo.objects.filetr(id=1).update()  # 自定義更新
# 5.delete()  刪除資料(批次刪除)
models.UserInfo.objects.filter().delete()  # 批次刪除
models.UserInfo.objects.filter(id=1).delete()  # 單個刪除
# 6.all()  查詢所有資料 結果是QuerySet [資料物件1,資料物件2]
res = models.UserInfo.objects.all()
# 7.values()  根據指定欄位獲取資料    結果是QuerySet [{},{},{},{}]
res = models.UserInfo.objects.all().values('name')
res = models.UserInfo.objects.filter().values()
res = models.UserInfo.objects.values()
# 8.values_list()  根據指定欄位獲取資料 結果是QuerySet [(),(),(),()]
res = models.UserInfo.objects.all().values_list('name','age')
# 9.distinct()   去重 資料一定要一模一樣才可以 如果有主鍵肯定不行
res = models.UserInfo.objects.values('name','age').distinct()
# 10.order_by()  根據指定條件排序 預設是升序 欄位前面加負號就是降序
res = models.UserInfo.objects.all().order_by('age')
# 11.get()   根據條件篩選資料並直接獲取到資料物件  一旦條件不存在會直接報錯 不建議使用
res = models.UserInfo.objects.get(pk=1)
# 12.exclude()  取反操作
res = models.UserInfo.objects.exclude(pk=1)
# 13.reverse()  顛倒順序(被操作的物件必須是已經排過序的才可以)
res1 = models.UserInfo.objects.all().order_by('age').reverse()
# 14.count()  統計結果集中資料的個數
res = models.User.objects.all().count()
# 15.exists()  判斷結果集中是否含有資料 如果有則返回True 沒有則返回False
res = models.User.objects.all().exists()

三、ORM查詢SQL語句的方法

方式1:raw()

models.User.objects.raw('select * from app01_user;')

方式2:cursor()

from django.db import connection
cursor = connection.cursor()
cursor.execute('select name from app01_user;')
print(cursor.fetchall())

四、神奇的雙下劃線查詢

只要還是queryset物件就可以無限制的點queryset物件的方法
比如:queryset.filter().values().values_list().filter()....

1.查詢年齡大於18歲的使用者資料/年齡小於38歲的使用者資料

res = models.Users.objects.filter(age__gt=18)  # 大於18歲
res = models.Users.objects.filter(age__lt=38)  # 小於18歲

2.查詢年齡大於等於18歲的使用者資料/年齡小於等於38歲的使用者資料

res = models.Users.objects.filter(age__gte=18)  # 大於等於18歲
res = models.Users.objects.filter(age__lte=38)  # 小於等於18歲

3.查詢年齡是18歲或28歲或38歲的使用者資料

res = models.User.objects.filter(age__in=(18, 28, 38))

4.查詢年齡在18歲到38歲之間所有使用者資料

res = models.User.objects.filter(age__range=(18, 38))

5.查詢名名字中含有字母j的使用者資料

res = models.User.objects.filter(name__contains='j')  # 只查j不查J字母
res = models.User.objects.filter(name__incontains='j')  # J,j都會查出來

6.查詢註冊年份是2022年的使用者資料

res = models.User.objects.filter(register_time__year=2022)

五、ORM外來鍵欄位建立

1.ORM外來鍵欄位建立理論鋪墊

MySQL外來鍵關係
一對多:外來鍵欄位在多的一方
多對多:外來鍵欄位統一建在第三張關係表
一對一:建議放在查詢頻率較高的表
ORM確定外來鍵關係
一對多:外來鍵欄位在多的一方 跟MySQL一致
多對多:外來鍵欄位建在查詢頻率較高的表(內部自動建立第三張表) 
一對一:建議放在查詢頻率較高的表 跟MySQL一致
ORM建立外來鍵欄位程式碼語句
針對一對多和一對一同步到表中之後會自動加_id的字尾
publish = models.ForeignKey(to='Publish',on_delete=models.CASCADE)
author_detail = models = models.OneToOneFiled(to='AuthorDetail', on_delete=models.CASCADE)
針對多對多不會在表中展示而是建立第三張表
authers = models.ManyToManyField(to='Author')

2.ORM外來鍵欄位建立相關操作

# 針對一對多 插入資料可以直接填寫表中的實際欄位
models.Book.objects.create(title='程式設計基礎教學', price=888.88, publish_id=1)
models.Book.objects.create(title='程式設計進階教學', price=999.99, publish_id=1)
# 針對一對多 插入資料也可以填寫表中的類中欄位
publish_obj = models.Publish.objects.filter(pk=1).first()
models.Book.objects.create(title='高階程式設計指導書', price=1000, publish=publish_obj)

'''一對一與一對多 一致'''
既可以傳數字也可以傳物件
# 針對多對多關係繫結
book_obj = models.Book.objects.filter(pk=1).first()
book_obj.authors.add(1)  # 在第三張關係表中給當前書籍繫結作者
book_obj.authors.add(2, 3)
book_obj = models.Book.objects.filter(pk=4).first()
author_obj1 = models.Author.objects.filter(pk=1).first()
author_obj2 = models.Author.objects.filter(pk=2).first()
book_obj.authors.add(author_obj1)
book_obj.authors.add(author_obj1, author_obj2)
book_obj = models.Book.objects.filter(pk=1).first()
book_obj.authors.set((1, 3))  # 修改關係
book_obj.authors.set([2, ])  # 修改關係
author_obj1 = models.Author.objects.filter(pk=1).first()
author_obj2 = models.Author.objects.filter(pk=2).first()
book_obj.authors.set((author_obj1,))
book_obj.authors.set((author_obj1, author_obj2))
book_obj.authors.remove(2)
book_obj.authors.remove(1, 3)
book_obj.authors.remove(author_obj1,)
book_obj.authors.remove(author_obj1,author_obj2)
book_obj.authors.clear()

二、多對多三種建立方式

1.全自動建立
2.純手動建立
3.半自動建立
1.全自動建立
class Book(models.Model):
    title = models.CharField(max_length=32)
    authors = models.ManyToManyField(to='Authors', on_delete=models.CASCADE)
class Authors(models.Model):
    name = models.CharField(max_length=32)
    
# 優勢:自動建立第三張表 並提供了add、remove、set、clear四種操作
# 劣勢:第三張表無法建立更多的欄位 擴充套件性比較差
2.手動建立
class Book(models.Moldel):
    title = model.CharField(max_length=32)
class Author(models.Model):
    name = moldel.CharField(max_length=32)
class Book2Author(models.Model):
    book = models.ForeignKey(to='Book',on_delete=models.CASCADE)
    others = models.CharField(max_length=32)
    join_time = models.DateField(auto_now_add=True)
    
# 優勢:第三張表自己建立 擴充套件性強
# 劣勢:編寫繁瑣並不再支援add、remove、set、clear以及正反向概念
3.半自動建立
class Book(models.Model):
    title = 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.Model):
    book = models.ForeignKey(to='Book', on_delete=models.CASCADE)
    author = models.ForeignKey(to='Author', on_delete=models.CASCADE)
    others = models.CharField(max_length=32)
    join_time = models.DateField(auto_now_add=True)
    
# 優勢:第三張表完全由自己建立 擴充套件性強 正反向概念依然清晰可用
# 劣勢:編寫繁瑣不再支援add、remove、set、clear
# 總結
add()\remove()	     多個位置引數(數字 物件)
set()			    可迭代物件(元組 列表) 數字 物件 
clear()			    情況當前資料物件的關係

六、多表查詢(基於物件和雙下劃線)

1.ORM跨表查詢理論

MySQL跨表查詢
子查詢:分佈操作(將一條SQL語句用括號括起來當作另外一條SQL語句的條件)
連表操作:先整合多張表之後基於單表查詢即可('inner'/left/right/union join)
正反向查詢(重要)
正向:由外來鍵欄位所在的表資料查詢關聯的表查詢
反向:無外來鍵欄位的表資料查詢關聯的表資料查詢
# 技巧:核心就看外來鍵欄位在不在當前資料所在的表中
ORM跨表查詢口訣(重要)
正向查詢按外來鍵欄位
反向查詢按表名小寫

2.基於物件的跨表查詢

# 1.查詢主鍵為1的書籍對應的出版社名稱
# 先根據條件獲取資料物件
book_obj = models.Book.objects.filter(pk=1).first()
# 再判斷正反向的概念  由書查出版社 外來鍵欄位在書所在的表中 所以是正向查詢
print(book_obj.publish.name)
# 2.查詢主鍵為4的書籍對應的作者姓名
# 先根據條件獲取資料物件
book_obj = models.Book.objects.filter(pk=4).first()
# 再判斷正反向的概念  由書查作者 外來鍵欄位在書所在的表中 所以是正向查詢
print(book_obj.authors)  # app01.Author.None
print(book_obj.authors.all())
print(book_obj.authors.all().values('name'))
# 3.查詢almira的電話號碼
author_obj = models.Author.objects.filter(name='almira').first()
print(author_obj.author_detail.phone)
# 4.查詢北方出版社出版過的書籍
publish_obj = models.Publish.objects.filter(name='新疆維吾爾出版社').first()
print(publish_obj.book_set)  # app01.Book.None
print(publish_obj.book_set.all())
# 5.查詢almira寫過的書籍
author_obj = models.Author.objects.filter(name='almira').first()
print(author_obj.book_set)  # app01.Book.None
print(author_obj.book_set.all())
# 6.查詢電話號碼是18988998899的作者姓名
author_detail_obj = models.AuthorDetail.objects.filter(phone=18988998899).first()
print(author_detail_obj.author)
print(author_detail_obj.author.name)

3.基於雙下劃線的跨表查詢

 # 1.查詢主鍵為1的書籍對應的出版社名稱
res = models.Book.objects.filter(pk=1).values('publish__name','title')
print(res)
# 2.查詢主鍵為4的書籍對應的作者姓名
res = models.Book.objects.filter(pk=4).values('title', 'authors__name')
print(res)
 # 3.查詢almira的電話號碼
res = models.Author.objects.filter(name='almira').values('author_detail__phone')
print(res)
# 4.查詢北方出版社出版過的書籍名稱和價格
res = models.Publish.objects.filter(name='新疆維吾爾出版社').values('book__title','book__price','name')
print(res)
# 5.查詢almira寫過的書籍名稱
res = models.Author.objects.filter(name='almira').values('book__title', 'name')
print(res)
# 6.查詢電話號碼是110的作者姓名
res = models.AuthorDetail.objects.filter(phone=110).values('phone', 'author__name')
print(res)

4.進階操作

# 1.查詢主鍵為1的書籍對應的出版社名稱
res = models.Publish.objects.filter(book__pk=1).values('name')
print(res) 
# 2.查詢主鍵為4的書籍對應的作者姓名
res = models.Author.objects.filter(book__pk=4).values('name','book__title')
print(res)
# 3.查詢almira的電話號碼
res = models.AuthorDetail.objects.filter(author__name='almira').values('phone')
print(res)
# 4.查詢北方出版社出版過的書籍名稱和價格
res = models.Book.objects.filter(publish__name='新疆維吾爾出版社').values('title','price')
print(res)
# 5.查詢almira寫過的書籍名稱
res = models.Book.objects.filter(authors__name='almira').values('title')
print(res)
# 6.查詢電話號碼是18988998899的作者姓名
res = models.Author.objects.filter(author_detail__phone=18988998899).values('name')
print(res)

5.補充說明

# 查詢主鍵為4的書籍對應的作者的電話號碼
res = models.Book.objects.filter(pk=4).values('authors__author_detail__phone')
print(res)
res = models.AuthorDetail.objects.filter(author__book__pk=4).values('phone')
print(res)
res = models.Author.objects.filter(book__pk=4).values('author_detail__phone')
print(res)

七、聚合查詢

聚合函式:Max Min Sun Count Avg
在ORM中支援單獨使用聚合函式 用關鍵字:aggregate
    
from django.db.models import Max, Min, Sum, Avg, Count
res = models.Book.objects.aggregate(Max('price'), Min('price'), Sum('price'), Avg('price'), Count('pk'))
print(res)

八、分組查詢

# 溫馨提示來襲喲!
如果執行ORM分組查詢報錯 並且有關鍵字sql_mode strict mode 
移除sql_mode中的only_full_group_by 即可! 程式碼如下

set global sql_mode='stric_trans_tables';

1.統計每一本書的作者個數

res = models.Book.objects.annotate(author_num=Count('authors_pk')).values('title', 'author_num')
print(res)

2.統計出每個出版社賣出的最便宜的書的價格

res = models.Publish.objects.annotate(min_price=Min('book_price')).values('name', 'min_price')
print(res)

3.統計不止一個作者的書

# 1.先統計每本書作者個數
res = models.Book.objects.annotate(author_num=Count('author_pk'))
# 2.再從中篩選作者個數大於1的資料
res.filter(author_num_gt=1).values('title', 'author_num')

3.查詢每個作者的書總價格

res = models.Author.objects.annotate(totalprice=Sum('book__price'),count_book=Count('book_pk')).values('name','totalprice','count_book')
print(res)
# 按照表名分組
models.表名.objects.annotate()
# 按照values括號內指定的欄位分組
models.表名.objects.values('欄位名').annotate()
res = models.Book.objects.values('publish_id').annotate(count_pk=Count('pk')).values('publish_id','count_pk')
print(res)

九、F與Q查詢

1.什麼是F查詢?
F查詢:同一張表格的不同欄位之間的查詢
當查詢條件不是明確的 也需要從資料庫中獲取 就需要使用F查詢
2.什麼是Q查詢?
Q查詢:需要複雜的邏輯關係的時候使用Q查詢
逗號預設是and,處理不了or not 這樣的更復雜查詢條件 這時Q查詢用上排場了
| 表示取或or
& 表示取且and
~ 表示取反not

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

from django.db.models import F
res = models.Book.objects.filter(stock__gt=F('sold'))
print(res)

2.將所有書的價格漲800

from django.db.models import F
models.Book.objects.update(price=F(price) + 800)

3.將所有書的名稱後面追加爆款

from django.db.models import F
from django.db.models.functions import Concat
from django.db.models import Value

models.Book.objects.update(title=Concat(F('title'),Value('爆款')))

4.查詢主鍵是1或者大於2000的書籍

from django.db.models import Q
res = models.Book.objects.filter(Q(pk=1) | Q(price__gt=2000))

十、Q查詢進階操作

from django.db.models import Q
q_obj = Q()  # 1.產生Q物件
q_obj.connector = 'or'  # 預設多個條件的連線是and可以修改為or
q_obj.children.append(('pk', 1))  # 2.新增查詢條件
q_obj.children.append(('price__gt', 2000))  # 支援新增多個
res = models.Book.objects.filter(q_obj)  # 查詢支援直接填寫q物件
print(res)

十一、ORM查詢最佳化

1.ORM的查詢預設都是惰性查詢

如果你僅僅只是書寫了ORM語句  沒有用該語句所查詢出來的引數
那麼ORM會自動識別 直接不執行 這個性質可以提升效率 節省資源    

2.ORM自帶分頁查詢(limit)

平時我們學習過程當中處理資料的量很少 我們沒有注意到ORM自帶的分頁查詢功能
將來在工作過程中要處理的資料上千萬的  這個時候我們得關注一下ORM的分頁查詢 
如果沒有分頁查詢功能那資料庫記憶體也會  受不了的 直接回導致專案癱瘓 系統崩掉   

3.only與defer

res = models.Book.objects.only('title', 'price')  # only關鍵字查詢詳解
print(res)  # 結果是queryset物件[資料物件、資料物件]
for obj in res:
    print(obj.title)  # 點選括號內填寫的欄位 不走SQL查詢語句
    print(obj.publish_time)  # 可以執行括號內沒有的欄位並獲取資料 但是會走SQL查詢語句
res = models.Book.objects.defer('title', 'price')  # defer關鍵字查詢詳解
print(res)  # 結果是queryset物件[資料物件、資料物件]
for obj in res:
    print(obj.title)  # 點選括號內填寫的欄位 走SQL查詢語句
    print(obj.publish_time)  # 點選括號內沒有的欄位獲取資料 不會走SQL查詢語句

4.selected_related與prefetch_related

res = models.Book.objects.selected_related('authors')  # selected_related相當於連表查詢 不支援多對多
for obj in res:
    print(res)
res = models.Book.objects.prefetch_related('publish')  # prefetch_related相當於子查詢
for obj in res:
    print(obj.publish.name)

十二、ORM批次運算元據(ORM操作最佳化)

關鍵字:
bulk_create()  # 批次建立資料
bulk_update()  # 批次更新資料
def ab_bk_func(request):
    book_obj_list = []  # 可以用列表生成式[... for i in ... if ...] 
    for i in range(1, 100000):
        book_obj = models.Books01(title='第%s本書' % i)  # 單純的用類名加括號產生物件
        book_obj_list.append(book_obj)
    models.Books01.objects.bulk_create(book_obj_list)  # 批次插入資料
    """使用orm提供的批次插入操作 5s 10萬條左右"""
    book_queryset = models.Books01.objects.all()  # 查詢出所有的表中並展示到前端頁面
    return render(request, 'BkPage.html', locals())

十三、ORM事務操作

1.SQL事務相關理論知識

1.事務的四大特性(ACID)
    A(Atomicity 原子性)
    C(Consistency 一致性)
    I(Isolation 隔離性)
    D(Durability 持續性) 

2.相關SQL關鍵字
    start transaction;  # 啟動事務
    rollback;  # 回退 回滾
    commit;  # 把事務儲存到資料庫
    savepoint;  # 在回滾中儲存一個節點下次直接定位到此節點

3.相關SQL重要概念
    髒讀:是指一個執行緒中的事務讀取到了另外一個執行緒中未提交的資料
    幻讀 :指一個執行緒中的事務讀取到了另外一個執行緒中提交的insert的資料
    不可重複讀: 是指一個執行緒中的事務讀取到了另外一個執行緒中提交的update的資料
    MVCC多版本控制:是透過資料行的多個版本管理來實現資料庫的併發控制

2.django orm 提供了三種開啟事務方法

# 方法一:在配置檔案裡寫如下程式碼 全域性有效
'ATOMIC_REQUESTS':True
# 方法二:裝飾器 區域性有效
from django.db import transation
@transaction.atomic
def index():pass
# 方法三:with上下文管理  區域性有效
from django.db import transation
def reg():
    with transaction.atomic():pass 

十四、ORM常用欄位

1.AutoFiled(primary_key=True)  # 主鍵
2.CharField(max_length=32)  # 字元
3.IntegerField  # 整型
4.BigIntegerField  # 大整型
5.DecimalField(max_digits=8, decimal_places=2)  # 十進位制欄位
6.DateField(auto_now, auto_now_add)	 # 年月日
7.DateTimeField(auto_now, auto_now_add)  # 年月日時分秒
8.BooleanField  # 傳佈爾值自動存0或1
9.TextField  # 儲存大段文字
10.EmailField  # 儲存郵件格式資料
11.FileField  # 傳文字物件 自動儲存到提前配置好的路徑下並儲存路徑資訊
# 可以自定義欄位
class MyCharField(models.Field):
    def __init__(self,max_length, *args, **kwargs):
        self.max_length = max_length
        super().__init__(max_length=manx_length, *args, **kwargs)
        
    def db_type(self, connection):
        return 'char(%s)' % self.max_length
    
    
class User(models.Model):
    name = models.CharField(max_length=32)
    info = MyCharField(max_length=64)

十五、ORM常用欄位引數

primary_key  # 主鍵
verbose_name  # 註釋
max_length  # 欄位長度
max_digits  # 小數總共多少位
decimal_places  # 小數點後面的位數
auto_now  # 每次運算元據自動更新事件
auto_now_add  # 首次建立自動更新事件 後續不會自動更新
null  # 允許欄位為空
default  # 欄位預設值
unique  # 唯一值
db_index  # 給欄位新增索引
choices  # 當某個欄位的可能效能被列舉完全的情況下使用
外來鍵欄位
to  #關聯表
to_field  # 關聯欄位
on_delete=models.CASCADE  # 級聯操作 當主表刪除一條資料 從表關聯資料同時被刪除
on_delete=models.SET_NULL  # 當主表刪除一條資料 從表關聯欄位設定為null 定義外來鍵必須可以允許為空
on_delete=models.PROTECT  # 當主表刪除一條資料 從表關聯欄位是受保護的外來鍵 所以都不允許刪除
on_delete=models.SET_DEFAULT  # 當主表刪除一條資料 從表關聯欄位設定為預設值 定義外來鍵必須有一個預設值
on_delete=models.SET()  # 當主表刪除一條資料 從表關聯欄位設定為SET()中設定的值
on_delete=models.DO_NOTHING  # 什麼都不做 一切都看資料庫級別的約束
# 詳細說一下choices引數 因為在專案裡常用
class User(models.Model):
    name = models.CharField(max_length=32)
    info = MyCharField(max_length=64)
    gender_choice = (
         (1, '男性')
         (2, '女性')
         (3, '其他')
    )
    gender = models.IntegerField(choices=gender_choice,null=True)
user_obj = User.objects.filter(pk=1).first()
user_obj.gender
user_obj.get_gender_display()

相關文章