Django 資料庫
一、聚合查詢
聚合函式:
- Max
- 求最大值
- Min
- 求最小值
- Sun
- 求和
- Cont
- 統計數量
- Avg
- 求平均值
使用方法
- 類名.object.aggreate(聚合函式(‘欄位名’))
在MySQL資料庫中,聚合函式需要在分組後(group by)才能使用
在Django中,可以直接使用,需要搭配關鍵詞:aggtegate
1、匯入模組
from django.db.models import Max,Min,Sun,Count,Avg
2、res = Book.objects.aggregate(Max('price'), Count('pk'), 最小价格=Min('price'), allPrice=Sum('price'),平均價格=Avg('price'))
二、分組查詢
補充
問題:
在執行orm分組查詢中,如果報錯,並且有關鍵字sql_mode strict mode
解決方法:
移除sql_mode中的only_full_group_by
使用方法
- 按表分組
- models.表名.objects.annotate()
- 按指定欄位分組
- models.表名.objects.values('欄位名').annotate()
按表分組
# 分組查詢
# 統計每一本書的作者個數
# res = models.Book.objects.annotate(author_num=Count('authors__pk')).values('title', 'author_num')
# print(res)
# 統計出每個出版社賣的最便宜的書的價格
# res = models.Publish.objects.annotate(min_price=Min('book__price')).values('name', 'min_price')
# print(res)
# 統計不止一個作者的圖書
# 1.先統計每本書的作者個數
# res = models.Book.objects.annotate(author_num=Count('authors__pk'))
# 2.篩選出作者個數大於1的資料
# res = models.Book.objects.annotate(author_num=Count('authors__pk')).filter(author_num__gt=1).values('title',
# 'author_num')
# print(res)
# 查詢每個作者出的書的總價格
# res = models.Author.objects.annotate(總價=Sum('book__price'),count_book=Count('book__pk')).values('name','總價','count_book')
# print(res)
按欄位分組
res = models.Book.objects.values('publish_id').annotate(count_pk=Count('pk')).values('publish_id', 'count_pk')
print(res)
三、F查詢與Q查詢
1、F查詢
當查詢條件不是明確的,也需要從資料庫中獲取,就需要使用F查詢,它可以直接對一整個欄位下的資料進行操作
使用方法
1、匯入模組
from django.db.models import F
2、具體用法
# 1.查詢庫存數大於賣出數的書籍
# res = models.Book.objects.filter(kucun__gt=F('maichu'))
# 2.將所有書的價格漲800
# models.Book.objects.update(price=F('price') + 800)
# 3.將所有書的名稱後面追加爆款
from django.db.models.functions import Concat
from django.db.models import Value
models.Book.objects.update(title=Concat(F('title'), Value('新款')))
2、Q查詢
ORM中,所有的資料條件都是使用逗號隔開,彼此的關係預設都是and的關係
Q:可以將多個查詢條件的關係做修改
使用方法
1、匯入模組
from django.db.models import Q
2、具體用法
# and關係
models.Book.objects.filter(Q(pk=1),Q(title='三國'))
# or關係
models.Book.objects.filter(Q(pk=1) | Q(title='三國'))
# not關係
models.Book.objects.filter(~Q(pk=1),Q(title='三國'))
3、Q查詢進階操作
說明
透過生成Q物件,並提前設定好條件,直接將Q物件傳入filter中當作條件進行查詢
具體用法
1、匯入Q模組
from django.db.models import Q
2、產生Q物件
q_obj = Q()
3、設定連結條件(預設為and,可以修改為or)
q_obj.connector = 'or'
4、新增查詢條件 (支援新增多個查詢條件)
# 查詢條件1
q_obj.children.append(('pk', 1))
# 查詢條件2
q_obj.children.append(('price__gt', 2000))
5、傳入Q物件,進行查詢(# 查詢支援直接填寫q物件)
res = models.Book.objects.filter(q_obj)
print(res)
四、ORM查詢最佳化
ORM查詢最佳化是指,在我們使用的ORM語句進行查詢時,它的底層幫助我們做了很多的最佳化,其目的都是為了節省記憶體空間
-
ORM預設都是惰性查詢
- 當我們不執行列印操作的時候,ORM語句不會執行,想要看到這個現象需要開啟日誌功能,即在配置檔案中進行配置
-
ORM查詢自帶分頁
- 可以透過日誌展示的程式碼檢視,日誌返回的sql程式碼後端會有一個limit
-
only與defer
- 特別說明
1、only與defer
前置說明
這裡需要做一些具體的說明,方便大家理解only和defer。
當我們在Django中執行ORM操作進行資料庫查詢的時候,其實內部的程式碼把所有的資料庫中的記錄,都封裝到了ORM操作的物件中去了,因此我們可以透過點的方式或是索引等方式查詢到對應的資料。
但是當遇到查詢的時候需要查詢不在條件中的記錄時,就需要執行sql語句進行查詢了。
比如我們在查詢的時候,需要的結果在外來鍵對應的表中,這時候去外來鍵對應的表中查詢資料,就需要執行sql語句進行查詢,並且查詢一條記錄需要執行一次sql語句
而我們的only的作用是把寫在括號內的引數中的欄位的值封裝到物件中,讓後續查詢的時候 不需要執行sql語句進行查詢,加快執行速度。或是起到一個減少程式碼封裝的資料量,加快執行的作用。
而defer則是和only相反,寫在括號內的欄位值不會被封裝到物件中,別的欄位反而會被封裝到物件中。
only
獲取資料物件+含有指定欄位對應的資料
res = models.Book.objects.only('title', 'price')
print(res) # queryset [資料物件、資料物件]
for obj in res:
print(obj.title) # 點選括號內填寫的欄位 不走SQL查詢
print(obj.price)
print(obj.publish_time) # 可以點選括號內沒有的欄位獲取資料 但是會走SQL查詢
defer
res = models.Book.objects.defer('title', 'price')
print(res) # queryset [資料物件、資料物件]
for obj in res:
print(obj.title) # 點選括號內填寫的欄位 走SQL查詢
print(obj.price)
print(obj.publish_time) # 點選括號內沒有的欄位獲取資料 不走SQL查詢
五、ORM事務操作
事務的四大特性(ACID)
原子性、一致性、隔離性、永續性
相關SQL關鍵字
1、開啟事務
start transaction;
2、回滾
rollback;
3、提交
commit;
4、設定回滾節點
savepoint;
相關重要概念
髒讀、幻讀、不可重複讀、MVCC多版本控制...
1、Django開啟事務的方法
方法一:全域性有效
# 配置檔案的資料庫相關配置中新增鍵值對
"ATOMIC_REQUESTS": True
方法二:裝飾器
# 區域性有效,這個檢視函式內的一些orm操作屬於一個事務
from django.db import transaction
@transaction.atomic
def index():pass
方法三:上下文管理
# 區域性有效,寫在with下方的orm操作屬於一個事務
from django.db import transaction
def reg():
with transaction.atomic():
pass
注意事項
這裡的三種方法有個小區別,前面兩種方式執行事務,檢視層函式遇到返回值型別不對,orm操作可以正常執行的,但是with上下文管理的方式操作事務的話,則不行,操作會回退。
六、ORM常用欄位型別
1、預設欄位型別
名稱 | 含義 |
---|---|
AutoField() | Int自增列 必須填入引數 primary_key=True 當model中如果沒有自增列 則自動會建立一個列名為id的列 |
CharField() | 字元型別 必須提供max_length引數 max_length表示字元長度 |
IntegerField() | 一個整數型別 範圍在 -2147483648 to 2147483647 (一般不用它來存手機號(位數也不夠) 直接用字串存) |
BigIntegerField() | 長整型(有符號的) -9223372036854775808 ~ 9223372036854775807 |
DateField() | 日期欄位 日期格式 YYYY-MM-DD 相當於Python中的datetime.date()例項 |
DateTimeField() | 日期時間欄位 格式 YYYY-MM-DD HH:MM[:ss[.uuuuuu]][TZ] 相當於Python中的datetime.datetime()例項 |
DecimalField() | 10進位制小數 引數 max_digits 小數總長度 decimal_places,小數位長度 |
EmailField() | 字串型別 Django Admin以及ModelForm中提供驗證機制 |
BooleanField() | 布林值型別 傳佈爾值存數字0或1 |
TextField() | 文字型別 儲存大段文字 |
FileField() | 字串 路徑儲存在資料庫 檔案上傳到指定目錄 引數 upload_to = " " 上傳檔案的儲存路徑 storage = None 儲存元件 預設django.core.files.storage.FileSystemStorage |
ForeignKey() | 外來鍵型別在ORM中用來表示外來鍵關聯關係 一般把ForeignKey欄位設定在 '一對多’中’多’的一方 ForeignKey可以和其他表做關聯關係同時也可以和自身做關聯關係 |
OneToOneField() | 一對一欄位 通常一對一欄位用來擴充套件已有欄位 通俗的說就是一個人的所有資訊不是放在一張表裡面的,簡單的資訊一張表,隱私的資訊另一張表,之間透過一對一外來鍵關聯 |
ManyToManyField() | 簡單來說就是在多對多表關係並且這一張多對多的關係表是有Django自動幫你建的情況下 下面的方法才可使用create add set remove clear |
2、自定義欄位型別
ORM支援使用者自定義欄位型別,比方說ORM中沒有設定char型別的欄位,我們可以使用自定義欄位來實現他。
方法
class MyCharField(models.Field):
def __init__(self, max_length, *args, **kwargs):
self.max_length = max_length
uper().__init__(max_length=max_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 | 當刪除關聯表中的資料時,當前表與其關聯的行的行為。 |
on_delete
當兩張連表進行關聯後,主表的資料被刪除時,從表中對應的資料正在預設情況下也會被刪除,但是on_delete可以設定預設值,當主表資料被刪除時,從表中對應的資料會被修改為預設值
def func():
return 10
class MyModel(models.Model):
user = models.ForeignKey(
to="User",
to_field="id",
on_delete=models.SET(func)
)
# on_delete可以設定預設值,當主表資料被刪除時,從表中對應的資料會被修改為預設值
不同值對應的功能:
1、models.CASCADE
級聯操作,當主表中被連線的一條資料刪除時,從表中所有與之關聯的資料同時被刪除
2、models.SET_NULL
當主表中的一行資料刪除時,從表中所有與之關聯的資料的相關欄位設定為null,此時注意定義外來鍵時,這個欄位必須可以允許為空
3、models.PROTECT
當主表中的一行資料刪除時,由於從表中相關欄位是受保護的外來鍵,所以都不允許刪除
4、models.SET_DEFAULT
當主表中的一行資料刪除時,從表中所有相關的資料的關聯欄位設定為預設值,此時注意定義外來鍵時,這個外來鍵欄位應該有一個預設值
5、models.SET()
當主表中的一條資料刪除時,從表中所有的關聯資料欄位設定為SET()中設定的值,與models.SET_DEFAULT相似,只不過此時從表中的相關欄位不需要設定default引數
6、models.DO_NOTHING
什麼都不做,一切都看資料庫級別的約束,注資料庫級別的預設約束為RESTRICT,這個約束與django中的models.PROTECT相似
choices
當欄位資料的可能性是可以完全列舉出來的時候 應該考慮使用該引數
- get_gender_display()
- 獲取choices引數內的資料值
class UserInfo(models.Model):
username = models.CharField(max_length=32)
gender_choice = (
(1, '男性'),
(2, '女性'),
(3, 'other'),
)
gender = models.IntegerField(choices=gender_choice)
user_obj = models.UserInfo.objects.filter(pk=1).first()
print(user_obj.gender) # 獲取的是真實資料
print(user_obj.get_gender_display())
user_obj1 = models.UserInfo.objects.filter(pk=2).first()
user_obj2 = models.UserInfo.objects.filter(pk=3).first()
user_obj3 = models.UserInfo.objects.filter(pk=4).first()
print(user_obj1.get_gender_display())
print(user_obj2.get_gender_display())
print(user_obj3.get_gender_display()) # 如果沒有則按照真實資料返回