Django-C002-深入模型,到底有多深
此文章完成度【100%】留著以后忘記的回顧。多寫多練多思考,我會努力寫出有意思的demo,如果知識點有錯誤、誤導,歡迎大家在評論處寫下你的感想或者糾錯。
?
?
ORM介紹:對象關系映射(英語:(Object Relational Mapping,簡稱ORM,或O/RM,或O/R mapping),是一種程序技術,用于實現面向對象編程語言里不同類型系統的數據之間的轉換?。從效果上說,它其實是創建了一個可在編程語言里使用的--“虛擬對象數據庫”。
面向對象是從軟件工程基本原則(如耦合、聚合、封裝)的基礎上發展起來的,而關系數據庫則是從數學理論發展而來的,兩套理論存在顯著的區別。為了解決這個不匹配的現象,對象關系映射技術應運而生。
那么說說他的好處:
-  實現了數據模型與數據庫的解耦,通過簡單的配置就可以輕松更換數據庫,而不需要修改代碼
-  只需要面向對象編程,不需要面向數據庫編寫代碼
-  在MVC中model中定義的類,通過ORM與關系型數據庫中的表對應,對象的屬性提現對象之間的關系,這種關系也被映射到數據表中
?
那么SQLite 并不是我喜歡的數據庫,雖然自帶,還是不感冒,所以下面就看看我們如何使用自己想用到的數據庫之MySQL
重新創建一個項目,來用于接下來的練習
【Django?version】: 2.1
【pymysql?version】:0.9.3
【python version】: 3.7
?1.首先找到test2/settings.py 里的有關數據庫設置的變量 變量名=DATABASES
# 你可以選擇將原有指定的數據庫注釋掉,也可以直接刪除
DATABASES = {
    'default': {
        # 'ENGINE': 'django.db.backends.sqlite3',
        # 'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
        'ENGINE': 'django.db.backends.mysql',  # 使用的引擎為mysql
        'NAME': 'school',  # 數據庫名稱
        'USER': 'root',  # 數據庫登錄名
        'PASSWORD': 'toor',  # 數據庫密碼
        'HOST': 'localhost',  # 指定的數據庫所在的主機
        'PORT': 3306,  # 數據庫使用的端口
    }
}  
?
※踩了一晚上坑,終于pymysql可以成功導入,Django2.2.X的版本貌似不兼容pymysql,將版本降到2.1之后
?
如果你的pycharm沒有安裝pymysql,你在導入的時候將會有紅色下標,這時只需要在Pycharm中的Settings里面找到Project:你的項目名 里面添加即可
?
# 接下來運行,成功返回下面的頁面,不成功你可以在評論中提出你的問題,咱們一起想解決辦法
python manage.py runserver   ?
?
通過之前的學習我們可以看出Django的M,是為了簡化程序與數據庫之間的操作,可以讓我們用更少的代碼實現更多的功能,這也是Python的編程風格,我們只需要通過類和對象,比如之前例子中的ClassInfo、StudentInfo這兩個類和他們的對象,就能完成很多數據庫的增、刪、改、查。
我們還是需要兩個類來進行演示:ClassInfo 【班級類】StudentInfo【學生類】
這里需要注意的是我們創建類的時候必須要繼承models.Model類
首先我們找到models.py把這兩個類創建出來
1 from django.db import models 2 3 4 class ClassInfo(models.Model): 5 """班級模型類""" 6 name = models.CharField(max_length=20) # 班級名 7 class_date = models.DateField() # 開班日期 8 class_size = models.IntegerField(default=0) # 班級人數 默認0 9 isDelete = models.BooleanField(default=False) # 邏輯刪除 默認False 10 11 12 class StudentInfo(models.Model): 13 """學生模型類""" 14 name = models.CharField(max_length=20) # 姓名 最大長度20 15 age = models.IntegerField() # 年齡 16 gender = models.BooleanField(default=False) # 性別 默認 False 男 17 interest = models.CharField(max_length=200) # 愛好 最大長度200 18 isDelete = models.BooleanField(default=False) # 邏輯刪除 默認False 19 # 關聯的班級 班級與學生是一對多的關系 所以定義在學生類中 20 the_class = models.ForeignKey(ClassInfo, on_delete=models.CASCADE)
?
?models.py里面的類已經創建完,之后需要生成遷移文件
python manage.py makemigrations
?
python manage.py migrate
?
# 首先進入到這個表里 use school
?
# 接下來我們檢查一下剛剛生成的數據表
show tables;
?既然已經完成讓我們看一下表結構:
desc school_classinfo;
desc school_studentinfo;
?
那么現在我們添加一些數據到數據庫中
?
# 班級
insert into school_classinfo(name,class_date,class_size,isDelete) values ('python class','2019-6-21',17,0), ('java class','2019-6-21',1,0), ('c#','2019-6-21',1,0), ('php','2019-6-21',1,0);
# 學生 insert into school_studentinfo(name,age,gender,interest,isDelete,the_class_id) values ('蒙奇·D·路飛',19,0,'所有美食首先是肉、喜歡探險、感興趣于新奇怪異的事物',0,1), ('羅羅諾亞·索隆',21,0,'睡覺、修煉、喝酒',0,1), ('娜美',20,1,'錢,橘子',0,1), ('烏索普',19,0,'發明各種東西、制造武器',0,1), ('山治',21,0,'下廚,抽煙,浪漫幻想',0,1), ('托尼托尼·喬巴',17,1,'醫術',0,1), ('妮可·羅賓',30,1,'考古',0,1), ('弗蘭奇',36,0,'漢堡包',0,1), ('布魯克',90,0,'演奏,喝紅茶,牛奶,說骷髏冷笑話',0,1), ('甚平',46,0,'不詳',0,1), ('紅發香克斯',46,0,'紅發海賊團船長,原羅杰海賊團的實習員,擁有強大的霸王色霸氣',0,1), ('喬拉可爾·米霍克',46,0,'世界第一大劍豪,紅發好友,索隆的師傅',0,1), ('巴索羅米·熊',46,0,'原革命軍干部,肉球果實能力者,已被貝加龐克改造成完全改造為“人間兵器”和平主義者PX-0,失去作為人的記憶',0,1), ('波雅·漢庫克',32,1,'路飛',0,1), ('特拉法爾加·羅',26,0,'紅心海賊團船長,手術果實能力者,極惡的世代之一',0,2), ('蒙奇·D·龍',42,0,'革命軍總司令,被政府認定為世界最兇惡的罪犯',0,3), ('唐吉訶德·多弗朗明哥',36,0,'王位',0,4);
?
模型類數據已經都就位了,接下來我們去定義視圖找到應用下的views.py
from django.shortcuts import render, redirect from school.models import ClassInfo from datetime import date# 查詢所有班級并顯示 def index(request):class_list = ClassInfo.objects.all()return render(request, 'school/index.html', {'class_list': class_list})# 創建新的班級類 def create(request):c = ClassInfo()c.name = "海賊團 class"c.class_date = date(2019, 6, 21)c.class_size = 0c.isDelete = 0c.save()return redirect('/index.html')# 邏輯刪除指定編號的圖書 def delete(request, cid):c = ClassInfo().objects.get(id=cid)c.isDelete = 1c.save()return redirect('/index.html')
為了讓程序能找到我們的視圖,配置URLconf就顯得尤為重要了,那么顯示在test2下的urls.py
# test2下的urls.py from django.contrib import admin from django.urls import path from django.conf.urls import url, includeurlpatterns = [path('admin/', admin.site.urls),url(r'^', include("school.urls")) ]
等價于
?
# test2下的urls.pyfrom django.contrib import admin from django.urls import path, includeurlpatterns = [path('admin/', admin.site.urls),path('', include("school.urls")) ]
項目下的配置完,去應用下也就【school/urls【1】】配置具體對應的視圖函數
?【1】:這里的urls.py是我們自己創建的,用于配置我們自己的視圖,可以更好的進行路徑管理
from django.conf.urls import url from school import views urlpatterns = [url(r'index$', views.index),url(r'create$', views.create),url(r'delete(\d+)$', views.delete), ]
M和V已經完成,現在就差一個T了,那么就去創建模板,在創建模板之前先去settings.py檢查一下TEMPLATES里面是否已經配置好
?
下面就是模板index.html
<!DOCTYPE html> <html lang="en"> <head><meta charset="UTF-8"><title>school</title><style>a{text-decoration:none}</style> </head> <body><h1 style="text-align: center">Circle & School</h1><HR style="FILTER: alpha(opacity=100,finishopacity=0,style=2)" width="80%" color=#987cb9 SIZE=10><ul style="list-style-type:none">{% for class in class_list %}<li style="text-align: center">{% if class.isDelete != 1 %}<a href="#">{{ class.name }}</a>------------<a href="/delete{{class.id}}">刪除</a></li>{% endif %}</li>{% endfor %}</ul><a href="/create" style="padding-left: 120px;font-size: 30px">創建</a> </body> </html>
?這里開始測試一下,是否成功,那么我們將服務器運行起來
python manage.py runserver
?
?測試創建
?
測試刪除
這里只是邏輯上的刪除,并沒有在數據庫真正的刪除,有一個好處是避免我們誤操作,也可以保存數據,供以后研究
?
定義屬性
Django根據屬性的類型確定以下信息:
-  當前選擇的數據庫使用字段的類型
-  渲染管理表單時使用的默認HTML控件
-  在管理站點最低限度的驗證
django會為表創建自動增長的主鍵列,每個模型只能有一個主鍵列,如果使用選項設置某屬性為主鍵列后django不會再創建自動增長的主鍵列。
屬性命名限制:
-  不能是python的保留關鍵字。
-  不允許使用連續的下劃線,這是由django的查詢方式決定的,在字段查詢的說明會講解為什么不能使用連續下劃線
-  定義屬性時需要指定字段類型,通過字段類型的參數指定選項,語法如下-   屬性 = models.字段類型(選項) ? 
 
-   
字段類型:使用時需要導入django.db.models包
- AutoField:自動增長的IntegerField,默認不需要寫,不寫會自動創建屬性名為id的自動增長屬性
- BooleanField:布爾字段,值?True、False
- NullBooleanField:值為 Null、True、False
- CharField:字符串 - 參數max_length表示最大字符個數
 
- TextField:大文本字段?一般超過4000個字符使用
- IntegerField:整數
- DecimalField:十進制浮點數 - 參數max_digits表示總位數。
- 參數decimal_places表示小數的位數
 
- FloatField:浮點數
- DateField[auto_now=False, auto_now_add=False]):日期。 - 參數auto_now表示每次保存對象時,自動設置該字段為當前時間,用于"最后一次修改"的時間戳,它總是使用當前日期,默認為false。
- 參數auto_now_add表示當對象第一次被創建時自動設置當前時間,用于創建的時間戳,它總是使用當前日期,默認為false。
- 參數auto_now_add和auto_now是相互排斥的,組合將會發生錯誤。
 
