服務端編程(七)- Django - MVC MVT model ORM操作

  • 本文將介紹Django的MVT模型 中的model 也就是ORM 通過python操縱數據庫
  • model 定義了數據的結構 也就是 列屬性 列約束 列數據類型等等
  • 本文將幫你學習以下內容…
    • 0 簡單認識MVT
    • 1 認識model
    • 2 如何對數據庫進行 增刪改查
    • 3 metadata method的概念

MVT模式

其實就是MVC模式的簡單改造?
MVT

  • M model 數據庫結構模型 說白了就是懶得用sql 而是用面嚮對象語言(python&Django js&NodeJS)操作數據庫
  • V view 直接翻譯是視圖 但其實意思是 渲染視圖的執行者
    渲染視圖 就是給html模板填空 模板固定 但是上面的數據隨着用戶請求的不同而不同
    類似 百度搜索 網址導航不同 模板一樣(就是一個個網址導航排成列)數據不同(只要你搜索關鍵詞不同)
    所以 View 是視圖的靈魂
  • template 這就是html模板 動態網站的一大特徵——以不變應萬變 —— 以不變的模板 動態的數據 應萬變的用戶需求

MVC模式

  • M 還是 model 同上
  • V 還是view 可以看做MVT的 VT合併起來的東西 因爲不僅僅負責數據 還有靜態的模板
    MVC的V真的是全權負責 顯示 能看的見的 這一部分
  • C controller 控制器 負責頁面邏輯 這裏就類似MVT中V的部分功能——頁面邏輯 比如鼠標位置影響某些動態內容的加載等等

其實就是MVT中VT職能的劃分的不同 我們沒必要糾結 尤其是小規模的網站

好 背景補充完了

下面 我們就來學習model 也即是ORM —— 如何用python django 操作數據庫

field 列屬性

這裏我們聊一下 如何通過python 制定我們表的結構(就是加條件:)
學過MySQL的朋友知道 表的結構其實就是通過DDL 數據定義語言 我在我MySQL專欄強調過多次
也就是 我們只需要限定

  • 列屬性的名稱(真名或者AS的別名)
  • 列屬性的數據類型 比如整形 浮點
  • 列屬性的約束 比如
    • 默認是NULL
    • 列屬性的索引 就是添加Unique唯一鍵 或者主鍵 外鍵等等

我們看看語法

my_field_name = models.CharField(max_length=20, help_text='Enter field documentation')

也就是 我們想往列屬性上加條件 往CharField( )裏面塞就行了
那麼有這些“條件”名稱:

  • max_length 這個列屬性 最多可容納的字符數 注意 是字符
  • help_text 解釋一下這個列屬性幹啥用的
  • verbose_name: 別名
  • default: 默認值
  • null: 決定是否默認值爲NULL (null = true 是)
  • blank: 當爲true 可以值爲空 意思就是你不往這裏輸入一個值 他就會報錯:)
  • choices: 這個類似ENUM屬性 或者是CHECK約束 比如性別 只有男 女 兩個選項
  • primary_key: 主鍵 也就是 唯一 而且不爲NULL的鍵(可能含有多列 這經常被考到 套路我們)而且django設計的 如果沒有主鍵 他會給我們自動安上一個2333
    主鍵 索引概念有點忘記的朋友可以參考:數據庫學習之MySQL (二十六)—— DDL(四)約束 - 主鍵 外鍵詳解 添加約束 刪除約束 修改約束

我們看看這堆玩意兒 說白了還不是用原生sql概念可以解決的東西 (又包裝一遍真快樂)
那麼我們ORM 也就是用py控制sql的方法也就是這幾個方面

  • 名稱
    • verbose_name 別名 主要是方便我們 “顧名思義”
      比如 ISDN 和 the_ISDN_of_books 真名 和 別名
    • help_text
  • 約束(constrain)
    • null 當null屬性爲true
    • default
    • blank
    • primary_key
    • choices
  • 數據類型
    • max_length

這樣歸類對於學過sql語句的朋友可能更加清晰

