Python Web 開發(fā)之 Django Models 詳解

Django 是由 Python 語言編寫的基于 MVC(即 Model View Controller)架構的 Web 開發(fā)框架。
其架構中的模型(Model)主要負責處理 Web 應用的數(shù)據(jù)邏輯部分,包括定義數(shù)據(jù)存儲單位(即數(shù)據(jù)庫表)的字段屬性和行為、與數(shù)據(jù)庫交互以及其他相關聯(lián)的操作。
通常一個模型映射于一個特定的數(shù)據(jù)庫表。

Django 中的模型有以下幾個基本屬性:

  • 每個模型都是繼承自 django.db.models.Model 類的子類
  • 模型類的屬性分別對應于與之相關聯(lián)的數(shù)據(jù)表中的字段
  • Django 會自動生成用于訪問數(shù)據(jù)庫的 API

一、基本使用

項目初始化

在開始編寫 Web 應用代碼之前,需要先使用如下命令初始化一個 Django 項目并創(chuàng)建應用:

$ django-admin startproject myproject
$ cd myproject
$ python manage.py startapp myapp

最終生成的項目目錄結(jié)構如下:

myproject
    ├─manage.py
    │
    ├─myapp
    │  ├─admin.py
    │  ├─apps.py
    │  ├─models.py
    │  ├─tests.py
    │  ├─views.py
    │  └─migrations
    │
    └─myproject
        ├─settings.py
        ├─urls.py
        └─wsgi.py
定義模型

用于定義模型的代碼通常保存在 myproject/myapp/models.py 文件中。
下面的代碼即定義了一個簡單的 Person 模型:

from django.db import models

class Person(models.Model):
    first_name = models.CharField(max_length=30)
    last_name = models.CharField(max_length=30)

其中的 first_namelast_name 兩個類屬性即對應于數(shù)據(jù)庫表的兩個字段。
Person 模型會以如下的 SQL 語句創(chuàng)建與之關聯(lián)的數(shù)據(jù)庫表(id 字段默認會自動添加):

CREATE TABLE myapp_person (
    "id" serial NOT NULL PRIMARY KEY,
    "first_name" varchar(30) NOT NULL,
    "last_name" varchar(30) NOT NULL
);

為了使模型生效,還需要將 myapp 包含進 settings.py 配置文件中的 INSTALLED_APPS,編輯 myproject/myproject/settings.py 文件,內(nèi)容如下:

INSTALLED_APPS = [
    #...
    'myapp',
    #...
]

之后即可使用 python manage.py makemigrations myapp 創(chuàng)建數(shù)據(jù)庫遷移文件;
再運行 python manage.py migrate 命令將模型中定義的表結(jié)構遷移至數(shù)據(jù)庫中。

Django Shell 測試

完成數(shù)據(jù)庫遷移后,可使用 python manage.py shell 命令進入 Django Shell 交互式命令行,通過 Django 提供的模型 API 進行測試(插入數(shù)據(jù)):

>>> from myapp.models import Person
>>> john = Person(first_name='John', last_name='Smith')
>>> john.save()
>>> Person.objects.all()
<QuerySet [<Person: Person object (1)>]>
>>> john.first_name
'John'

訪問 sqlite3 數(shù)據(jù)庫查詢最終結(jié)果,John Smith 已添加至數(shù)據(jù)表中:

>>> import sqlite3
>>> conn = sqlite3.connect('db.sqlite3')
>>> cursor = conn.cursor()
>>> cursor.execute('select * from myapp_person')
<sqlite3.Cursor object at 0x0000022C36ADBD50>
>>> print(cursor.fetchone())
(1, 'John', 'Smith')

二、字段(Field)

模型中最重要的也是唯一必須存在的項目就是字段,它由模型類的屬性定義,用來表述與模型相關聯(lián)的數(shù)據(jù)表的結(jié)構。

字段的類型與選項

模型中的每個字段都是 django.db.models.Field 類的實例,對應于數(shù)據(jù)庫表中的列。
Django 內(nèi)置了大量的字段類型,如 CharField,TextFieldDateTimeField 等。具體可查看 模型字段參考。

