Django 是由 Python 語言編寫的基于 MVC(即 Model View Controller)架構(gòu)的 Web 開發(fā)框架轴踱。
其架構(gòu)中的模型(Model)主要負責處理 Web 應用的數(shù)據(jù)邏輯部分棺聊,包括定義數(shù)據(jù)存儲單位(即數(shù)據(jù)庫表)的字段屬性和行為掸掸、與數(shù)據(jù)庫交互以及其他相關(guān)聯(lián)的操作盆犁。
通常一個模型映射于一個特定的數(shù)據(jù)庫表幽七。
Django 中的模型有以下幾個基本屬性:
- 每個模型都是繼承自
django.db.models.Model
類的子類 - 模型類的屬性分別對應于與之相關(guān)聯(lián)的數(shù)據(jù)表中的字段
- Django 會自動生成用于訪問數(shù)據(jù)庫的 API
一谬运、基本使用
項目初始化
在開始編寫 Web 應用代碼之前爽冕,需要先使用如下命令初始化一個 Django 項目并創(chuàng)建應用:
$ django-admin startproject myproject
$ cd myproject
$ python manage.py startapp myapp
最終生成的項目目錄結(jié)構(gòu)如下:
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_name
和 last_name
兩個類屬性即對應于數(shù)據(jù)庫表的兩個字段。
Person
模型會以如下的 SQL 語句創(chuàng)建與之關(guān)聯(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é)構(gòu)遷移至數(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)
模型中最重要的也是唯一必須存在的項目就是字段,它由模型類的屬性定義疏叨,用來表述與模型相關(guān)聯(lián)的數(shù)據(jù)表的結(jié)構(gòu)潘靖。
字段的類型與選項
模型中的每個字段都是 django.db.models.Field
類的實例,對應于數(shù)據(jù)庫表中的列蚤蔓。
Django 內(nèi)置了大量的字段類型卦溢,如 CharField
,TextField
和 DateTimeField
等秀又。具體可查看 模型字段參考单寂。
每個字段都可以接收特定的字段相關(guān)的參數(shù),比如 CharField
需要傳入 max_length
用于定義 VARCHAR
類型的字符長度吐辙。
此外還有一些通用的可選的字段選項宣决。如:
-
null
:如為 True,則 Django 會將空值在數(shù)據(jù)庫中存為 NULL袱讹。該選項默認為 False疲扎。 -
blank
:如為 True昵时,則該字段允許為空捷雕。與null
選項不同,blank
是與表單驗證相關(guān)的壹甥,而null
是數(shù)據(jù)庫相關(guān)的救巷。 -
default
:用于設置字段的默認值。 -
primary_key
:用于設置模型的主鍵句柠。如未指定任何字段為主鍵浦译,則 Django 會自動添加IntegerField
字段作為主鍵。 -
unique
:設置字段的值是否允許重復溯职。
PS:默認情況下精盅,Django 會給每個模型添加如下字段
id = models.AutoField(primary_key=True)
作為為自增的主鍵。如果想覆蓋此默認行為谜酒,直接手動指定其他字段為主鍵(primary_key=True
)即可叹俏。
關(guān)系
額,關(guān)系型數(shù)據(jù)庫的強大之處即在于各數(shù)據(jù)庫表之間的相互關(guān)聯(lián)僻族。Django 支持定義三種最常見的數(shù)據(jù)庫關(guān)系:多對一粘驰、多對多和一對一。
可以通過 django.db.models.ForeignKey
創(chuàng)建多對一關(guān)系,只需要像定義其他字段那樣將它作為類屬性引入即可先舷。如:
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ù)庫關(guān)系則分別可以使用 ManyToManyField
和 OneToOneField
定義贷祈。
三、模型的屬性與方法
Meta 選項
模型的 Meta 選項在模型類的定義中是可選的顶伞,它基本上包含了除字段以外的所有內(nèi)容饵撑。比如數(shù)據(jù)紀錄的順序(ordering
)、關(guān)聯(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ù)庫中的篩選條件如 WHERE
或 LIMIT
等屎勘。
獲取單個對象
>>> one_entry = Entry.objects.get(pk=1)
PS:pk
即 primary 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';
其中 lte
即 less 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ù)的接口,且 authors
與 editors
已預先根據(jù) role
對數(shù)據(jù)進行了篩選或详。