還有別的屬性 我建議沒必要記太多 畢竟原生sql纔是關鍵2333
需要的時候請查詢 django的說明

不知道你有沒有觀察到他用的函數“CharField” 那我用"IntField" 行不?
對了!django把數據類型有關的條件放到函數裏面去了:

django函數 解釋 對應的sql數據類型
CharField 放字符串 注意要制定最大字長 char (注意不是varchar 這玩意固定的)
TextField 長字符串(比如博文) text
IntegerField 存整形 integer
DateField、DateTimeField 時間日期數據 date time
EmailField 存email地址的 -
FileField、ImageField 存文件 存圖片 blob
AutoField 自增的那個約束 auto_increment 注意不是increasement

還有兩個 羣體對羣體的條件 我們後面會講

  • ForeignKey
  • ManyToManyField

metadata

django官方文檔
meta 元 metadata元數據 就是一些 描述數據的數據(發明這個概念的人真矯情)
說白了 我們想通過ORM查出數據 找一條條記錄
但是 查出的記錄是以什麼樣的順序呢? 從大到小?從A-Z?
我們就用一個數據 描述它 這就是元數據的作用之一 非常類似ORDER BY

class meta:
    ordering = ['title', '-pubdate']

這個非常類似下面這段sql代碼:

ORDER BY title ASC, pubdate DESC;

很明顯 代碼通過title作爲第一排序 然後當title相同的時候 就用pubdate排序

注意 對於char 排序採用A-Z 對於數字 排序按大小 對於日期 排序按先後
然後django寫法中 用- 表示 DESC 也就是默認是升序的(ASC) 然後加- 成了 降序(DESC)

另外 元數據的另一個作用是 給這個類起別名

verbose_name = 'BetterName'

還可以弄一個抽象類 也就是 我們如果多個表格都是同樣的套路(限制條件) 就可以統一繼承於一個抽象類的下面 這樣就更加高效簡單 需要修改 重載一下就好
這個我們後面會講到

注意 meta 既然是描述數據的數據 自然是隸屬於某一個類(也就是對應一個表)

method

每個model都會有一些方法method 有些方法比較常用 比如:

  • __str__
    這樣就能給對象返回一個我們能夠讀的懂的字符串 作爲說明之用
  • get_absolute_urls
    這樣就能提供給你一個能夠顯示獨立model的記錄的url 我們作爲admin 管理起來更加方便

所以大概是這麼寫:

def __str__(self):
    return self.field_name
def get_absolute_url(self):
    """Returns the url to access a particular instance of the model."""
    return reverse('model-detail-view', args=[str(self.id)])

這個也不用擔心 後面會詳細講的

ORM操作 model管理

說白了 這裏就是將一些基本的 通過ORM 操作數據庫的方式 也就是DQL DDL DML TCL的封裝而已
也就是 增 刪 改 查CRUD

# Create a new record using the model's constructor.
record = MyModelName(my_field_name="Instance #1")

# Save the object into the database.
record.save()

# Change record by modifying the fields, then calling save().
record.my_field_name = "New Instance Name"

# Save the object into the database.
record.save()

第一句 增 生成model並且填值
第二句 生效到數據庫 類似 commit
第三句 更改數據(就是沒有生效的事務)
第四句 commit

注意你的思維方式 列就是一個對象的屬性 我們的記錄只是個對象實例 表就是這種對象關係

然後 我們用fliter 類似WHERE的篩選條件 作爲DQL 查詢記錄:

# 所有結果 沒有應用fliter()篩選的結果
all_books = Book.objects.all()

# title裏面含有‘wild’的記錄 也就是 類似sql 的LIKE關鍵字
wild_books = Book.objects.filter(title__contains='wild')

# 類似sql的count() 函數
number_wild_books = wild_books.count()

還有更多可以使用的API 不過這裏限於篇幅不講太多 後面會專門歸類整理的 放心
或者 傳送官方文檔:

空談誤國 實幹興邦

我們先複習一下:

面向對象概念 sql概念 實際位置
class 類 relation 關係 table 表
attribute 屬性 field 欄位 字段 column 列
instance 實例 record 記錄 row 行