每個字段都可以接收特定的字段相關的參數(shù),比如 CharField 需要傳入 max_length 用于定義 VARCHAR 類型的字符長度。

此外還有一些通用的可選的字段選項。如:

  • null:如為 True,則 Django 會將空值在數(shù)據(jù)庫中存為 NULL。該選項默認為 False。
  • blank:如為 True,則該字段允許為空。與 null 選項不同,blank 是與表單驗證相關的,而 null 是數(shù)據(jù)庫相關的。
  • default:用于設置字段的默認值。
  • primary_key:用于設置模型的主鍵。如未指定任何字段為主鍵,則 Django 會自動添加 IntegerField 字段作為主鍵。
  • unique:設置字段的值是否允許重復。

PS:默認情況下,Django 會給每個模型添加如下字段
id = models.AutoField(primary_key=True)
作為為自增的主鍵。如果想覆蓋此默認行為,直接手動指定其他字段為主鍵(primary_key=True)即可。

關系

額,關系型數(shù)據(jù)庫的強大之處即在于各數(shù)據(jù)庫表之間的相互關聯(lián)。Django 支持定義三種最常見的數(shù)據(jù)庫關系:多對一、多對多和一對一。

可以通過 django.db.models.ForeignKey 創(chuàng)建多對一關系,只需要像定義其他字段那樣將它作為類屬性引入即可。如:

from django.db import models

class Manufacturer(models.Model):
    name = models.CharField(max_length=20)
    location = models.CharField(max_length=40)

class Car(models.Model):
    manufacturer = models.ForeignKey(Manufacturer, on_delete=models.CASCADE)
    price = models.IntegerField()

運行 python manage.py makemigrations 命令創(chuàng)建數(shù)據(jù)庫遷移文件:

$ python manage.py makemigrations myapp
Migrations for 'myapp':
  myapp/migrations/0001_initial.py
    - Create model Manufacturer
    - Create model Car

使用 python manage.py sqlmigrate myapp 0001 命令查看具體會執(zhí)行哪些 SQL 語句(基于 sqlite3):

$ python manage.py sqlmigrate myapp 0001
BEGIN;
--
-- Create model Manufacturer
--
CREATE TABLE "myapp_manufacturer" ("id" integer NOT NULL PRIMARY KEY AUTOINCREMENT, "name" varchar(20) NOT NULL, "location" varchar(40) NOT NULL);
--
-- Create model Car
--
CREATE TABLE "myapp_car" ("id" integer NOT NULL PRIMARY KEY AUTOINCREMENT, "price" integer NOT NULL, "manufacturer_id" integer NOT NULL REFERENCES "myapp_manufacturer" ("id") DEFERRABLE INITIALLY DEFERRED);
CREATE INDEX "myapp_car_manufacturer_id_2be676ab" ON "myapp_car" ("manufacturer_id");
COMMIT;

多對多和一對一的數(shù)據(jù)庫關系則分別可以使用 ManyToManyFieldOneToOneField 定義。

三、模型的屬性與方法

Meta 選項

模型的 Meta 選項在模型類的定義中是可選的,它基本上包含了除字段以外的所有內(nèi)容。比如數(shù)據(jù)紀錄的順序(ordering)、關聯(lián)的數(shù)據(jù)庫表的名稱(db_table)和索引(indexes)等。

Django 模型支持的所有 Meta 選項可以參考 Model Meta options

示例代碼:

from django.db import models

class Ox(models.Model):
    horn_length = models.IntegerField()

    class Meta:
        ordering = ["horn_length"]
        verbose_name_plural = "oxen"
自定義模型方法

在模型中創(chuàng)建自定義方法可以為模型對象添加個性化的“底層”功能。參考如下代碼:

from django.db import models

class Person(models.Model):
    first_name = models.CharField(max_length=50)
    last_name = models.CharField(max_length=50)
    birth_date = models.DateField()

    @property
    def full_name(self):
        "Returns the person's full name."
        return '%s %s' % (self.first_name, self.last_name)

此時的 Person 模型除了可以從數(shù)據(jù)庫中讀取和寫入數(shù)據(jù)等基本功能外,還可以通過它調(diào)用自定義的 full_name 方法完成額外的需求(返回全名)。

