《怪物獵人崛起:曙光》新映像見聞錄 阿爾洛教官
聚合查詢
在ORM中也有相應的方法與MySQL中的聚合函式作用一致。
一共有五個聚合函式:
方法 | 作用 |
---|---|
Max(欄位) | 返回欄位中資料最大值 |
Min(欄位) | 返回欄位中資料最小值 |
Sum(欄位) | 返回欄位中資料總和 |
Count(欄位) | 返回欄位中資料個數 |
Avg(欄位) | 返回欄位中資料平均值 |
在使用以上五個聚合函式是需要先匯入才能使用:
from django.db.models import Max,Min,Sum,Count,Avg
使用:統計Student表中score欄位各項資料
res = models.Student.objects.aggregate(
Max('score'),
Min('score'),
Sum('score'),
Avg('score'),
Count('score')
)
分組查詢
ORM分組查詢使用方法annotate()。
現有如下表:
- 作者(編號,姓名,性別)
- 書本(編號,書名,價格,作者編號)
以欄位為單位分組
要求:統計作者中各個性別的人數。
res = models.Author.objects.values('sex').annotate(sex_count=Count('pk')).values('sex','sex_count')
print(res)
以表為單位做分組
要求:統計每個作者出的書本數量。
res = models.Author.objects.annotate(book_count=Count('book__pk')).values('name','book_count')
print(res)
上述程式碼表示對作者表進行分組,並對書本編號進行統計,annotate()中的book_count是自己起的別名。
要求:統計每個作者出的最便宜的價格。
res = models.Author.objects.annotate(min_price=Min('book__price')).values('name', 'min_price')
print(res)
F與Q查詢
F方法
如果我們想要讓兩個欄位進行比較篩選,或者是讓欄位資料在原來的基礎上繼續修改,這時就需要用到F()方法了。
匯入:
from django.db.models import F
查詢學生中數學成績(math)高於英語(english)的學生:
res = models.Student.objects.filter(math__gt=F('english'))
把所有書本的價格提升10元:
models.Book.objects.update(price=F('price')+10)
把所有書本的後面加上"(爆款)"(字串拼接需要額外兩個方法):
from django.db.models import Value
from django.db.models.functions import Concat
models.Book.objects.update(name=Concat(F('name'), Value('(爆款)')))
Q方法
filter()方法可以填寫多個條件,每個條件逗號隔開,條件之間預設是and關係,無法直接修改。
如果想要讓多個條件之間有更多的關係(比如or、not),需要用到Q方法。
使用Q方法後,,(逗號)隔開代表and,|(管道符)隔開代表or,~(波浪號)隔開代表not。
匯入:
from django.db.models import Q
查詢書本價格(price)大於50或者數量(quantity)大於50的書本
res = models.Book.objects.filter(
Q(price__gt=50) | Q(quantity__gt=50)
)
查詢書本名稱(name)不包含'J'的書本:
res = models.Book.objects.filter(
~Q(name__contains='J')
)
進階
我們都知道,filter()內部不能使用字串傳參,比如filter('price'=50)這種,也就是說我們無法讓使用者自己輸入欄位名和欄位值篩選資料,但是使用Q方法就可以做到。
q_obj = Q() # 獲取Q物件
field = input('請輸入欄位名:')
value = input('請輸入欄位值:')
q_obj.children.append((field,value)) # 新增條件
res = models.Book.objects.filter(q_obj) # 篩選
print(res)
還可以設定條件之間的關係:
q_obj = Q()
q_obj.connector = 'or' # 用於表示條件之間的關係,預設為and
q_obj.children.append(('price__gt',50))
q_obj.children.append(('quantity__gt',50))
res = models.Book.objects.filter(q_obj)
print(res.query)
ORM查詢優化
1.ORM查詢預設都是惰性查詢(能不消耗資料庫資源就不消耗)
編寫ORM語句並不會直接使用SQL語句,只有後續的程式碼用到了才會執行。
2.ORM查詢預設自帶分頁功能(儘量減輕單次查詢資料的壓力)
每次執行SQL都會限制查詢資料數量。
除了values()可以查詢指定欄位外,還有only()、defer()、select_related()、prefetch_related()。
only()與defer()
only()
only()返回物件結果集,獲取only()中指定的欄位不會再次執行SQL語句,但是如果獲取沒有指定的欄位,會去執行SQL語句獲取結果。
res = models.Book.objects.only('name','price')
for i in res:
print(i.quantity) # 獲取沒有指定的欄位
defer()
defer()與only()剛好相反,獲取defer()中指定的欄位會再次執行SQL語句獲取結果,但是如果獲取沒有指定的欄位,不會去執行SQL語句。
res = models.Book.objects.defer('name','price')
for i in res:
print(i.name) # 獲取指定的欄位
select_related()與prefetch_related()
select_related()
select_related()括號內只能傳一對一和一對多欄位,不能傳多對多欄位或者其他欄位。
相當於連表操作,使用join一次性連線之後的大表中所有的資料全部封裝到資料物件中,後續使用物件跨表查詢都不會執行SQL語句。
res = models.Book.objects.select_related('author')
for i in res:
print(i.name)
print(i.author.name)
prefetch_related()
prefetch_related()括號內只能傳一對一、一對多、多對多欄位,不能傳其他欄位。
相當於子查詢,會分開操作查詢語句,將多次查詢之後的結果封裝到資料物件中,後續使用物件跨表查詢都不會執行SQL語句。
res = models.Book.objects.prefetch_related('author')
for i in res:
print(i.name)
print(i.author.name)
ORM常見欄位
常見欄位 | 描述 |
---|---|
AutoField() | 自動增長int型別 |
CharField(max_length=) | varchar型別,必須提供max_length引數指定長度 |
IntergerField() | int型別 |
DecimalField(max_digits=, decimal_places=) | decimal型別,總位數max_digits,小數位數decimal_places |
DateField() | date型別,提供auto_now和auto_now_add引數 |
DateTimeField() | datetime型別,提供auto_now和auto_now_add引數 |
BigIntergerField() | bigint型別 |
BooleanField() | 布林值,存0和1 |
TextField() | 儲存大段文字 |
FileField() | 傳檔案時會自動儲存到指定位置並只存檔案路徑 |
EmailField() | varchar(254) |
ForeignKey(to=) | 一對多外來鍵關係,to指定有關係的表 |
OneToOneField(to=) | 一對一外來鍵關係,to指定有關係的表 |
ManyToManyField(to=) | 多對多外來鍵關係,to指定有關係的表 |
自定義欄位型別
自定義一個char型別的方法:
# 自定義欄位型別
class MyCharField(models.Field):
def __init__(self, max_length, *args, **kwargs):
self.max_length = max_length
super().__init__(max_length=max_length, *args, **kwargs)
def db_type(self, connection):
return 'char(%s)' % self.max_length
欄位重要引數
重要引數 | 描述 |
---|---|
primary_key | 是否為主鍵 |
null | 是否可以為空 |
unique | 是否唯一 |
db_index | 是否設定索引 |
default | 設定預設值 |
max_length | 設定長度 |
verbose_name | 給欄位起別名 |
choices | 給定一些選擇,有對應關係就拿,沒有還是本身 |
日期欄位引數 | |
auto_now | 是否每次更新資料時都要更新該欄位 |
auto_now_add | 是否新增資料時將當前時間新增到該欄位 |
Decimal欄位引數 | |
max_digits | 設定Decimal欄位總位數 |
decimal_places | 設定Decimal欄位小數位數 |
外來鍵欄位 | |
to | 設定要關聯的表 |
to_fileld | 設定要關聯的表的欄位 |
on_delete | 設定當刪除關聯表中的資料時,當前表與其關聯的行的行為 |
db_constraint | 是否在資料庫中建立外來鍵約束,預設為True |
choices引數
設定
class User(models.Model):
gender_choice = (
(1, '男性'),
(2, '女性')
)
gender = models.IntegerField(choices=gender_choice)
取值的時候使用物件.get_xxx_display()來獲取choices後面具體的值,當choices中沒有該資料對應的時候獲取的仍是數字自己
User表
id | gender |
---|---|
1 | 1 |
2 | 4 |
user_obj = models.Userinfo.objects.get(pk=1)
print(user_obj.get_gender_display()) # 男
user_obj = models.Userinfo.objects.get(pk=2)
print(user_obj.get_gender_display()) # 4
on_delete引數
on_delete有如下選擇:
on_delete值 | 描述 |
---|---|
models.CASCADE | 刪除關聯資料,與之關聯也刪除 |
models.DO_NOTHING | 刪除關聯資料,引發錯誤IntegrityError |
models.PROTECT | 刪除關聯資料,引發錯誤ProtectedError |
models.SET_NULL | 刪除關聯資料,與之關聯的值設定為null(前提FK欄位需要設定為可空) |
models.SET_DEFAULT | 刪除關聯資料,與之關聯的值設定為預設值(前提FK欄位需要設定預設值) |
models.SET | 刪除關聯資料,與之關聯的值設定為指定值或是可執行物件的返回值 |
事務操作
使用:
from django.db import transaction
with transaction.atomic():
pass
with上下文管理開始執行代表事務開始,中間報錯自動回滾,結束with語句自動提交事務。
ORM執行原生SQL
方式一:
from django.db import connection, connections
cursor = connection.cursor()
cursor = connections['default'].cursor()
cursor.execute("""SELECT * from auth_user where id = %s""", [1])
cursor.fetchone()
方式二:
models.UserInfo.objects.extra(
select={'newid':'select count(1) from app01_usertype where id>%s'},
select_params=[1,],
where = ['age>%s'],
params=[18,],
order_by=['-age'],
tables=['app01_usertype']
)
多對多三種建立方式
全自動
orm自動建立第三張表,但是無法擴充套件第三張表的欄位。
class Student(models.Model):
courses = models.ManyToManyField(to='Course')
全手動
優勢在於第三張表完全自定義擴充套件性高,劣勢在於無法使用外來鍵方法和正反向查詢。
class Student(models.Model):
title = models.CharField(max_length=32)
class Course(models.Model):
name = models.CharField(max_length=32)
class Student2Course(models.Model):
student = models.ForeignKey(to='Student')
course = models.ForeignKey(to='Course')
半自動
正反向查詢還可以使用,並且第三張表可以擴充套件,唯一的缺陷是不能用add\set\remove\clear四個方法。
class Student(models.Model):
name = models.CharField(max_length=32)
courses = models.ManyToManyField(
to='Course',
through='Student2Course', # 指定表
through_fields=('student', 'course') # 指定欄位,注意順序
)
class Course(models.Model):
name = models.CharField(max_length=32)
class Student2Course(models.Model):
student = models.ForeignKey(to='Student')
course = models.ForeignKey(to='Course')
多對多建在任意一方都可以,如果建在Course表,欄位順序互換即可。