在上一個章節中,咱們提到了Django是基於MVC架構的Web框架,MVC架構追求的是「模型」和「視圖」的解耦合。所謂「模型」說得更直白一些就是數據,因此一般也被稱做「數據模型」。在實際的項目中,數據模型一般經過數據庫實現持久化操做,而關係型數據庫在很長一段時間都是持久化的首選方案,下面咱們以MySQL爲例來講明如何使用關係型數據庫來實現持久化操做。python
咱們繼續來完善上一個章節中的OA項目,首先從配置項目使用的數據庫開始。mysql
修改項目的settings.py文件,首先將咱們以前建立的應用hrs添加已安裝的項目中,而後配置MySQL做爲持久化方案。git
(venv)$ cd oa/settings.py
# 此處省略上面的代碼 INSTALLED_APPS = [ 'django.contrib.admin', 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', 'hrs', ] DATABASES = { 'default': { 'ENGINE': 'django.db.backends.mysql', 'NAME': 'oa', 'HOST': 'localhost', 'PORT': 3306, 'USER': 'root', 'PASSWORD': '123456', } } # 此處省略下面的代碼
在配置ENGINE屬性時,經常使用的可選值包括:github
'django.db.backends.sqlite3'
:SQLite嵌入式數據庫。'django.db.backends.postgresql'
:BSD許可證下發行的開源關係型數據庫產品。'django.db.backends.mysql'
:轉手屢次目前屬於甲骨文公司的經濟高效的數據庫產品。'django.db.backends.oracle'
:甲骨文公司的關係型數據庫旗艦產品。其餘的配置能夠參考官方文檔中數據庫配置的部分。正則表達式
NAME屬性表明數據庫的名稱,若是使用SQLite它對應着一個文件,在這種狀況下NAME的屬性值應該是一個絕對路徑;使用其餘關係型數據庫,則要配置對應的HOST(主機)、PORT(端口)、USER(用戶名)、PASSWORD(口令)等屬性。sql
安裝MySQL客戶端工具,Python 3中使用PyMySQL,Python 2中用MySQLdb。shell
(venv)$ pip install pymysql
若是使用Python 3須要修改項目的__init__.py
文件並加入以下所示的代碼,這段代碼的做用是將PyMySQL視爲MySQLdb來使用,從而避免Django找不到鏈接MySQL的客戶端工具而詢問你:「Did you install mysqlclient? 」(你安裝了mysqlclient嗎?)。數據庫
import pymysql pymysql.install_as_MySQLdb()
運行manage.py並指定migrate參數實現數據庫遷移,爲應用程序建立對應的數據表,固然在此以前須要先啓動MySQL數據庫服務器並建立名爲oa的數據庫,在MySQL中建立數據庫的語句以下所示。django
drop database if exists oa; create database oa default charset utf8;
(venv)$ cd .. (venv)$ python manage.py migrate Operations to perform: Apply all migrations: admin, auth, contenttypes, sessions Running migrations: Applying contenttypes.0001_initial... OK Applying auth.0001_initial... OK Applying admin.0001_initial... OK Applying admin.0002_logentry_remove_auto_add... OK Applying contenttypes.0002_remove_content_type_name... OK Applying auth.0002_alter_permission_name_max_length... OK Applying auth.0003_alter_user_email_max_length... OK Applying auth.0004_alter_user_username_opts... OK Applying auth.0005_alter_user_last_login_null... OK Applying auth.0006_require_contenttypes_0002... OK Applying auth.0007_alter_validators_add_error_messages... OK Applying auth.0008_alter_user_username_max_length... OK Applying auth.0009_alter_user_last_name_max_length... OK Applying sessions.0001_initial... OK
能夠看到,Django幫助咱們建立了10張表,這些都是使用Django框架須要的東西,稍後咱們就會用到這些表。除此以外,咱們還應該爲咱們本身的應用建立數據模型。若是要在hrs應用中實現對部門和員工的管理,咱們能夠建立以下所示的數據模型。vim
(venv)$ vim hrs/models.py
from django.db import models class Dept(models.Model): """部門類""" no = models.IntegerField(primary_key=True, db_column='dno', verbose_name='部門編號') name = models.CharField(max_length=20, db_column='dname', verbose_name='部門名稱') location = models.CharField(max_length=10, db_column='dloc', verbose_name='部門所在地') class Meta: db_table = 'tb_dept' class Emp(models.Model): """員工類""" no = models.IntegerField(primary_key=True, db_column='eno', verbose_name='員工編號') name = models.CharField(max_length=20, db_column='ename', verbose_name='員工姓名') job = models.CharField(max_length=10, verbose_name='職位') # 自參照完整性多對一外鍵關聯 mgr = models.ForeignKey('self', on_delete=models.SET_NULL, null=True, blank=True, verbose_name='主管編號') sal = models.DecimalField(max_digits=7, decimal_places=2, verbose_name='月薪') comm = models.DecimalField(max_digits=7, decimal_places=2, null=True, blank=True, verbose_name='補貼') # 多對一外鍵關聯 dept = models.ForeignKey(Dept, db_column='dno', on_delete=models.PROTECT, verbose_name='所在部門') class Meta: db_table = 'tb_emp'
說明:上面定義模型時使用了字段類及其屬性,其中IntegerField對應數據庫中的integer類型,CharField對應數據庫的varchar類型,DecimalField對應數據庫的decimal類型,ForeignKey用來創建多對一外鍵關聯。字段屬性primary_key用於設置主鍵,max_length用來設置字段的最大長度,db_column用來設置數據庫中與字段對應的列,verbose_name則設置了Django後臺管理系統中該字段顯示的名稱。若是對這些東西感到很困惑也沒關係,文末提供了字段類、字段屬性、元數據選項等設置的相關說明,不清楚的讀者能夠稍後查看對應的參考指南。
經過模型建立數據表。
(venv)$ python manage.py makemigrations hrs
Migrations for 'hrs': hrs/migrations/0001_initial.py - Create model Dept - Create model Emp (venv)$ python manage.py migrate Operations to perform: Apply all migrations: admin, auth, contenttypes, hrs, sessions Running migrations: Applying hrs.0001_initial... OK
執行完數據模型遷移操做以後,能夠在經過圖形化的MySQL客戶端工具查看到E-R圖(實體關係圖)。
建立超級管理員帳號。
(venv)$ python manage.py createsuperuser
Username (leave blank to use 'hao'): jackfrued Email address: jackfrued@126.com Password: Password (again): Superuser created successfully.
啓動Web服務器,登陸後臺管理系統。
(venv)$ python manage.py runserver
訪問http://127.0.0.1:8000/admin,會來到以下圖所示的登陸界面。
登陸後進入管理員操做平臺。
至此咱們尚未看到以前建立的模型類,須要在應用的admin.py文件中模型進行註冊。
註冊模型類。
(venv)$ vim hrs/admin.py
from django.contrib import admin from hrs.models import Emp, Dept admin.site.register(Dept) admin.site.register(Emp)
註冊模型類後,就能夠在後臺管理系統中看到它們。
對模型進行CRUD操做。
能夠在管理員平臺對模型進行C(新增)R(查看)U(更新)D(刪除)操做,以下圖所示。
添加新的部門。
查看全部部門。
更新和刪除部門。
註冊模型管理類。
再次修改admin.py文件,經過註冊模型管理類,能夠在後臺管理系統中更好的管理模型。
from django.contrib import admin from hrs.models import Emp, Dept class DeptAdmin(admin.ModelAdmin): list_display = ('no', 'name', 'location') ordering = ('no', ) class EmpAdmin(admin.ModelAdmin): list_display = ('no', 'name', 'job', 'mgr', 'sal', 'comm', 'dept') search_fields = ('name', 'job') admin.site.register(Dept, DeptAdmin) admin.site.register(Emp, EmpAdmin)
爲了更好的查看模型數據,能夠爲Dept和Emp兩個模型類添加__str__
魔法方法。
from django.db import models class Dept(models.Model): """部門類""" # 此處省略上面的代碼 def __str__(self): return self.name # 此處省略下面的代碼 class Emp(models.Model): """員工類""" # 此處省略上面的代碼 mgr = models.ForeignKey('self', on_delete=models.SET_NULL, null=True, blank=True, verbose_name='直接主管') # 此處省略下面的代碼 # 此處省略上面的代碼 def __str__(self): return self.name # 此處省略下面的代碼
修改代碼後刷新查看Emp模型的頁面,效果以下圖所示。
在瞭解了Django提供的模型管理平臺以後,咱們來看看如何從代碼層面完成對模型的CRUD(Create / Read / Update / Delete)操做。咱們能夠經過manage.py開啓Shell交互式環境,而後使用Django內置的ORM框架對模型進行CRUD操做。
(venv)$ python manage.py shell
Python 3.6.4 (v3.6.4:d48ecebad5, Dec 18 2017, 21:07:28)
[GCC 4.2.1 (Apple Inc. build 5666) (dot 3)] on darwin
Type "help", "copyright", "credits" or "license" for more information. (InteractiveConsole) >>>
>>> from hrs.models import Dept, Emp
>>> dept = Dept(40, '研發2部', '深圳') >>> dept.save()
>>> dept.name = '研發3部' >>> dept.save()
查詢全部對象。
>>> Dept.objects.all() <QuerySet [<Dept: 研發1部>, <Dept: 銷售1部>, <Dept: 運維1部>, <Dept: 研發3部>]>
過濾數據。
>>> Dept.objects.filter(name='研發3部') # 查詢部門名稱爲「研發3部」的部門 <QuerySet [<Dept: 研發3部>]> >>> >>> Dept.objects.filter(name__contains='研發') # 查詢部門名稱包含「研發」的部門(模糊查詢) <QuerySet [<Dept: 研發1部>, <Dept: 研發3部>]> >>> >>> Dept.objects.filter(no__gt=10).filter(no__lt=40) # 查詢部門編號大於10小於40的部門 <QuerySet [<Dept: 銷售1部>, <Dept: 運維1部>]> >>> >>> Dept.objects.filter(no__range=(10, 30)) # 查詢部門編號在10到30之間的部門 <QuerySet [<Dept: 研發1部>, <Dept: 銷售1部>, <Dept: 運維1部>]>
查詢單個對象。
>>> Dept.objects.get(pk=10)
<Dept: 研發1部> >>> >>> Dept.objects.get(no=20) <Dept: 銷售1部> >>> >>> Dept.objects.get(no__exact=30) <Dept: 運維1部> >>> >>> Dept.objects.filter(no=10).first() <Dept: 研發1部>
排序數據。
>>> Dept.objects.order_by('no') # 查詢全部部門按部門編號升序排列 <QuerySet [<Dept: 研發1部>, <Dept: 銷售1部>, <Dept: 運維1部>, <Dept: 研發3部>]> >>> >>> Dept.objects.order_by('-no') # 查詢全部部門按部門編號降序排列 <QuerySet [<Dept: 研發3部>, <Dept: 運維1部>, <Dept: 銷售1部>, <Dept: 研發1部>]>
切片數據。
>>> Dept.objects.order_by('no')[0:2] # 按部門編號排序查詢1~2部門 <QuerySet [<Dept: 研發1部>, <Dept: 銷售1部>]> >>> >>> Dept.objects.order_by('no')[2:4] # 按部門編號排序查詢3~4部門 <QuerySet [<Dept: 運維1部>, <Dept: 研發3部>]>
高級查詢。
>>> Emp.objects.filter(dept__no=10) # 根據部門編號查詢該部門的員工 <QuerySet [<Emp: 喬峯>, <Emp: 張無忌>, <Emp: 張三丰>]> >>> >>> Emp.objects.filter(dept__name__contains='銷售') # 查詢名字包含「銷售」的部門的員工 <QuerySet [<Emp: 黃蓉>]> >>> >>> Dept.objects.get(pk=10).emp_set.all() # 經過部門反查部門全部的員工 <QuerySet [<Emp: 喬峯>, <Emp: 張無忌>, <Emp: 張三丰>]>
說明1:因爲員工與部門之間存在多對一外鍵關聯,因此也能經過部門反向查詢該部門的員工(從一對多關係中「一」的一方查詢「多」的一方),反向查詢屬性默認的名字是
類名小寫_set
(如上面例子中的emp_set
),固然也能夠在建立模型時經過ForeingKey
的related_name
屬性指定反向查詢屬性的名字。若是不但願執行反向查詢能夠將related_name
屬性設置爲'+'
或以'+'
開頭的字符串。
說明2:查詢多個對象的時候返回的是QuerySet對象,QuerySet使用了惰性查詢,即在建立QuerySet對象的過程當中不涉及任何數據庫活動,等真正用到對象時(求值QuerySet)才向數據庫發送SQL語句並獲取對應的結果,這一點在實際開發中須要引發注意!
說明3:能夠在QuerySet上使用
update()
方法一次更新多個對象。
>>> Dept.objects.get(pk=40).delete()
(1, {'hrs.Dept': 1})
related_name
屬性。OneToOneField
代替ForeignKeyField(unique=True)
。NullBooleanField
。<ModelName>.DoesNotExists
取代ObjectDoesNotExists
。QuerySet
調用len()
函數。QuerySet
的exists()
方法的返回值用於if
條件。DecimalField
來存儲貨幣相關數據而不是FloatField
。__str__
方法。說明:以上內容來自於STEELKIWI網站的Best Practice working with Django models in Python,有興趣的小夥伴能夠閱讀原文。
對字段名稱的限制
Django模型字段類
字段類 | 說明 |
---|---|
AutoField | 自增ID字段 |
BigIntegerField | 64位有符號整數 |
BinaryField | 存儲二進制數據的字段,對應Python的bytes類型 |
BooleanField | 存儲True或False |
CharField | 長度較小的字符串 |
DateField | 存儲日期,有auto_now和auto_now_add屬性 |
DateTimeField | 存儲日期和日期,兩個附加屬性同上 |
DecimalField | 存儲固定精度小數,有max_digits(有效位數)和decimal_places(小數點後面)兩個必要的參數 |
DurationField | 存儲時間跨度 |
EmailField | 與CharField相同,能夠用EmailValidator驗證 |
FileField | 文件上傳字段 |
FloatField | 存儲浮點數 |
ImageField | 其餘同FileFiled,要驗證上傳的是否是有效圖像 |
IntegerField | 存儲32位有符號整數。 |
GenericIPAddressField | 存儲IPv4或IPv6地址 |
NullBooleanField | 存儲True、False或null值 |
PositiveIntegerField | 存儲無符號整數(只能存儲正數) |
SlugField | 存儲slug(簡短標註) |
SmallIntegerField | 存儲16位有符號整數 |
TextField | 存儲數據量較大的文本 |
TimeField | 存儲時間 |
URLField | 存儲URL的CharField |
UUIDField | 存儲全局惟一標識符 |
通用字段屬性
選項 | 說明 |
---|---|
null | 數據庫中對應的字段是否容許爲NULL,默認爲False |
blank | 後臺模型管理驗證數據時,是否容許爲NULL,默認爲False |
choices | 設定字段的選項,各元組中的第一個值是設置在模型上的值,第二值是人類可讀的值 |
db_column | 字段對應到數據庫表中的列名,未指定時直接使用字段的名稱 |
db_index | 設置爲True時將在該字段建立索引 |
db_tablespace | 爲有索引的字段設置使用的表空間,默認爲DEFAULT_INDEX_TABLESPACE |
default | 字段的默認值 |
editable | 字段在後臺模型管理或ModelForm中是否顯示,默認爲True |
error_messages | 設定字段拋出異常時的默認消息的字典,其中的鍵包括null、blank、invalid、invalid_choice、unique和unique_for_date |
help_text | 表單小組件旁邊顯示的額外的幫助文本。 |
primary_key | 將字段指定爲模型的主鍵,未指定時會自動添加AutoField用於主鍵,只讀。 |
unique | 設置爲True時,表中字段的值必須是惟一的 |
verbose_name | 字段在後臺模型管理顯示的名稱,未指定時使用字段的名稱 |
ForeignKey屬性
'+'
,或者以'+'
結尾。ManyToManyField屬性
選項 | 說明 |
---|---|
abstract | 設置爲True時模型是抽象父類 |
app_label | 若是定義模型的應用不在INSTALLED_APPS中能夠用該屬性指定 |
db_table | 模型使用的數據表名稱 |
db_tablespace | 模型使用的數據表空間 |
default_related_name | 關聯對象回指這個模型時默認使用的名稱,默認爲<model_name>_set |
get_latest_by | 模型中可排序字段的名稱。 |
managed | 設置爲True時,Django在遷移中建立數據表並在執行flush管理命令時把表移除 |
order_with_respect_to | 標記對象爲可排序的 |
ordering | 對象的默認排序 |
permissions | 建立對象時寫入權限表的額外權限 |
default_permissions | 默認爲('add', 'change', 'delete') |
unique_together | 設定組合在一塊兒時必須獨一無二的字段名 |
index_together | 設定一塊兒創建索引的多個字段名 |
verbose_name | 爲對象設定人類可讀的名稱 |
verbose_name_plural | 設定對象的複數名稱 |
按字段查找能夠用的條件:
like
的模糊查詢between…and…
)Q對象(用於執行復雜查詢)的使用:
>>> from django.db.models import Q
>>> Emp.objects.filter(
... Q(name__startswith='張'), ... Q(sal__gte=5000) | Q(comm__gte=1000) ... ) # 查詢名字以「張」開頭且工資大於等於5000或補貼大於等於1000的員工 <QuerySet [<Emp: 張三丰>]>