A place for study and research

Python 100 Days Day42 Django Advance

|

author: jackfrued

深入模型

在上一個章節中,我們提到了Django是基於MVC架構的Web框架,MVC架構追求的是“模型”和“視圖”的解耦合。所謂“模型”說得更直白一些就是數據(的表示),所以通常也被稱作“數據模型”。在實際的項目中,數據模型通常通過數據庫實現持久化操作,而關系型數據庫在過去和當下都是持久化的首選方案,下面我們通過完成一個投票項目來講解和模型相關的知識點。投票項目的首頁會展示某在線教育平台所有的學科;點擊學科可以查看到該學科的老師及其信息;用戶登錄後在查看老師的頁面為老師投票,可以投讚成票和反對票;未登錄的用戶可以通過登錄頁進行登錄;尚未注冊的用戶可以通過注冊頁輸入個人信息進行注冊。在這個項目中,我們使用MySQL數據庫來實現數據持久化操作。

創建項目和應用

我們首先創建Django項目vote並為其添加虛擬環境和依賴項。接下來,在項目下創建名為polls的應用和保存模板頁的文件夾tempaltes,項目文件夾的結構如下所示。

根據上面描述的項目需求,我們準備了四個靜態頁面,分別是展示學科的頁面subjects.html,顯示學科老師的頁面teachers.html,登錄頁面login.html,注冊頁面register.html,稍後我們會將靜態頁修改為Django項目所需的模板頁。

配置關系型數據庫MySQL

  1. 在MySQL中創建數據庫,創建用戶,授權用戶訪問該數據庫。

    create database vote default charset utf8;
    create user 'hellokitty'@'%' identified by 'Hellokitty.618';
    grant all privileges on vote.* to 'hellokitty'@'%';
    flush privileges;
    
  2. 在MySQL中創建保存學科和老師信息的二維表(保存用戶信息的表稍後處理)。

    use vote;
       
    -- 創建學科表
    create table `tb_subject`
    (
    	`no` integer auto_increment comment '學科編號',
        `name` varchar(50) not null comment '學科名稱',
        `intro` varchar(1000) not null default '' comment '學科介紹',
        `is_hot` boolean not null default 0 comment '是不是熱門學科',
        primary key (`no`)
    );
    -- 創建老師表
    create table `tb_teacher`
    (
        `no` integer auto_increment comment '老師編號',
        `name` varchar(20) not null comment '老師姓名',
        `sex` boolean not null default 1 comment '老師性別',
        `birth` date not null comment '出生日期',
        `intro` varchar(1000) not null default '' comment '老師介紹',
        `photo` varchar(255) not null default '' comment '老師照片',
        `gcount` integer not null default 0 comment '好評數',
        `bcount` integer not null default 0 comment '差評數',
        `sno` integer not null comment '所屬學科',
        primary key (`no`),
        foreign key (`sno`) references `tb_subject` (`no`)
    );
    
  3. 在虛擬環境中安裝連接MySQL數據庫所需的依賴項。

    pip install mysqlclient
    

    說明:如果因為某些原因無法安裝mysqlclient三方庫,可以使用它的替代品pymysqlpymysql是用純Python開發的連接MySQL的Python庫,安裝更容易成功,但是需要在Django項目文件夾的__init__.py中添加如下所示的代碼。

    import pymysql
    
    pymysql.install_as_MySQLdb()
    

    如果使用Django 2.2及以上版本,還會遇到PyMySQL跟Django框架的兼容性問題,兼容性問題會導致項目無法運行,需要按照GitHub上PyMySQL倉庫Issues中提供的方法進行處理。總體來說,使用pymysql會比較麻煩,強烈建議大家首選安裝mysqlclient

  4. 修改項目的settings.py文件,首先將我們創建的應用polls添加已安裝的項目(INSTALLED_APPS)中,然後配置MySQL作為持久化方案。

    INSTALLED_APPS = [
        'django.contrib.admin',
        'django.contrib.auth',
        'django.contrib.contenttypes',
        'django.contrib.sessions',
        'django.contrib.messages',
        'django.contrib.staticfiles',
        'polls',
    ]
       
    DATABASES = {
        'default': {
            # 數據庫引擎配置
            'ENGINE': 'django.db.backends.mysql',
            # 數據庫的名字
            'NAME': 'vote',
            # 數據庫服務器的IP地址(本機可以寫localhost或127.0.0.1)
            'HOST': 'localhost',
            # 啟動MySQL服務的端口號
            'PORT': 3306,
            # 數據庫用戶名和口令
            'USER': 'hellokitty',
            'PASSWORD': 'Hellokitty.618',
            # 數據庫使用的字符集
            'CHARSET': 'utf8',
            # 數據庫時間日期的時區設定
            'TIME_ZONE': 'Asia/Chongqing',
        }
    }
    

    在配置ENGINE屬性時,常用的可選值包括:

    • 'django.db.backends.sqlite3':SQLite嵌入式數據庫。
    • 'django.db.backends.postgresql':BSD許可證下發行的開源關系型數據庫產品。
    • 'django.db.backends.mysql':甲骨文公司經濟高效的數據庫產品。
    • 'django.db.backends.oracle':甲骨文公司關系型數據庫旗艦產品。

    其他的配置可以參考官方文檔中數據庫配置的部分。

  5. Django框架提供了ORM來解決數據持久化問題,ORM翻譯成中文叫“對象關系映射”。因為Python是面向對象的編程語言,我們在Python程序中使用對象模型來保存數據,而關系型數據庫使用關系模型,用二維表來保存數據,這兩種模型並不匹配。使用ORM是為了實現對象模型到關系模型的雙向轉換,這樣就不用在Python代碼中書寫SQL語句和遊標操作,因為這些都會由ORM自動完成。利用Django的ORM,我們可以直接將剛才創建的學科表和老師表變成Django中的模型類。

    python manage.py inspectdb > polls/models.py
    

    我們可以對自動生成的模型類稍作調整,代碼如下所示。

    from django.db import models
       
       
    class Subject(models.Model):
        no = models.AutoField(primary_key=True, verbose_name='編號')
        name = models.CharField(max_length=50, verbose_name='名稱')
        intro = models.CharField(max_length=1000, verbose_name='介紹')
        is_hot = models.BooleanField(verbose_name='是否熱門')
       
        class Meta:
            managed = False
            db_table = 'tb_subject'
       
       
    class Teacher(models.Model):
        no = models.AutoField(primary_key=True, verbose_name='編號')
        name = models.CharField(max_length=20, verbose_name='姓名')
        sex = models.BooleanField(default=True, verbose_name='性別')
        birth = models.DateField(verbose_name='出生日期')
        intro = models.CharField(max_length=1000, verbose_name='個人介紹')
        photo = models.ImageField(max_length=255, verbose_name='照片')
        good_count = models.IntegerField(default=0, db_column='gcount', verbose_name='好評數')
        bad_count = models.IntegerField(default=0, db_column='bcount', verbose_name='差評數')
        subject = models.ForeignKey(Subject, models.DO_NOTHING, db_column='sno')
       
        class Meta:
            managed = False
            db_table = 'tb_teacher'
    

    說明:模型類都直接或間接繼承自Model類,模型類跟關系型數據庫的二維表對應,模型對象跟表中的記錄對應,模型對象的屬性跟表中的字段對應。如果對上面模型類的屬性定義不是特別理解,可以看看本文後面提供的“模型定義參考”部分的內容。