覆蓋默認的模型方法

有些情況下,還可以通過修改模型內(nèi)置的方法,改變模型與數(shù)據(jù)庫的具體交互方式。尤其是 save() (向數(shù)據(jù)庫中存入數(shù)據(jù))和 delete() (從數(shù)據(jù)庫中刪除紀錄)等方法。如:

from django.db import models

class Person(models.Model):
    first_name = models.CharField(max_length=50)
    last_name = models.CharField(max_length=50)

    def save(self, *args, **kwargs):
        self.first_name = self.first_name.capitalize()
        self.last_name = self.last_name.capitalize()
        super().save(*args, **kwargs)

    @property
    def full_name(self):
        "Returns the person's full name."
        return '%s %s' % (self.first_name, self.last_name)

重新遷移數(shù)據(jù)庫,進入 Django Shell 測試,結(jié)果如下:

>>> from myapp.models import Person
>>> john = Person(first_name='john', last_name='smith')
>>> john.save()
>>> john
<Person: Person object (2)>
>>> john.full_name
'John Smith'

四、數(shù)據(jù)庫操作

一旦創(chuàng)建了數(shù)據(jù)模型,Django 即會自動生成與數(shù)據(jù)庫交互的 API 供用戶創(chuàng)建、獲取、更新和刪除數(shù)據(jù)對象。

此處先創(chuàng)建如下的模型文件供測試使用:

from django.db import models

class Blog(models.Model):
    name = models.CharField(max_length=100)
    tagline = models.TextField()

    def __str__(self):
        return self.name

class Author(models.Model):
    name = models.CharField(max_length=200)
    email = models.EmailField()

    def __str__(self):
        return self.name

class Entry(models.Model):
    blog = models.ForeignKey(Blog, on_delete=models.CASCADE)
    headline = models.CharField(max_length=255)
    body_text = models.TextField()
    pub_date = models.DateField()
    mod_date = models.DateField()
    authors = models.ManyToManyField(Author)
    n_comments = models.IntegerField()
    n_pingbacks = models.IntegerField()
    rating = models.IntegerField()

    def __str__(self):
        return self.headline
Insert

可以通過實例化模型類創(chuàng)建一個數(shù)據(jù)對象,并調(diào)用其 save() 方法將對應的記錄插入(執(zhí)行 INSERT SQL 語句)到數(shù)據(jù)庫表中。

>>> from myapp.models import Blog
>>> b = Blog(name='Beatles Blog', tagline='All the latest Beatles news')
>>> b.save()
>>> b.name = 'Beatles Blog All'
>>> b.save()
>>> b
<Blog: Beatles Blog All>
>>> b.tagline
'All the latest Beatles news'

插入 ForeignKey 與 ManyToManyField
更新 ForeignKey 與操作普通字段的方式相同,將正確類型的對象賦值給對應字段并調(diào)用 save() 方法即可。如:

>>> from blog.models import Blog, Entry
>>> entry = Entry.objects.get(pk=1)
>>> cheese_blog = Blog.objects.get(name="Cheddar Talk")
>>> entry.blog = cheese_blog
>>> entry.save()

更新 ManyToManyField 的方式稍有不同,需要使用 add() 方法:

>>> from blog.models import Author
>>> joe = Author.objects.create(name="Joe")
>>> entry.authors.add(joe)
獲取數(shù)據(jù)

從數(shù)據(jù)庫中獲取數(shù)據(jù)會生成一個 QuerySet 對象,它代表從數(shù)據(jù)庫中取出的數(shù)據(jù)對象的集合。QuerySet 等同于數(shù)據(jù)庫中的 SELECT 語句,它可以有零個或者多個 filter 。filter 對應于數(shù)據(jù)庫中的篩選條件如 WHERELIMIT 等。

獲取單個對象
>>> one_entry = Entry.objects.get(pk=1)

PS:pkprimary key 。

獲取所有對象
>>> all_entries = Entry.objects.all()

應用篩選器
>>> entry = Entry.objects.filter(pub_date__year=2006)

Limiting
>>> entries = Entry.objects.all()[:5]

排序
>>> entry = Entry.objects.order_by('headline')[0]