然後 應用所學知識於我們的圖書館項目:

我們可以這麼構思:
我們需要以下的表:

  • 書名(一種書的表)
  • 具體的書(copies的管理)
  • 書籍分類表(genre)
  • 作者的表

然後 在models.py /locallibrary/catalog/model.py 裏面 我們創建

from django.db import models
from django.urls import reverse # Used to generate URLs by reversing the URL patterns
import uuid # Required for unique book instances

# 書的作者 模型
class Author(models.Model):
    first_name = models.CharField(max_length=100)
    last_name = models.CharField(max_length=100)
    date_of_birth = models.DateField(null=True, blank=True)
    date_of_death = models.DateField('Died', null=True, blank=True)

    class Meta:
        ordering = ['last_name', 'first_name']

    def get_absolute_url(self):
        return reverse('author-detail', args=[str(self.id)])

    def __str__(self):
        return f'{self.last_name}, {self.first_name}'
        
# 書籍分類模型
class Genre(models.Model):
    name = models.CharField(max_length=200, help_text='Enter a book genre (e.g. Science Fiction)')
    
    def __str__(self):
        return self.name

# 書種類的模型 注意是一種書 不是一本書(copy)
class Book(models.Model):
    title = models.CharField(max_length=200)

    author = models.ForeignKey(Author, on_delete=models.SET_NULL, null=True)
    
    summary = models.TextField(max_length=1000, help_text='Enter a brief description of the book')
    isbn = models.CharField('ISBN', max_length=13, help_text='13 Character <a href="https://www.isbn-international.org/content/what-isbn">ISBN number</a>')
    
    genre = models.ManyToManyField(Genre, help_text='Select a genre for this book')
    
    def __str__(self):
        """String for representing the Model object."""
        return self.title
    
    def get_absolute_url(self):
        """Returns the url to access a detail record for this book."""
        return reverse('book-detail', args=[str(self.id)])


# 書實體的模型 也就是具體的一本書
class BookInstance(models.Model):
    id = models.UUIDField(primary_key=True, default=uuid.uuid4, help_text='Unique ID for this particular book across whole library')
    book = models.ForeignKey(Book, on_delete=models.SET_NULL, null=True) 
    imprint = models.CharField(max_length=200)
    due_back = models.DateField(null=True, blank=True)

    LOAN_STATUS = (
        ('m', 'Maintenance'),
        ('o', 'On loan'),
        ('a', 'Available'),
        ('r', 'Reserved'),
    )
    status = models.CharField(
        max_length=1,
        choices=LOAN_STATUS,
        blank=True,
        default='m',
        help_text='Book availability',
    )
    class Meta:
        ordering = ['due_back']
    def __str__(self):
        return f'{self.id} ({self.book.title})'



思考一下爲啥這幾個類的放置順序是這樣的 後面我們會說明的:)

下面我們詳細介紹一下 算是對前面內容的複習補充:

  • 書籍分類
class Genre(models.Model):
    name = models.CharField(max_length=200, help_text='Enter a book genre (e.g. Science Fiction)')
    
    def __str__(self):
        """String for representing the Model object."""
        return "《"+self.name+"》"

這個就是一個列是名字(name) 最大長度爲200字符 然後還有個__str__用於返回說明文字 這裏偷懶 就添加一個書名號了


  • 書的種類 也就是不管印刷了多少本 都算一種