- TimeField:時間,參數同DateField。
- DateTimeField:日期時間,參數同DateField。
- FileField:上傳文件字段。
- ImageField:繼承于FileField,對上傳的內容進行校驗,確保是有效的圖片。
選項
通過選項實現對字段的約束,選項如下:
- null:如果為True,表示允許為空,默認值是False。
- blank:如果為True,則該字段允許為空白,默認值是False。
- 對比:null是數據庫范疇的概念,blank是表單驗證范疇的。
- db_column:字段的名稱,如果未指定,則使用屬性的名稱。
- db_index:若值為True, 則在表中會為此字段創建索引,默認值是False。
- default:默認值。
- primary_key:若為True,則該字段會成為模型的主鍵字段,默認值是False,一般作為AutoField的選項使用。
- unique:如果為True, 這個字段在表中必須有唯一值,默認值是False。
?
字段查詢
實現sql中where的功能,通過過濾器filter()、exclude()、get()?
語法如下:
說明:屬性名稱和條件運算符間使用兩個下劃線,所以屬性名不能包括多個下劃線。
過濾器(屬性名__條件運算符=值) # 這里的_是兩個
條件運算符
1.exact :?判斷相等
# 要求: 查詢id為1的班級 c = ClassInfo.objects.filter(id__exact=1)# 等價于 c = ClassInfo.objects.filter(id=1)
這里ClassInfo.objects.filter(id__exact=1)返回的是一個Queryset,簡單理解Queryset就像Python中的list
<QuerySet [<ClassInfo: ClassInfo object (1)>]>說明里面只有一個ClassInfo對象
我們可以用c[0]查看name屬性
得到的就是id=1那個屬性的名字為python class
想詳細了解Queryset的話,后續會推出
2. contains:?判斷是否包含
# 查詢班級名是否包含'python'的班級 c = ClassInfo.objects.filter(name__contains=‘python’)
?3.startwith、endswith:?查詢以指定開頭或結尾
# 查詢班級以class結尾的班級 c = ClassInfo.objects.filter(name__endswith='class')
?4.isnull:查詢是否為null
# 查詢班級名不為null的班級 c = ClassInfo.objects.filter(name__isnull=False)
5.in:?是否包含在范圍內
# 查詢id為1、3、17的班級 c = ClassInfo.objects.filter(id__in=[1,3,17])
# 這里會返回三個ClassInfo對象
# 上面查詢的都是在數據庫里有的數據,如果要是查詢一個不存在的數據會怎樣? c = ClassInfo.objects.filter(id__in=[1,3,5]) # 可以通過上面的數據看出,這里1,3都是包含在內的,但是5是不包含在內的
結果可以的看出,這里返回了除了id=5的所有相等的ClassInfo對象
6.比較查詢--->?gt、gte、lt、lte:大于、大于等于、小于、小于等于。
# id大于3的班級 c = ClassInfo.objects.filter(id__gt=3)
7.不等可以使用exclude()過濾器
# 查詢不等id為3的班級 c = ClassInfo.objects.exclude(id=3)
可以看出上面沒有返回id=3的ClassInfo對象
8.日期查詢---->year、month、day、week_day、hour、minute、second
# 查詢2019年開班的班級 c = ClassInfo.objects.filter(class_date__year=2019)
# 查詢2019年6月20日以后開班的班級 from datetime import date c = ClassInfo.objects.filter(class_date__gt=date(2019,6,20))
兩個屬性相比較怎么辦?那么就來介紹一個F對象,但是沒什么辦法好舉例,所以下面加一個屬性
?
讓我們來復習一下,之后需要用到生成遷移文件,和遷移
python manage.py makemigrationspython manage.py migrate   然后修改一id為一的班級那條數據庫,將reported_number?改為14
UPDATE `school_classinfo` SET `name` = 'python class', `class_date` = '2019-06-21',`class_size` = 14, `reported_number` = 14, `isDelete` = 0 WHERE `school_classinfo`.`id` = 1;
數據準備完畢,我們來接著學習F對象
# 查詢班級人數和報道人數相等的數據,也就是兩個屬性間的比較 from django.db.models import F # 想要使用F對象,必須要導包 c = ClassInfo.objects.filter(class_size=F('reported_number'))
這里報道人數和班級人數相等的只有兩個,分別是id=1、id=17的
Q對象
多個過濾器逐個調用表示邏輯與 [&] 關系,同SQL中的where里的and關鍵字
Q對象被義在django.db.models中。
# 查詢班級人數大于10,并且報道人數大于10的 c = ClassInfo.objects.filter(class_size__gt=10,reported_number__gt=10) 或者 c = ClassInfo.objects.filter(class_size__gt=10).filter(reported_number__gt=10) 或者 from django.db.models import Q c = ClassInfo.objects.filter(Q(class_size__gt=10) & Q(reported_number__gt=10))
?
如果需要實現邏輯或 [ | ] 的查詢,需要使用Q()對象結合 | 運算符,同SQL語句中的where里的or關鍵字
# 查詢班級人數大于10的,或者id 小于3 的班級 c = ClassInfo.objects.filter(Q(class_size__gt=10) | Q(id__lt=3))
這里返回了兩個ClassInfo對象,是因為或只要滿足一個條件就成立:
- id=1?的是都成立,既滿足班級人數大于10.,也滿足id小于3
- id=2?的id小于3這個成立,所以也會返回在Queryset中
Q也可以使用非~操作符
# 查詢id不等于3的班級 c = ClassInfo.objects.filter(~Q(id=3))
這里除了id=3的班級都會返回到Queryset中
聚合函數:使用aggregate()過濾器調用聚合函數[Avg,Count,Max,Min,Sum]使用時候需要導入django.db.models
# 查詢所有學生 from django.db.models import Sum c_dict = ClassInfo.objects.aggregate(Sum('class_size'))
使用count時一般不使用aggregate()過濾器。
# 查詢班級總數 class_count = ClassInfo.objects.count()
count()返回的是一個數字
Queryset:Queryset表示從數據庫中獲取的對象集合,在管理器上調用某些過濾器方法會返回Queryset,Queryset可以含有0個、1、或者多個過濾器,過濾器基于所給的參數有限制查詢結果,從SQL的角度,Queryset和select語句等價,過濾器類似where、limit
返回多個值的過濾器:
- all():?返回所有數據
- filter():?返回滿足條件的數據
- exclude():返回滿足條件之外的數據
- order_by():?對結果進行排序
返回單個值得過濾器:
- get():返回單個滿足條件的對象 - 如果沒找到會報DoesNotExist異常
- 如果多條被返回,會返回MultipleObjectsReturned異常
 