字段查詢

字段查詢對應于 SQL 中的 WHERE 語句,可以通過向 QuerySet 對象的方法 filter()、exclude()get() 中傳入特定的參數(shù)來實現(xiàn)。
基本的查詢參數(shù)語法如下:field__lookuptype=value 。

如:>>> Entry.objects.filter(pub_date__lte='2006-01-01')
等同于:SELECT * FROM blog_entry WHERE pub_date <= '2006-01-01';

其中 lteless or equal(小于等于)。其他類似的 lookuptype 還包括 gt(大于)、gte(大于等于)、lt(小于)、exact、iexact(忽略大小寫)、startswith、istartswith、endswith、iendswith、contains、range(指定范圍)、regex(正則表達式)、iregex 等。

以下為一些常見的使用示例:

  • exact:>>> Entry.objects.get(headline__exact="Cat bites dog")
    等于 SELECT ... WHERE headline = 'Cat bites dog';

  • iexact:>>> Blog.objects.get(name__iexact="beatles blog")
    等于 SELECT ... WHERE name ILIKE 'beatles blog';

  • startswith:>>> Entry.objects.filter(headline__startswith='Lennon')
    等于 SELECT ... WHERE headline LIKE 'Lennon%';

  • contains:>>> Entry.objects.get(headline__contains='Lennon')
    等于 SELECT ... WHERE headline LIKE '%Lennon%';

  • in:>>> Entry.objects.filter(id__in=[1, 3, 4])
    等于 SELECT ... WHERE id IN (1, 3, 4);

  • range:

import datetime
start_date = datetime.date(2005, 1, 1)
end_date = datetime.date(2005, 3, 31)
Entry.objects.filter(pub_date__range=(start_date, end_date))

等于 SELECT ... WHERE pub_date BETWEEN '2005-01-01' and '2005-03-31';

Manager

Manager 是提供給 Django 模型,用于做數(shù)據(jù)庫查詢操作的接口。Django 項目中的每一個模型都需要至少包含一個 Manager 對象。

默認情況下,Django 會在每一個模型類中添加一個名為 objects 的 Manager 。通過將 models.Manager() 賦值給除 objects 以外的類屬性,可以覆蓋此默認行為:

from django.db import models

class Person(models.Model):
    #...
    people = models.Manager()

此時 Person.objects.all() 查詢語句會報出 AttributeError 錯誤,而 Person.people.all() 則返回所有的 Person 對象。

自定義 Manager

自定義的 Manager 方法可以向模型中添加表級別的查詢功能。與之對應的紀錄級別的功能則需要使用模型方法

如:

# First, define the Manager subclass.
class DahlBookManager(models.Manager):
    def get_queryset(self):
        return super().get_queryset().filter(author='Roald Dahl')

# Then hook it into the Book model explicitly.
class Book(models.Model):
    title = models.CharField(max_length=100)
    author = models.CharField(max_length=50)

    objects = models.Manager() # The default manager.
    dahl_objects = DahlBookManager() # The Dahl-specific manager.

以上面的模型為例,Book.objects.all() 會返回數(shù)據(jù)庫中所有的書籍信息,而 Book.dahl_objects.all() 則會返回所有作者為 Roald Dahl 的書籍。
Django 允許向模型中添加任意數(shù)量的 Manager() 實例,因此可以用來為模型定義一些通用的篩選器。如:

class AuthorManager(models.Manager):
    def get_queryset(self):
        return super().get_queryset().filter(role='A')

class EditorManager(models.Manager):
    def get_queryset(self):
        return super().get_queryset().filter(role='E')

class Person(models.Model):
    first_name = models.CharField(max_length=50)
    last_name = models.CharField(max_length=50)
    role = models.CharField(max_length=1, choices=[('A', _('Author')), ('E', _('Editor'))])
    people = models.Manager()
    authors = AuthorManager()
    editors = EditorManager()

Person.people.all()Person.authors.all()Person.editors.all() 都可以作為從模型中獲取數(shù)據(jù)的接口,且 authorseditors 已預先根據(jù) role 對數(shù)據(jù)進行了篩選。

參考資料

Django 2.2 官方文檔

?著作權歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容