模型M
ORM
一、MVC 與 ORM
MVC框架中包括一個重要的部分,就是ORM,它實現了數據模型與數據庫的解耦,即數據模型的設計不需要依賴於特定的數據庫,通過簡單的配置就可以輕鬆更換數據庫,比如我們之前搭建的快速開發項目,就不需要考慮數據庫的問題
ORM是“對象-關係-映射”的簡稱,主要任務是:
1,對象的類型生成表結構
2,將對象、列表的操作,轉換爲sql語句
3,將sql查詢到的結果轉換爲對象、列表
這極大的減輕了開發人員的工作量,不需要面對因數據庫變更而導致的無效勞動
Django中的模型包含存儲數據的字段和約束,對應着數據庫中唯一的表
二、ORM分爲兩種:
1,DB First 數據庫裏先創建數據庫表結構,根據表結構生成類,根據類操作數據庫
2,Code First 先寫代碼,執行代碼創建數據庫表結構
主流的orm都是code first。
django 的orm也是code first,所以學的時候,本質就分爲兩塊:
1,根據類自動創建數據庫表
python manage.py makemigrations
2,根據類對數據庫表中的數據進行各種操作
三、優缺點:
優點:
擺脫複雜的SQL操作,適應快速開發;
讓數據結構變得簡潔;
數據庫遷移成本更低
Django中自帶了ORM ,如果我更熟悉其他的ORM,怎麼辦?
缺點:
Django框架中ORM與其他模塊耦合度較高,不能使用其他ORM框架,這就是一個大Bug
模型定義
一,定義屬性
1,在模型中定義的屬性,會生成表中的字段
2,定義屬性時,需要字段類型,字段類型被定義在django.db.models.fields目錄下
3,django會爲表增加自動增長的主鍵列,每個模型只能有一個主鍵列,如果使用選項設置某屬性爲主鍵列後,則django不會再生成默認的主鍵列
屬性命名限制
不能是python的保留關鍵字
由於django的查詢方式,不允許使用連續的下劃線
使用方式
- 導入from django.db import models
- 通過models. Field創建字段類型的對象,賦值給屬性
二,字段類型
AutoField:一個根據實際ID自動增長的IntegerField,通常不指定,如果不指定,一個主鍵字段將自動添加到模型中
BooleanField:true/false 字段,此字段的默認表單控制是CheckboxInput
NullBooleanField:支持null、true、false三種值
CharField(max_length=字符長度):字符串,默認的表單樣式是 TextInput
TextField:大文本字段,一般超過4000使用,默認的表單控件是Textarea
IntegerField:整數
FloatField:用Python的float實例來表示的浮點數
DecimalField(max_digits=None, decimal_places=None):使用python的Decimal實例表示的十進制浮點數
DecimalField.max_digits:位數總數
DecimalField.decimal_places:小數點後的數字位數
DateField[auto_now=False, auto_now_add=False]):使用Python的datetime.date實例表示的日期
參數DateField.auto_now:每次保存對象時,自動設置該字段爲當前時間,用於"最後一次修改"的時間戳,它總是使用當前日期,默認爲false
參數DateField.auto_now_add:當對象第一次被創建時自動設置當前時間,用於創建的時間戳,它總是使用當前日期,默認爲false
該字段默認對應的表單控件是一個TextInput
在管理員站點添加了一個JavaScript寫的日曆控件,和一個“Today"的快捷按鈕
auto_now_add, auto_now, and default 這些設置是相互排斥的,他們之間的任何組合將會發生錯誤的結果
TimeField:使用Python的datetime.time實例表示的時間,參數同DateField
DateTimeField:使用Python的datetime.datetime實例表示的日期和時間,參數同DateField
FileField:一個上傳文件的字段
ImageField:繼承了FileField的所有屬性和方法,但對上傳的對象進行校驗,確保它是個有效的image
三,字段選項
通過字段選項,可以實現對字段的約束,在定義字段對象時通過關鍵字參數指定
verbose_name:在admin界面顯示的字段標識
null:如果爲True,Django 將空值以NULL 存儲到數據庫中,默認值是 False
blank:如果爲True,則該字段允許爲空白,默認值是 False
對比:null是數據庫範疇的概念,blank是表單驗證證範疇的
db_column:字段的名稱,如果未指定,則使用屬性的名稱
db_index:若值爲 True, 則在表中會爲此字段創建索引
default:默認值
primary_key:若爲 True, 則該字段會成爲模型的主鍵字段
unique:如果爲 True, 這個字段在表中必須有唯一值
help_text:會在form表單控件中顯示help文本
choices:Admin中選擇框的內容
editable Admin中是否可以編輯
四,元選項
在模型類中定義類Meta,用於設置元信息,對應於數據庫中表信息
app_label:指明該模型屬於哪一個應用
db_table:定義數據表名稱,推薦使用小寫字母,數據表的默認名稱<app_name>_<model_name>
ordering:對象的默認排序字段,獲取對象的列表時使用,接收屬性構成的列表
verbose_name:Admin顯示模型名
verbose_name_plural:Admin顯示模型名附屬
五,自定義管理類
class BookManage(models.Manager):
def setprice(self,book,price):
if isinstance(book,self.model):
book.price = price
book.save()
else:
raise Exception(f"{book}並非{self.model}實例")
Create your models here.
class Book(models.Model):
title = models.CharField(max_length=20)
pub_time = models.DateField()
price = models.DecimalField(max_digits=6,decimal_places=3)
manage =BookManage()
具體操作
六,關係
關係的類型包括
ForeignKey:一對多,將字段定義在多的端中
ManyToManyField:多對多,將字段定義在任意一端中
OneToOneField:一對一,將字段定義在任意一端中
1,一對多案例:
2,一對一案例:
在我們自己的項目中需要使用到用戶系統,然而django自帶了用戶系統,並且定義了很多常用字段,此時我們就可以使用一對一關係
如:銀行卡號和銀行用戶
3,多對多案例:
添加關係 t.articles.add(a) 刪除t.acticles.remove(a)
未重命名查詢 a.tag_set.all() t.acticles.all()
重命名查詢 a.tags.all() t.acticles.all()
總結一下一對一,一對多,多對多之間的關聯查詢
# 一對多 一方Book 實例b 多方Hero 實例h 關係字段定義在多方
# 一找多 b.hero_set.all() 如果定義過related_name='heros' 則使用 b.heros.all()
# 多找一 h.book
# 一對一 一方Account 實例a 一方Concact 實例c 關係字段定義在任意一方
# a 找 c a.concact
# c 找 a c.account
# 多對多 多方Article 實例a 多方Tag 實例t 關係字段可以定義在任意一方
# 添加關係 t.articles.add(a) 移除關係 t.articles.remove(a)
# a 找 所有的 t a.tag_set.all() 如果定義過related_name='tags' 則使用 a.tags.all()
# t 找 所有的 a t.articles.all()
模型查詢
一、查詢集與過濾器
查詢集表示從數據庫中獲取的對象集合,查詢集是可迭代的對象
返回查詢集的方法,稱爲過濾器,過濾器基於所給的參數限制查詢的結果
從Sql的角度,查詢集和select語句等價,過濾器像where和limit子句
查詢集經過過濾器篩選後返回新的查詢集,因此可以寫成鏈式過濾
惰性執行:創建查詢集不會帶來任何數據庫的訪問,直到調用數據時,纔會訪問數據庫
1,返回新的查詢集:
all() :檢索所有的對象
filter(**kwargs) :檢索特定的對象 返回一個與參數匹配的QuerySet
exclude():返回一個與參數不匹配的QuerySet
order_by(column_name):檢索排序後的對象,column_name:排序的列,默認升序,列名前加- 降序排序
2,返回單個對象
get():返回單個滿足條件的對象,
如果未找到會引發"模型類.DoesNotExist"異常
如果多條被返回,會引發"模型類.MultipleObjectsReturned"異常
count():返回當前查詢的總條數
first():返回第一個對象
last():返回最後一個對象
exists():判斷查詢集中是否有數據,如果有則返回True
3,限制查詢集
查詢集返回列表,可以使用下標的方式進行限制
等同於sql中的limit 和offset子句
注意:不支持負數索引
使用下標後返回一個新的查詢集,不會立即執行查詢
如果獲取一個對象,直接使用[0],等同於[0:1].get()
BookInfo.objects.all()[:5]
查找前5個entry表裏的數據
BookInfo.objects.all()[5:10]
查找從第5個到第10個之間的數據。
BookInfo.objects.all()[:10:2]
查詢從第0個開始到第10個,步長爲2的數據。
4,字段查詢
實現where子名,作爲方法filter()、exclude()、get()的參數
語法:屬性名稱_ _比較運算符=值
表示兩個下劃線,左側是屬性名稱,右側是比較類型
比較運算符
exact:表示判等
如果沒有寫“比較運算符”,表示判等
Book.objects.filter(id__exact=1)
contains:是否包含,大小寫敏感
exclude(title__contains=‘傳’)
startswith、endswith:以value開頭或結尾,大小寫敏感
exclude(title__endswith=‘傳’)
isnull、isnotnull:是否爲null
filter(title__isnull=False)
在前面加個i表示不區分大小寫,如iexact、icontains、istarswith、iendswith
in:是否包含在範圍內
filter(pk__in=[1, 2, 3, 4, 5])
gt、gte、lt、lte:大於、大於等於、小於、小於等於
filter(id__gt=3)
year、month、day、week_day、hour、minute、second:對日期間類型的屬性進行運算
filter(bpub_date__year=1980)
filter(bpub_date__gt=date(1980, 12, 31))
查詢的快捷方式:pk,pk表示primary key,默認的主鍵是id
filter(pk__lt=6)
5,聚合函數
使用aggregate()函數返回聚合函數的值
函數:Avg,Count,Max,Min,Sum
from django.db.models import Max
maxDate = Book.objects.all().aggregate(Max(‘pub_date’))
6,F對象
F()允許Django在未實際鏈接數據的情況下具有對數據庫字段的值的引用。
場景
Python操作方式
通常情況下我們在更新數據時需要先從數據庫裏將原數據取出後放在內存裏,然後編輯某些屬性,最後提交。
book = Book.objects.get(pk=3)
delta = timedelta(days=3)
book.pub_date = book.pub_date + delta
book.save()
book = Book.objects.get(pk=3)
print(book.pub_date)
當我們使用了F()之後呢?
from django.db.models import F
book = Book.objects.get(pk=3)
delta = timedelta(days=3)
book.pub_date = F(‘pub_date’) + delta
book.save()
book = Book.objects.get(pk=3)
print(book.pub_date)
結論
當Django程序中出現F()時,Django會使用SQL語句的方式取代標準的Python操作。
上述代碼中不管book.pub_date的值是什麼,Python都不曾獲取過其值,python做的唯一的事情就是通過Django的F()函數創建了一條SQL語句然後執行而已。
實質上通過F對象減少了一次與數據庫的交互。
特別是併發的情況,效率也變高了,減少了多線程同時操作帶來的隱患。
可以使用模型的字段A與字段B進行比較,如果A寫在了等號的左邊,則B出現在等號的右邊,需要通過F對象構造
filter(pk__lt=F(‘price’))
7,Q對象
應用場景
一般我們在Django程序中查詢數據庫操作都是在QuerySet裏進行進行,例如下面
代碼:
q1 = Book.objects.filter(pub_date__gt=date(1992, 1, 1))
q2 = q1.exclude(pk__gt=2)
將其組合起來:
q = Book.objects.filter(bpub_date__gt=date(1992, 1,1)).exclude(pk__gt=2)
問題:複雜的查詢條件,導致查詢越來越長。
引入Q對象
Q()對象就是爲了將這些條件組合起來。
當我們在查詢的條件中需要組合條件時(例如兩個條件“且”或者“或”)時。我們可以使用Q()查詢對象。
&(與)或者|(或)~(非)
可以將多個Q()對象組合起來傳遞給filter(),exclude(),get()等函數。
當操作符應用在兩個Q對象時,會產生一個新的Q對象
結合括號進行分組,構造複雜的Q對象
Q(pub_date__gt=date(1992, 1, 1)) & ~Q(title__startswith=“笑”)
改寫一下
c1 = Q(pub_date__gt=date(1992, 1, 1))
c2 = Q(title__startswith=“笑”)
q= BookInfo.books.filter(c1 & ~c2 )