- count():返回當前查詢結果的總條數
- aggregate():?聚合,返回一個字典
判斷某一個Queryset中是否有數據:
exists():?判斷查詢集中是否有數據,如果有返回True?否則返回False
三大特性
- 惰性執行:創建Queryset不會訪問數據庫,直到調用數據時,才會訪問數據庫,調用數據的情況包括迭代、序列化、與if合用
- 緩存:使用同一個Queryset,第一次使用時會發生數據庫的查詢,然后把結果緩存下來,再次使用這個Queryset時,返回的是緩存數據
- 限制查詢:可以對Queryset進行取下標或切片操作,等同于sql中的limit和offset子句。注意:不支持負數索引。 - 如果獲取一個對象,直接使用[0],但是如果沒有數據,[0]引發IndexError異常
 
模型類關系
關系型數據庫的關系包括三種:
-  ForeignKey:一對多,將屬性定義在多的中
-  ManyToManyField:多對多,將屬性定義在任意類中
-  OneToOneField:一對一將屬性定義在任意類中
-  可以維護遞歸的關聯關系,使用self指定,應用在自關聯
一對多關系:之前我們一直使用的ClassInfo類和StudentInfo類就是一對多的關系
多對多的關系:創建一個課程表類與學生,每個學生有多門課程,一門課程也會有很多學生,在創建多對多關系的時候,除了學生表和課程表之外會多創建一個中間表,分別都是他們的id
# 在models.py中創建課程表類 class Timetable(models.Model):"""課程表模型類"""name = models.CharField(max_length=30) # 課程表名稱school_time = models.TimeField() # 課程時間stu_table = models.ManyToManyField(StudentInfo) # 課程表和學生屬于多對多的關系
生成遷移文件,并且遷移,
python manage.py makemigrations
python manage.py migrate   ?
關聯查詢:Django也能實現類似SQL中的join查詢
通過對象執行關聯查詢:在定義模型類的時候可以指定關聯關系,最常用的就是一對多的關系
# 查詢id為1的班級下的所有學生 # 1.首先來獲取id為1的班級 c = ClassInfo.objects.get(id=1) s = c.studentinfo_set.all() # 這里需要由ClassInfo類.學生類名小寫_set
# SQL語句
SELECT * FROM `school_studentinfo` WHERE `school_studentinfo`.`the_class_id` = 1 LIMIT 21;
?
通過SQL語句來來對比一下在Python?manage.py shell?中的數據:
# 查詢id=18的學生所在的班級 s = StudentInfo.objects.get(id=18) s.the_class
# SQL語句 SELECT * FROM `school_classinfo` WHERE `school_classinfo`.`id` = 1;
?
通過多模型類訪問關聯一模型類的語法
# 多對應的模型類對象.關聯類屬性_id s = StudentInfo.objects.get(id=18) s.the_class_id 對比 s.the_class
讓我們測試一下所學的關聯查詢
# 查詢id為1的班級 c = ClassInfo.objects.get(id=1) # SELECT * FROM `school_classinfo` WHERE `school_classinfo`.`id` = 1;
# 獲取C班級的所有學生 c.studentinfo_set.all() # SELECT * FROM `school_studentinfo` WHERE `school_studentinfo`.`the_class_id` = 1? LIMIT 21;
# 獲取id = 18 的學生 s = StudentInfo.objects.get(id=18) # SELECT * FROM `school_studentinfo` WHERE `school_studentinfo`.`id` = 18;
# s這個學生所在的班級 s.the_class # SELECT * FROM `school_classinfo` WHERE `school_classinfo`.`id` = 1;
通過模型類執行關聯查詢
?由多模型類條件查詢一模型類數據語法:
關聯模型類名小寫__屬性名__條件運算符 = 值
# 那么讓我們做一個例子 # 查詢班級,要求班級中的學生愛好里包含‘肉’的,返回所在的班級 class_list = ClassInfo.objects.filter(studentinfo__interest__contains = '肉')
?
# 多模型類查詢,但是由一模型類條件查詢 # 查詢班級名為python class班下的所有學生 stu_list = StudentInfo.objects.filter(the_class__name = 'python class')
自關聯:地區信息、分類信息、等等數據,表結構基本固定的,每個表的數據量不是特別龐大的,為了充分利用數據表的搭理那個數據存儲功能,可以設計成一張表,內部的關系字段指向本表的主鍵,這就是自關聯的表結構
# 第一步咱們開始準備模型類 models.py 下創建AreaInfo模型類
class AreaInfo(models.Model):"""地區模型類"""name = models.CharField(max_length=50) # 地區名稱area_parent = models.ForeignKey('self', on_delete=models.CASCADE, null=True, blank=True) # 關系鍵
【擴展】:在設置外鍵時,需要通過on_delete選項指明主表刪除數據時,對于外鍵引用表數據如何處理,在 django.db.models中包含了可選常量:
關聯屬性on_delete選項的取值
-  models.CASCADE?此為默認值,級聯刪除,會刪除關聯數據models.ForeignKey('self', on_delete=models.CASCADE)
-  models.PROTECT?只要存在關聯數據就不能刪除models.ForeignKey('self', on_delete=models.PROTECT)
-  models.SET_NULL?刪除數據后關聯字段設置為NULL,僅在該字段允許為null時可用(null=True)
   models.ForeignKey('self',?on_delete=models.PROTECT,null=True)