class Book(models.Model):
   title = models.CharField(max_length=200)

   author = models.ForeignKey(Author, on_delete=models.SET_NULL, null=True)
   
   summary = models.TextField(max_length=1000, help_text='Enter a brief description of the book')
   isbn = models.CharField('ISBN', max_length=13, help_text='13 Character <a href="https://www.isbn-international.org/content/what-isbn">ISBN number</a>')
   
   genre = models.ManyToManyField(Genre, help_text='Select a genre for this book')
   
   def __str__(self):
       """String for representing the Model object."""
       return self.title
   
   def get_absolute_url(self):
       """Returns the url to access a detail record for this book."""
       return reverse('book-detail', args=[str(self.id)])
  • title那個就是定長字符串
  • 外鍵foreign key的應用 類似於數學的映射 多對一 一個第一作者 有多個作品 然而 一本書只有一個 第一作者
    於是外鍵就是 在作品這方(“多”)可以引用 作者方(“一”)
    因爲作者就相當於標籤 比如 我們對一羣學生作區分 研究生 本科生 專科生 是三個標籤 但是學生有千千萬萬 於是我們學生的表格就應該引用 只有三個標籤(應該是標籤表中的記錄)
    同樣 我們給書打標籤 也是如此
  • ManyToManyField 多對多 這是外鍵的拓展 一本書可以已有多個領域分類(genre) 而一個領域分類肯定包含多本書
    那麼 我們也不用操心具體多對多的流程 —— 寫上genre = models.ManyToManyField(Genre, help_text='Select a genre for this book') 這句完事

  • 具體的書
    • 如果把 一種書作爲 那麼具體的書就是書的實例
    • 當然我們還是定義了類描述“書的實例”
import uuid # Required for unique book instances

class BookInstance(models.Model):
    id = models.UUIDField(primary_key=True, default=uuid.uuid4, help_text='Unique ID for this particular book across whole library')
    book = models.ForeignKey(Book, on_delete=models.SET_NULL, null=True) 
    imprint = models.CharField(max_length=200)
    due_back = models.DateField(null=True, blank=True)

    LOAN_STATUS = (
        ('m', 'Maintenance'),
        ('o', 'On loan'),
        ('a', 'Available'),
        ('r', 'Reserved'),
    )

    status = models.CharField(
        max_length=1,
        choices=LOAN_STATUS,
        blank=True,
        default='m',
        help_text='Book availability',
    )

    class Meta:
        ordering = ['due_back']

    def __str__(self):
        """String for representing the Model object."""
        return f'{self.id} ({self.book.title})'
  • id的定義用了 UUIDField 也就是我們定義主鍵的時候可以這麼用
    • 當然有時候我們想定義爲非主鍵的列 那樣的話就要修改一下 (否則可能報錯):
      • 可以爲空的列
        範例:bookuuid = models.UUIDField(default=uuid.uuid4, null=False,verbose_name=u’book_verbose _id’,help_text=“book verbose id”)
      • 不可爲空的列
        範例:bookuuid = models.UUIDField(default=None, null=True, blank=True,verbose_name=u’book_verbose_id’,help_text=“book_verbose_id”)
  • book imprint due_back 正常套路
  • LOAN_STATUS 這個就類似ENUM 指示status只能有4種情況
  • 然後 class meta 指示了 查詢輸出記錄的 order by 順序
  • __str__ 正常套路

  • 作者表
class Author(models.Model):
    first_name = models.CharField(max_length=100)
    last_name = models.CharField(max_length=100)
    date_of_birth = models.DateField(null=True, blank=True)
    date_of_death = models.DateField('Died', null=True, blank=True)

    class Meta:
        ordering = ['last_name', 'first_name']

    def get_absolute_url(self):
        return reverse('author-detail', args=[str(self.id)])

    def __str__(self):
        return f'{self.last_name}, {self.first_name}'
  • return reverse('author-detail', args=[str(self.id)]) 注意這句 reverse的意思就是 “反過來”
    還記得我們的url映射嘛 我們是把url映射到 資源實際地址或者能夠返回資源的對象(url.py 上寫的“處理url請求的套路”)上去
    這裏我們反過來 通過套路author-detail 得到請求的url

然後呢?

就是進行數據庫遷移啦:

python3 manage.py makemigrations
python3 manage.py migrate

總結 ´◡`

本節我們玩了一下django的數據庫操作工具 也就是ORM
但是應該如何修改這些數據庫呢?
下一節我們來通過“後臺” admin頁面進行修改
然後到後期才你能做出來 提供多用戶 的前端客戶端的修改

下一節 服務端編程(八)———後臺管理站點 後臺操作數據庫 admin頁面

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章