使用ORM完成模型的CRUD操作

有了Django框架的ORM,我們可以直接使用面向對象的方式來實現對數據的CRUD(增刪改查)操作。我們可以在PyCharm的終端中輸入下面的命令進入到Django項目的交互式環境,然後嘗試對模型的操作。

python manage.py shell

新增

from polls.models import Subject

subject1 = Subject(name='Python全棧開發', intro='當下最熱門的學科', is_hot=True)
subject1.save()
subject2 = Subject(name='全棧軟件測試', intro='學習自動化測試的學科', is_hot=False)
subject2.save()
subject3 = Subject(name='JavaEE分布式開發', intro='基於Java語言的服務器應用開發', is_hot=True)

刪除

subject = Subject.objects.get(no=2)
subject.delete()

更新

subject = Subject.objects.get(no=1)
subject.name = 'Python全棧+人工智能'
subject.save()

查詢

  1. 查詢所有對象。
Subjects.objects.all()
  1. 過濾數據。
# 查詢名稱為“Python全棧+人工智能”的學科
Subject.objects.filter(name='Python全棧+人工智能')

# 查詢名稱包含“全棧”的學科(模糊查詢)
Subject.objects.filter(name__contains='全棧')
Subject.objects.filter(name__startswith='全棧')
Subject.objects.filter(name__endswith='全棧')

# 查詢所有熱門學科
Subject.objects.filter(is_hot=True)

# 查詢編號大於3小於10的學科
Subject.objects.filter(no__gt=3).filter(no__lt=10)
Subject.objects.filter(no__gt=3, no__lt=10)

# 查詢編號在3到7之間的學科
Subject.objects.filter(no__ge=3, no__le=7)
Subject.objects.filter(no__range=(3, 7))
  1. 查詢單個對象。