生成遷移文件,并且遷移
python manage.py makemigrations # 生成遷移文件 python manage.py migrate # 遷移
接下來就是將省、市、區縣的數據導入
我在windows的環境下,所以編碼問題很是頭疼這里是通過Navicat導入的數據:
數據在這里:鏈接:https://pan.baidu.com/s/1SIPtBmQRoEnL_DYDYIB9qQ 提取碼:r3px
?M準備完成,開始準備V,定義視圖views.py下的area
# 這里需要導入AreaInfo這個類 from school.models import AreaInfo # 創建地區信息的視圖 def area(request):# 獲取數據,由于數據太多咱們選擇一個省的數據 230100=哈爾濱area = AreaInfo.objects.get(id=230100)return render(request, 'school/area.html', {'area': area})
設置URLconf配置
1 from django.conf.urls import url 2 from school import views 3 urlpatterns = [ 4 url(r'index$', views.index), 5 url(r'create$', views.create), 6 url(r'delete(\d+)$', views.delete), 7 url(r'area$', views.area), # 新增的url 8 ]
Django框架MVT,MV已經準備完,現在來創建模板(T)area.html文件
<!DOCTYPE html> <html lang="en"> <head><meta charset="UTF-8"><title>Title</title> </head> <body><h1 style="text-align: center">Circle & 地區</h1><HR style="FILTER: alpha(opacity=100,finishopacity=0,style=2)" width="80%" color=#987cb9 SIZE=10><div style="padding-left: 200px">當前市: {{ area.name }}</div><HR style="FILTER: alpha(opacity=100,finishopacity=0,style=2)" width="80%" color=#987cb9 SIZE=10><div style="padding-left: 200px">{{ area.name }}的省是: {{ area.area_parent.name }}</div><HR style="FILTER: alpha(opacity=100,finishopacity=0,style=2)" width="80%" color=#987cb9 SIZE=10><div style="padding-left: 200px">下級區 縣 市:<ul>{% for a in area.areainfo_set.all %}<li>{{ a.name }}</li>{% endfor %}</ul></div> </body> </html>
返回的結果是
模型類實例方法:
- str():?將對象轉換成字符串
- save():?將對象保存到數據表中
- delete():?將對象從數據表中刪除
模型類的屬性
  objects
  模型當中最重要的屬性是?Manager。它是Django模型和數據庫查詢操作之間的接口,并且它被用作從數據庫? ? ? ? ?當中查詢,如果沒有指定自定義的?Manager?默認名稱是?objects。Manager只能通過模型類來訪問,不能通? ? ? ? ? ?過模型實例來訪問。
管理器(Manager):是向Django模型提供數據庫查詢操作的接口。Django應用程序中的每個模型至少有一個管理器。
管理器命名(Manager):默認Django會為每一個模型類添加一個命名為objects的管理器 ,如果你想用objects作為字段名,或者你想用除了objects以外的名字為這個管理器命名,你可以根據模型類來重命名,將重命名的管理器給定一個類,在這個模型類上定義一個models.Manager()類型的類屬性。例如:
from django.db import modelsclass ClassInfo(models.Model):class_manager = models.Manager()
使用上面的例子,ClassInfo.objects將會產生一個AttributeError的異常,但是ClassInfo.class_manager.all()?將會提供ClassInfo對象的列表
自定義管理器:你可以用一個自定義管理器在模型中擴展這個基本的管理器,和在模型中實例化自定義管理器。
自定義管理器有兩個好處,也許正是你想要修改管理器的兩個原因
1.修改管理器返回的初始Queryset。
2.添加額外的管理器方法
之前我們使用的all()函數都是直接返回Queryset,這里我來學習一下修改管理器返回的初始Queryset
# 首先我們來定義一個管理器 class ClassInfoManager(models.Manager):"""班級管理器"""# 重寫all()方法def all(self):# 調用的父類的all()方法,默認獲取所有的班級信息class_model = super().all()# 修改查詢的結果,將isDelete 為 True 的不返回# 返回結果return class_model.filter(isDelete=False)class ClassInfo(models.Model):"""班級模型類"""name = models.CharField(max_length=20) # 班級名class_date = models.DateField() # 開班日期class_size = models.IntegerField(default=0) # 班級人數 默認0reported_number = models.IntegerField(default=0) # 報到人數isDelete = models.BooleanField(default=False) # 邏輯刪除 默認Falseobjects = ClassInfoManager() # 將自定義的管理器賦值給objects管理器對象
第二個好處就是添加額外的管理器方法,還記得之前在網站點擊創建就會添加一個班級,這里我們可以將它把創建方法封裝到models的管理器中
管理器:models.py
# 首先我們來定義一個管理器 class ClassInfoManager(models.Manager):"""班級管理器"""# 重寫all()方法def all(self):# 調用的父類的all()方法,默認獲取所有的班級信息class_model = super().all()# 修改查詢的結果,將isDelete 為 True 的不返回# 返回結果return class_model.filter(isDelete=False)# 創建一個新的方法:添加班級def create_class(self, name, class_date, class_size=0, reported=0):# 創建對象c = self.model() # 等價于 c = ClassInfo()# print(type(c)) # 測試self.model()的返回值類型c.name = namec.class_date = class_datec.class_size = class_sizec.reported_number = reportedc.isDelete = Falsec.save()return c
?
視圖:views.py
# 創建新的班級類 def create(request):"""c = ClassInfo()c.name = "海賊團 class"c.class_date = date(2019, 6, 21)c.class_size = 0c.isDelete = 0c.save()return redirect('/index')"""ClassInfo.objects.create_class("web class", '2019-06-23')return redirect('/index')
?
元選項(Meta options) : 設置元信息,需要在模型類中定義一個Meta的內部類
這貨到底有啥用,簡單的說明一下。當我們定義完模型類的時候,生成的數據庫表名都是?應用名_模型類名(例如:school_classinfo),但是我還不想讓他在數據庫生成這種名字,我們就可以用Meta當中的一個選項,就是db_table,設置數據庫的表名,具體如何操作呢?我們就動手寫一下
# 這里我們就用課程表模型類演示 class Timetable(models.Model):"""課程表模型類"""name = models.CharField(max_length=30) # 課程表名稱school_time = models.TimeField() # 課程時間stu_table = models.ManyToManyField(StudentInfo) # 課程表和學生屬于多對多的關系class Meta:db_table = 'timetable' # 設置表名為timetable
?
?其實仔細想想,這也是與應用名之間的解耦,還有很多的元選項之后有時間,可以做一個專輯
?
?
-To Be Continued-
- The?end -
轉載于:https://www.cnblogs.com/Hannibal-2018/p/11055936.html
總結
以上是生活随笔為你收集整理的Django-C002-深入模型,到底有多深的全部內容,希望文章能夠幫你解決所遇到的問題。
 
                            
                        - 上一篇: shred命令
- 下一篇: 求一个陪伴的个性签名!
