Django 是由 Python 语言编写的基于 MVC(即 Model View Controller)架构的 Web 开发框架。
其架构中的模型(Model)主要负责处理 Web 应用的数据逻辑部分,包括定义数据存储单位(即数据库表)的字段属性和行为、与数据库交互以及其他相关联的操作。
通常一个模型映射于一个特定的数据库表。
Django 中的模型有以下几个基本属性:
- 每个模型都是继承自
django.db.models.Model
类的子类 - 模型类的属性分别对应于与之相关联的数据表中的字段
- Django 会自动生成用于访问数据库的 API
一、基本使用
项目初始化
在开始编写 Web 应用代码之前,需要先使用如下命令初始化一个 Django 项目并创建应用:1
2
3$ django-admin startproject myproject
$ cd myproject
$ python manage.py startapp myapp
最终生成的项目目录结构如下:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15myproject
├─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
模型:1
2
3
4
5from 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
两个类属性即对应于数据库表的两个字段。Person
模型会以如下的 SQL 语句创建与之关联的数据库表(id
字段默认会自动添加):1
2
3
4
5CREATE 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
文件,内容如下:1
2
3
4
5INSTALLED_APPS = [
#...
'myapp',
#...
]
之后即可使用 python manage.py makemigrations myapp
创建数据库迁移文件;
再运行 python manage.py migrate
命令将模型中定义的表结构迁移至数据库中。
Django Shell 测试
完成数据库迁移后,可使用 python manage.py shell
命令进入 Django Shell 交互式命令行,通过 Django 提供的模型 API 进行测试(插入数据):1
2
3
4
5
6
7>>> 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 数据库查询最终结果,John Smith 已添加至数据表中:1
2
3
4
5
6
7>>> 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)
模型中最重要的也是唯一必须存在的项目就是字段,它由模型类的属性定义,用来表述与模型相关联的数据表的结构。
字段的类型与选项
模型中的每个字段都是 django.db.models.Field
类的实例,对应于数据库表中的列。
Django 内置了大量的字段类型,如 CharField
,TextField
和 DateTimeField
等。具体可查看 模型字段参考。
每个字段都可以接收特定的字段相关的参数,比如 CharField
需要传入 max_length
用于定义 VARCHAR
类型的字符长度。
此外还有一些通用的可选的字段选项。如:
null
:如为 True,则 Django 会将空值在数据库中存为 NULL。该选项默认为 False。blank
:如为 True,则该字段允许为空。与null
选项不同,blank
是与表单验证相关的,而null
是数据库相关的。default
:用于设置字段的默认值。primary_key
:用于设置模型的主键。如未指定任何字段为主键,则 Django 会自动添加IntegerField
字段作为主键。unique
:设置字段的值是否允许重复。
PS:默认情况下,Django 会给每个模型添加如下字段id = models.AutoField(primary_key=True)
作为为自增的主键。如果想覆盖此默认行为,直接手动指定其他字段为主键(primary_key=True
)即可。
关系
额,关系型数据库的强大之处即在于各数据库表之间的相互关联。Django 支持定义三种最常见的数据库关系:多对一、多对多和一对一。
可以通过 django.db.models.ForeignKey
创建多对一关系,只需要像定义其他字段那样将它作为类属性引入即可。如:1
2
3
4
5
6
7
8
9from 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
命令创建数据库迁移文件:1
2
3
4
5$ 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
命令查看具体会执行哪些 SQL 语句(基于 sqlite3):1
2
3
4
5
6
7
8
9
10
11
12$ 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;
多对多和一对一的数据库关系则分别可以使用 ManyToManyField
和 OneToOneField
定义。
三、模型的属性与方法
Meta 选项
模型的 Meta 选项在模型类的定义中是可选的,它基本上包含了除字段以外的所有内容。比如数据纪录的顺序(ordering
)、关联的数据库表的名称(db_table
)和索引(indexes
)等。
Django 模型支持的所有 Meta 选项可以参考 Model Meta options
示例代码:1
2
3
4
5
6
7
8from django.db import models
class Ox(models.Model):
horn_length = models.IntegerField()
class Meta:
ordering = ["horn_length"]
verbose_name_plural = "oxen"
自定义模型方法
在模型中创建自定义方法可以为模型对象添加个性化的“底层”功能。参考如下代码:1
2
3
4
5
6
7
8
9
10
11from 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 模型除了可以从数据库中读取和写入数据等基本功能外,还可以通过它调用自定义的 full_name
方法完成额外的需求(返回全名)。
覆盖默认的模型方法
有些情况下,还可以通过修改模型内置的方法,改变模型与数据库的具体交互方式。尤其是 save()
(向数据库中存入数据)和 delete()
(从数据库中删除纪录)等方法。如:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15from 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)
重新迁移数据库,进入 Django Shell 测试,结果如下:1
2
3
4
5
6
7>>> 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'
四、数据库操作
一旦创建了数据模型,Django 即会自动生成与数据库交互的 API 供用户创建、获取、更新和删除数据对象。
此处先创建如下的模型文件供测试使用:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29from 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
可以通过实例化模型类创建一个数据对象,并调用其 save()
方法将对应的记录插入(执行 INSERT
SQL 语句)到数据库表中。1
2
3
4
5
6
7
8
9>>> 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 与操作普通字段的方式相同,将正确类型的对象赋值给对应字段并调用 save()
方法即可。如:1
2
3
4
5>>> 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()
方法:1
2
3>>> from blog.models import Author
>>> joe = Author.objects.create(name="Joe")
>>> entry.authors.add(joe)
获取数据
从数据库中获取数据会生成一个 QuerySet
对象,它代表从数据库中取出的数据对象的集合。QuerySet
等同于数据库中的 SELECT
语句,它可以有零个或者多个 filter
。filter
对应于数据库中的筛选条件如 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()
中传入特定的参数来实现。
基本的查询参数语法如下: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:
1
2
3
4import 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 模型,用于做数据库查询操作的接口。Django 项目中的每一个模型都需要至少包含一个 Manager 对象。
默认情况下,Django 会在每一个模型类中添加一个名为 objects
的 Manager 。通过将 models.Manager()
赋值给除 objects
以外的类属性,可以覆盖此默认行为:1
2
3
4
5from django.db import models
class Person(models.Model):
#...
people = models.Manager()
此时 Person.objects.all()
查询语句会报出 AttributeError
错误,而 Person.people.all()
则返回所有的 Person 对象。
自定义 Manager
自定义的 Manager 方法可以向模型中添加表级别的查询功能。与之对应的纪录级别的功能则需要使用模型方法。
如:1
2
3
4
5
6
7
8
9
10
11
12# 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()
会返回数据库中所有的书籍信息,而 Book.dahl_objects.all()
则会返回所有作者为 Roald Dahl
的书籍。
Django 允许向模型中添加任意数量的 Manager()
实例,因此可以用来为模型定义一些通用的筛选器。如:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15class 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()
都可以作为从模型中获取数据的接口,且 authors
与 editors
已预先根据 role
对数据进行了筛选。