# 查詢主鍵為1的學科
Subject.objects.get(pk=1)
Subject.objects.get(no=1)
Subject.objects.filter(no=1).first()
Subject.objects.filter(no=1).last()
  1. 排序。
# 查詢所有學科按編號升序排列
Subject.objects.order_by('no')
# 查詢所有部門按部門編號降序排列
Subject.objects.order_by('-no')
  1. 切片(分頁查詢)。
# 按編號從小到大查詢前3個學科
Subject.objects.order_by('no')[:3]
  1. 計數。
# 查詢一共有多少個學科
Subject.objects.count()
  1. 高級查詢。
# 查詢編號為1的學科的老師
Teacher.objects.filter(subject__no=1)
Subject.objects.get(pk=1).teacher_set.all() 

# 查詢學科名稱有“全棧”二字的學科的老師
Teacher.objects.filter(subject__name__contains='全棧') 

說明1:由於老師與學科之間存在多對一外鍵關聯,所以能通過學科反向查詢到該學科的老師(從一對多關系中“一”的一方查詢“多”的一方),反向查詢屬性默認的名字是類名小寫_set(如上面例子中的teacher_set),當然也可以在創建模型時通過ForeingKeyrelated_name屬性指定反向查詢屬性的名字。如果不希望執行反向查詢可以將related_name屬性設置為'+'或者以'+'開頭的字符串。

說明2:ORM查詢多個對象時會返回QuerySet對象,QuerySet使用了惰性查詢,即在創建QuerySet對象的過程中不涉及任何數據庫活動,等真正用到對象時(對QuerySet求值)才向數據庫發送SQL語句並獲取對應的結果,這一點在實際開發中需要引起注意!

說明3:如果希望更新多條數據,不用先逐一獲取模型對象再修改對象屬性,可以直接使用QuerySet對象的update()方法一次性更新多條數據。

利用Django後台管理模型

在創建好模型類之後,可以通過Django框架自帶的後台管理應用(admin應用)實現對模型的管理。雖然實際應用中,這個後台可能並不能滿足我們的需求,但是在學習Django框架時,我們可以利用admin應用來管理我們的模型,同時也通過它來了解一個項目的後台管理系統需要哪些功能。使用Django自帶的admin應用步驟如下所示。

  1. admin應用所需的表遷移到數據庫中。admin應用本身也需要數據庫的支持,而且在admin應用中已經定義好了相關的數據模型類,我們只需要通過模型遷移操作就能自動在數據庫中生成所需的二維表。

    python manage.py migrate
    
  2. 創建訪問admin應用的超級用戶賬號,這里需要輸入用戶名、郵箱和口令。

    python manage.py createsuperuser
    

    說明:輸入口令時沒有回顯也不能退格,需要一氣呵成完成輸入。

  3. 運行項目,在瀏覽器中訪問http://127.0.0.1:8000/admin,輸入剛才創建的超級用戶賬號和密碼進行登錄。

    登錄後進入管理員操作平台。

    注意,我們暫時還沒能在admin應用中看到之前創建的模型類,為此需要在polls應用的admin.py文件中對需要管理的模型進行注冊。

  4. 注冊模型類。

    from django.contrib import admin
       
    from polls.models import Subject, Teacher
    
    admin.site.register(Subject)
    admin.site.register(Teacher)
    

    注冊模型類後,就可以在後台管理系統中看到它們。

  5. 對模型進行CRUD操作。

    可以在管理員平台對模型進行C(新增)、R(查看)、U(更新)、D(刪除)操作,如下圖所示。

    • 添加學科。

    • 查看所有學科。

    • 刪除和更新學科。

  6. 注冊模型管理類。

    可能大家已經注意到了,剛才在後台查看部門信息的時候,顯示的部門信息並不直觀,為此我們再修改admin.py文件,通過注冊模型管理類,可以在後台管理系統中更好的管理模型。

    from django.contrib import admin
    
    from polls.models import Subject, Teacher
       
       
    class SubjectModelAdmin(admin.ModelAdmin):
        list_display = ('no', 'name', 'intro', 'is_hot')
        search_fields = ('name', )
        ordering = ('no', )
       
       
    class TeacherModelAdmin(admin.ModelAdmin):
        list_display = ('no', 'name', 'sex', 'birth', 'good_count', 'bad_count', 'subject')
        search_fields = ('name', )
        ordering = ('no', )
       
       
    admin.site.register(Subject, SubjectModelAdmin)
    admin.site.register(Teacher, TeacherModelAdmin)
    

    為了更好的查看模型,我們為Subject類添加__str__魔法方法,並在該方法中返回學科名字。這樣在如上圖所示的查看老師的頁面上顯示老師所屬學科時,就不再是Subject object(1)這樣晦澀的信息,而是學科的名稱。

