Model模型
- 模型是你的數據的唯一的、權威的信息源。它包含你所儲存數據的必要字段和行爲。
- 通常,每個模型對應數據庫中唯一的一張表。
- 每個模型都是
django.db.models.Model
的一個Python 子類。 - 模型的每個屬性都表示爲數據庫中的一個字段。
- Django 提供一套自動生成的用於數據庫訪問的API;
- 這極大的減輕了開發人員的工作量,不需要面對因數據庫變更而導致的無效勞動。
- 備註:本文基於django版本1.11展開, 技術棧會有所升級, 僅供參考!
模型與數據庫的關係
- 模型(Model)負責業務對象和數據庫的關係映射(ORM)
- ORM是“對象-關係-映射”的簡稱,主要任務是:
- 根據對象的類型生成表結構
- 將對象、列表的操作,轉換爲sql語句
- 將sql查詢到的結果轉換爲對象、列表
- 使用ORM,我們就可以不用像以前一樣寫sql語言了
爲什麼要用模型
- Model是MVC框架中重要的一部分,主要負責程序中用於處理數據邏輯的部分。通常模型對象負責在數據庫中存取數據
- 它實現了數據模型與數據庫的解耦,即數據模型的設計不需要依賴於特定的數據庫,通過簡單的配置就可以輕鬆更換數據庫
配置Mysql數據庫
1 ) 在當前環境中安裝mysql
- $
sudo apt-get install mysql-server
- $
sudo apt install mysql-client
- $
sudo apt install libmysqlclient-dev
2 ) 在當前python環境中安裝 pymysql
- $
pip3 install pymysql
3 ) 在mysql中創建數據庫
- $
create databases d_models_db default charset=utf8
4 ) 在Django項目中配置數據庫
- 修改settings.py文件中的DATABASE配置項
DATABASES = { 'default': { 'ENGINE': 'django.db.backends.mysql', 'NAME': 'd_models_db', # 選擇數據庫的名, 請確認你的mysql中有這個庫 'USER': 'root', 'PASSWORD': '', 'HOST': 'localhost', 'PORT': '3306', } }
5 ) 告訴Django在接下來的mysql操作中使用pymysql
- 打開主項目目錄下的__init__.py,寫入以下代碼導入pymysql:
import pymysql pymysql.install_as_MySQLdb()
開發流程
1 ) 在models.py中定義模型類,要求繼承自models.Model
from django.db import models
from datetime import datetime
# Create your models here.
class Users(models.Model):
name = models.CharField(max_length=32)
age = models.IntegerField(default=20)
phone = models.CharField(max_length=16)
addtime=models.DateTimeField(default=datetime.now)
class Meta:
db_table = "users" # 指定表名
2 ) 把應用加入settings.py文件的installed_app項
-
編輯主項目目錄下的settings.py文件,並將項目應用文件名添加到該INSTALLED_APPS設置。
INSTALLED_APPS = [ 'django.contrib.admin', 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', 'app', ]
-
生成遷移文件
- $
python3 manage.py makemigrations
django.core.exceptions.ImproperlyConfigured: Error loading MySQLdb module: No module named 'MySQLdb'. Did you install mysqlclient or MySQL-python? # 安裝上面"配置Mysql數據庫"的第五步配置,或安裝:pip install mysqlclient後就可以了
- 若執行 $
python3 manage.py makemigrations
命令(可能把migrations文件夾給刪了),會提示"No changes detected." 可能有用的解決方式如下:- 先 $
python3 manage.py makemigrations --empty yourappname
生成一個空的initial.py - 再 $
python3 manage.py makemigrations
生成原先的model對應的migration file
- 先 $
- $
-
執行遷移
- $
python3 manage.py migrate
- $
-
使用模型類進行crud操作
-
備註:在實際項目中生成遷移文件和執行遷移這兩部基本不會出現,因爲我們的數據庫基本是已經設計好的
定義模型
- 在模型中定義屬性,會生成表中的字段
- django根據屬性的類型確定以下信息:
- 當前選擇的數據庫支持字段的類型
- 渲染管理表單時使用的默認html控件
- 在管理站點最低限度的驗證
- django會爲表增加自動增長的主鍵列,每個模型只能有一個主鍵列,如果使用選項設置某屬性爲主鍵列後,則django不會再生成默認的主鍵列
- 屬性命名限制
- 不能是python的保留關鍵字
- 由於django的查詢方式,不允許使用連續的下劃線
定義屬性
- 定義屬性時,需要字段類型
- 字段類型被定義在
django.db.models.fields
目錄下,爲了方便使用,被導入到django.db.models
中 - 使用方式
- 導入
from django.db import models
- 通過
models.Field
創建字段類型的對象,賦值給屬性
- 導入
- 對於重要數據都做邏輯刪除,不做物理刪除,實現方法是定義
isDelete
屬性,類型爲BooleanField
,默認值爲False
1 ) 字段類型
AutoField
:一個根據實際ID自動增長的IntegerField,通常不指定- 如果不指定,一個主鍵字段將自動添加到模型中
BooleanField
:true/false 字段,此字段的默認表單控制是CheckboxInputNullBooleanField
:支持null、true、false三種值CharField(max_length=字符長度)
:字符串,默認的表單樣式是 TextInputTextField
:大文本字段,一般超過4000使用,默認的表單控件是TextareaIntegerField
:整數DecimalField(max_digits=None, decimal_places=None)
:使用python的Decimal實例表示的十進制浮點數DecimalField.max_digits
:位數總數DecimalField.decimal_places
:小數點後的數字位數
FloatField
:用Python的float實例來表示的浮點數DateField[auto_now=False, auto_now_add=False])
:使用Python的datetime.date實例表示的日期- 參數
DateField.auto_now
:每次保存對象時,自動設置該字段爲當前時間,用於"最後一次修改"的時間戳,它總是使用當前日期,默認爲false - 參數
DateField.auto_now_add
:當對象第一次被創建時自動設置當前時間,用於創建的時間戳,它總是使用當前日期,默認爲false - 該字段默認對應的表單控件是一個TextInput. 在管理員站點添加了一個JavaScript寫的日曆控件,和一個“Today"的快捷按鈕,包含了一個額外的invalid_date錯誤消息鍵
auto_now_add
,auto_now
這些設置是相互排斥的,他們之間的任何組合將會發生錯誤的結果
- 參數
TimeField
:使用Python的datetime.time實例表示的時間,參數同DateFieldDateTimeField
:使用Python的datetime.datetime實例表示的日期和時間,參數同DateFieldFileField
:一個上傳文件的字段ImageField
:繼承了FileField的所有屬性和方法,但對上傳的對象進行校驗,確保它是個有效的image
2 ) 字段選項
- 通過字段選項,可以實現對字段的約束
- 在字段對象時通過關鍵字參數指定
null
:如果爲True,Django 將空值以NULL 存儲到數據庫中,默認值是 Falseblank
:如果爲True,則該字段允許爲空白,默認值是 False- 對比:null是數據庫範疇的概念,blank是表單驗證證範疇的
db_column
:字段的名稱,如果未指定,則使用屬性的名稱db_index
:若值爲 True, 則在表中會爲此字段創建索引default
:默認值primary_key
:若爲 True, 則該字段會成爲模型的主鍵字段unique
:如果爲 True, 這個字段在表中必須有唯一值
3 ) 關係
- 關係的類型包括
ForeignKey
:一對多,將字段定義在多端中ManyToManyField
:多對多,將字段定義在兩端中OneToOneField
:一對一,將字段定義在任意一端中
- 可以維護遞歸的關聯關係,使用’self’指定,詳見“自關聯”
- 用一訪問多:對象.模型類小寫_set
bookinfo.heroinfo_set
- 用一訪問一:對象.模型類小寫
heroinfo.bookinfo
- 訪問id:對象.屬性_id
heroinfo.book_id
元選項
-
在模型類中定義類
Meta
,用於設置元信息 -
元信息
db_table
:定義數據表名稱,推薦使用小寫字母 -
數據表的默認名稱:<app_name>_<model_name>
-
例如:
djangoApp_users
-
ordering
:對象的默認排序字段,獲取對象的列表時使用,接收屬性構成的列表class BookInfo(models.Model): ... class Meta(): ordering = ['id']
-
字符串前加-表示倒序,不加-表示正序
class BookInfo(models.Model): ... class Meta(): ordering = ['-id']
-
排序會增加數據庫的開銷
示例演示
- 創建test2項目,並創建booktest應用,使用mysql數據庫
- 定義圖書模型
class BookInfo(models.Model): btitle = models.CharField(max_length=20) bpub_date = models.DateTimeField() bread = models.IntegerField(default=0) bcommet = models.IntegerField(default=0) isDelete = models.BooleanField(default=False)
- 英雄模型
class HeroInfo(models.Model): hname = models.CharField(max_length=20) hgender = models.BooleanField(default=True) isDelete = models.BooleanField(default=False) hcontent = models.CharField(max_length=100) hbook = models.ForeignKey('BookInfo')
- 定義index、detail視圖
- index.html、detail.html模板
- 配置url,能夠完成圖書及英雄的展示
測試數據
-
模型BookInfo的測試數據
insert into booktest_bookinfo(btitle,bpub_date,bread,bcommet,isDelete) values ('射鵰英雄傳','1980-5-1',12,34,0), ('天龍八部','1986-7-24',36,40,0), ('笑傲江湖','1995-12-24',20,80,0), ('雪山飛狐','1987-11-11',58,24,0)
-
模型HeroInfo的測試數據
insert into booktest_heroinfo(hname,hgender,hbook_id,hcontent,isDelete) values ('郭靖',1,1,'降龍十八掌',0), ('黃蓉',0,1,'打狗棍法',0), ('黃藥師',1,1,'彈指神通',0), ('歐陽鋒',1,1,'蛤蟆功',0), ('梅超風',0,1,'九陰白骨爪',0), ('喬峯',1,2,'降龍十八掌',0), ('段譽',1,2,'六脈神劍',0), ('虛竹',1,2,'天山六陽掌',0), ('王語嫣',0,2,'神仙姐姐',0), ('令狐沖',1,3,'獨孤九劍',0), ('任盈盈',0,3,'彈琴',0), ('嶽不羣',1,3,'華山劍法',0), ('東方不敗',0,3,'葵花寶典',0), ('胡斐',1,4,'胡家刀法',0), ('苗若蘭',0,4,'黃衣',0), ('程靈素',0,4,'醫術',0), ('袁紫衣',0,4,'六合拳',0)
模型實例
1 ) 類的屬性
objects
:是Manager類型的對象,用於與數據庫進行交互- 當定義模型類時沒有指定管理器,則Django會爲模型類提供一個名爲objects的管理器
- 支持明確指定模型類的管理器
class BookInfo(models.Model): ... books = models.Manager()
- 當爲模型類指定管理器後,django不再爲模型類生成名爲objects的默認管理器
2 ) 創建對象
- 當創建對象時,django不會對數據庫進行讀寫操作
- 調用
save()
方法才與數據庫交互,將對象保存到數據庫中 - 使用關鍵字參數構造模型對象很麻煩,推薦使用下面的兩種之式
- 說明:init方法已經在基類
models.Model
中使用,在自定義模型中無法使用
3 ) 實例的屬性
DoesNotExist
:在進行單個查詢時,模型的對象不存在時會引發此異常,結合try/except使用
4 ) 實例的方法
__str__(self)
:重寫object方法,此方法在將對象轉換成字符串時會被調用save()
:將模型對象保存到數據表中delete()
:將模型對象從數據表中刪除
模型查詢
- 查詢集表示從數據庫中獲取的對象集合
- 查詢集可以含有零個、一個或多個過濾器
- 過濾器基於所給的參數限制查詢的結果
- 從Sql的角度,查詢集和select語句等價,過濾器像where和limit子句
- 接下來主要討論如下知識點
- 查詢集
- 字段查詢:比較運算符,F對象,Q對象
1 ) 查詢集
- 在管理器上調用過濾器方法會返回查詢集
- 查詢集經過過濾器篩選後返回新的查詢集,因此可以寫成鏈式過濾
- 惰性執行:創建查詢集不會帶來任何數據庫的訪問,直到調用數據時,纔會訪問數據庫
- 何時對查詢集求值:迭代,序列化,與if合用
- 返回查詢集的方法,稱爲過濾器
all()
filter()
exclude()
order_by()
values()
: 一個對象構成一個字典,然後構成一個列表返回
- 寫法:
filter(鍵1=值1,鍵2=值2) 等價於 filter(鍵1=值1).filter(鍵2=值2)
- 返回單個值的方法
get()
:返回單個滿足條件的對象- 如果未找到會引發"模型類.DoesNotExist"異常
- 如果多條被返回,會引發"模型類.MultipleObjectsReturned"異常
count()
:返回當前查詢的總條數first()
:返回第一個對象last()
:返回最後一個對象exists()
:判斷查詢集中是否有數據,如果有則返回True
限制查詢集
- 查詢集返回列表,可以使用下標的方式進行限制,等同於sql中的limit和offset子句
- 注意:不支持負數索引
- 使用下標後返回一個新的查詢集,不會立即執行查詢
- 如果獲取一個對象,直接使用[0],等同於[0:1].get(),但是如果沒有數據,[0]引發IndexError異常,[0:1].get()引發DoesNotExist異常
#這會返回前5個對象 LIMIT 5 Entry.objects.all()[:5] #這將返回第六個到第十個對象 OFFSET 5 LIMIT 5 Entry.objects.all()[5:10]
查詢集的緩存
- 每個查詢集都包含一個緩存來最小化對數據庫的訪問
- 在新建的查詢集中,緩存爲空,首次對查詢集求值時,會發生數據庫查詢,django會將查詢的結果存在查詢集的緩存中,並返回請求的結果,接下來對查詢集求值將重用緩存的結果
- 情況一:這構成了兩個查詢集,無法重用緩存,每次查詢都會與數據庫進行一次交互,增加了數據庫的負載
print([e.title for e in Entry.objects.all()]) print([e.title for e in Entry.objects.all()])
- 情況二:兩次循環使用同一個查詢集,第二次使用緩存中的數據
querylist=Entry.objects.all() print([e.title for e in querylist]) print([e.title for e in querylist])
- 何時查詢集不會被緩存:當只對查詢集的部分進行求值時會檢查緩存,但是如果這部分不在緩存中,那麼接下來查詢返回的記錄將不會被緩存,這意味着使用索引來限制查詢集將不會填充緩存,如果這部分數據已經被緩存,則直接使用緩存中的數據
2 ) 字段查詢
- 實現where子名,作爲方法filter()、exclude()、get()的參數
- 語法:屬性名稱__比較運算符=值
- 表示兩個下劃線,左側是屬性名稱,右側是比較類型
- 對於外鍵,使用“屬性名_id”表示外鍵的原始值
- 轉義:like語句中使用了%與,匹配數據中的%與,在過濾器中直接寫,例如:filter(title__contains="%")=>where title like ‘%%%’,表示查找標題中包含%的
比較運算符
-
exact
:表示判等,大小寫敏感;如果沒有寫“ 比較運算符”,表示判等filter(isDelete=False)
-
contains
:是否包含,大小寫敏感exclude(btitle__contains='www')
-
startswith
、endswith
:以value開頭或結尾,大小寫敏感exclude(btitle__endswith='com')
-
isnull
、isnotnull
:是否爲nullfilter(btitle__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))
跨關聯關係的查詢:處理join查詢
- 語法:模型類名 <屬性名> <比較>
- 注:可以沒有__<比較>部分,表示等於,結果同inner join
- 可返向使用,即在關聯的兩個模型中都可以使用
filter(heroinfo_ _hcontent_ _contains='八')
聚合函數
- 使用
aggregate()
函數返回聚合函數的值 - 函數:
Avg
,Count
,Max
,Min
,Sum
from django.db.models import Max maxDate = list.aggregate(Max('bpub_date'))
count
的一般用法count = list.count()
Model模型的實戰
1 ) 創建數據庫和表(若是執行表遷移,可跳過下面操作)
- 進入MySQL數據庫創建數據庫:
d_models_db
- 進入數據庫創建數據表:users
CREATE TABLE `myapp_users` ( `id` int(10) unsigned NOT NULL AUTO_INCREMENT, `name` varchar(32) NOT NULL, `age` tinyint(3) unsigned NOT NULL DEFAULT '20', `phone` varchar(16) DEFAULT NULL, `addtime` datetime(6) NOT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8
- 備註:當然也可以通過遷移文件生成users表(需要使用Meta來改名)
- 添加幾條測試數據
2 ) 創建項目djangoApp和應用app
- 創建項目框架 djangoApp $
django-admin startproject djangoApp && cd djangoApp
- 在項目中創建一個myapp應用 $
python3 manage.py startapp app
- 創建模板目錄 $
mkdir tpls && cd mkdir tpls/app
- 注:最外層的目錄
Django-1.11.11-model
是改名而來的 - 項目目錄結構:
Django-1.11.11-model ├── djangoApp │ ├── __init__.py │ ├── settings.py │ ├── urls.py │ └── wsgi.py ├── manage.py ├── app │ ├── admin.py │ ├── apps.py │ ├── __init__.py │ ├── models.py │ ├── tests.py │ └── views.py └── tpls └── app
3 ) 執行數據庫連接配置,網站配置
-
編輯djangoApp/init.py文件,添加Pymysql的數據庫操作支持
import pymysql pymysql.install_as_MySQLdb()
-
編輯djangoApp/settings.py文件,配置數據庫連接
# ... #配置自己的服務器IP地址 ALLOWED_HOSTS = ['localhost','127.0.0.1','*'] # ... #添加自己應用 INSTALLED_APPS = [ 'django.contrib.admin', 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', 'app', ] # ... # 配置模板路徑信息 TEMPLATES = [ { 'BACKEND': 'django.template.backends.django.DjangoTemplates', 'DIRS': [os.path.join(BASE_DIR,'tpls')], 'APP_DIRS': True, 'OPTIONS': { 'context_processors': [ 'django.template.context_processors.debug', 'django.template.context_processors.request', 'django.contrib.auth.context_processors.auth', 'django.contrib.messages.context_processors.messages', ], }, }, ] # ... # 數據庫連接配置 DATABASES = { 'default': { 'ENGINE': 'django.db.backends.mysql', 'NAME': 'd_models_db', 'USER': 'root', 'PASSWORD': '', # 填寫自己的數據庫密碼 'HOST': 'localhost', 'PORT': '3306', } # ...
4 ) 定義Model類
-
編輯djangoApp/models.py
from django.db import models from datetime import datetime class Users(models.Model): name = models.CharField(max_length=32) age = models.IntegerField(default=20) phone = models.CharField(max_length=20) createTime = models.DateTimeField(default=datetime.now) def __str__(self): return self.name+":"+self.phone class Meta: db_table = "users" # 指定表名
-
測試Model類的使用, 執行 $
python3 manage.py shell
[root@localhost djangoApp]# python3 manage.py shell >>> from app.models import Users >>> Users.objects.all() <QuerySet [<Users: 張三:12345678901>, <Users: aa:13456789023>]> >>> s = Users.objects.get(id=1) >>> s.id 1 >>> s.name '張三' >>> s.age 20 >>> s.phone '12345678901'
5 ) 實現Web端訪問
-
編輯djangoApp/settings.py文件.做 ALLOWED_HOSTS 主機訪問配置(若之前已做可跳過此步驟)
ALLOWED_HOSTS = ['localhost','127.0.0.1','這裏填寫主機的ip地址', '*']
-
編寫項目主路由urls配置,配置對djangoApp應用路由的訪問連接配置
from django.conf.urls import include,url from django.contrib import admin urlpatterns = [ url(r'^admin/', admin.site.urls), url(r'^', include('app.urls')), ]
-
配置當前應用djangoApp的路由配置
- 在djangoApp應用目錄下創建一個路由文件urls.py文件,注意此文件編碼爲utf-8(建議複製一個)。
- 編輯應用中的路由配置文件:djangoApp/app/urls.py, 內容如下:
from django.conf.urls import url from . import views urlpatterns = [ url(r'^$', views.index, name="index"), ]
-
編輯視圖文件:djangoApp/views.py,內容如下
#from django.shortcuts import render from django.http import HttpResponse from myapp.models import Users def index(request): try: s = Users.objects.get(id=1) return HttpResponse(s) except: return HttpResponse("沒有找到對應的信息!")
-
測試環節:在項目根目錄下運行 $
python3 manage.py runserver
命令,開啓服務 -
打開瀏覽器,在瀏覽其中輸入網址測試:
127.0.0.1:8000
-
繼續上面操作完成Web版的Users信息的crud操作,詳見下面示例倉庫
6 ) 示例操作代碼倉庫詳見