目錄
模型層與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()