補充內容

Django模型最佳實踐

  1. 正確的為模型和關系字段命名。
  2. 設置適當的related_name屬性。
  3. OneToOneField代替ForeignKeyField(unique=True)
  4. 通過“遷移操作”(migrate)來添加模型。
  5. 用NoSQL來應對需要降低範式級別的場景。
  6. 如果布爾類型可以為空要使用NullBooleanField
  7. 在模型中放置業務邏輯。
  8. <ModelName>.DoesNotExists取代ObjectDoesNotExists
  9. 在數據庫中不要出現無效數據。
  10. 不要對QuerySet調用len()函數。
  11. QuerySetexists()方法的返回值用於if條件。
  12. DecimalField來存儲貨幣相關數據而不是FloatField
  13. 定義__str__方法。
  14. 不要將數據文件放在同一個目錄中。

說明:以上內容來自於STEELKIWI網站的Best Practice working with Django models in Python,有興趣的小夥伴可以閱讀原文。

模型定義參考

字段

對字段名稱的限制

  • 字段名不能是Python的保留字,否則會導致語法錯誤
  • 字段名不能有多個連續下劃線,否則影響ORM查詢操作

Django模型字段類

字段類 說明
AutoField 自增ID字段
BigIntegerField 64位有符號整數
BinaryField 存儲二進制數據的字段,對應Python的bytes類型
BooleanField 存儲TrueFalse
CharField 長度較小的字符串
DateField 存儲日期,有auto_nowauto_now_add屬性
DateTimeField 存儲日期和日期,兩個附加屬性同上
DecimalField 存儲固定精度小數,有max_digits(有效位數)和decimal_places(小數點後面)兩個必要的參數
DurationField 存儲時間跨度
EmailField CharField相同,可以用EmailValidator驗證
FileField 文件上傳字段
FloatField 存儲浮點數
ImageField 其他同FileFiled,要驗證上傳的是不是有效圖像
IntegerField 存儲32位有符號整數。
GenericIPAddressField 存儲IPv4或IPv6地址
NullBooleanField 存儲TrueFalsenull
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 設定字段拋出異常時的默認消息的字典,其中的鍵包括nullblankinvalidinvalid_choiceuniqueunique_for_date
help_text 表單小組件旁邊顯示的額外的幫助文本。
primary_key 將字段指定為模型的主鍵,未指定時會自動添加AutoField用於主鍵,只讀。
unique 設置為True時,表中字段的值必須是唯一的
verbose_name 字段在後台模型管理顯示的名稱,未指定時使用字段的名稱

ForeignKey屬性

  1. limit_choices_to:值是一個Q對象或返回一個Q對象,用於限制後台顯示哪些對象。
  2. related_name:用於獲取關聯對象的關聯管理器對象(反向查詢),如果不允許反向,該屬性應該被設置為'+',或者以'+'結尾。
  3. to_field:指定關聯的字段,默認關聯對象的主鍵字段。
  4. db_constraint:是否為外鍵創建約束,默認值為True
  5. on_delete:外鍵關聯的對象被刪除時對應的動作,可取的值包括django.db.models中定義的:
    • CASCADE:級聯刪除。
    • PROTECT:拋出ProtectedError異常,阻止刪除引用的對象。
    • SET_NULL:把外鍵設置為null,當null屬性被設置為True時才能這麽做。
    • SET_DEFAULT:把外鍵設置為默認值,提供了默認值才能這麽做。

ManyToManyField屬性

  1. symmetrical:是否建立對稱的多對多關系。
  2. through:指定維持多對多關系的中間表的Django模型。
  3. throughfields:定義了中間模型時可以指定建立多對多關系的字段。
  4. db_table:指定維持多對多關系的中間表的表名。
模型元數據選項
選項 說明
abstract 設置為True時模型是抽象父類
app_label 如果定義模型的應用不在INSTALLED_APPS中可以用該屬性指定
db_table 模型使用的數據表名稱
db_tablespace 模型使用的數據表空間
default_related_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 設定對象的覆數名稱

查詢參考

按字段查找可以用的條件
  1. exact / iexact:精確匹配/忽略大小寫的精確匹配查詢
  2. contains / icontains / startswith / istartswith / endswith / iendswith:基於like的模糊查詢
  3. in :集合運算
  4. gt / gte / lt / lte:大於/大於等於/小於/小於等於關系運算
  5. range:指定範圍查詢(SQL中的between…and…
  6. year / month / day / week_day / hour / minute / second:查詢時間日期
  7. isnull:查詢空值(True)或非空值(False)
  8. search:基於全文索引的全文檢索(一般很少使用)
  9. regex / iregex:基於正則表達式的模糊匹配查詢

Comments