通过【Django入门】——MVC和MVT两种模式的异同,我们知道Django是典型的遵循MVT模式的后端框架,那么在实际的开发中M(模型)、V(视图)和T(模板)三个模块都是怎么使用的呢?
本文就将通过一个简单的示例来演示如何使用M(模型),关于使用V(视图)和T(模板)的示例请见【Django入门】——Django中视图函数和模板文件的使用。
在文章从零开始创建并启动一个Django项目中,我们知道了如何创建并启动一个Django项目,我们还知道通过在项目中创建应用后,在应用的目录有一个models.py
文件,该文件即对应MVT模式框架中的Model模块,主要用于定义操作数据表的模型类。本文即要学习如何在models.py
文件中通过使用继承自Model
类的自定义类来操作数据表。
在Django框架中,因为使用Model
类来实现数据表的操作是通过ORM框架来实现的,而ORM框架是內置在Django框架中的,所以首先需要了解什么是ORM。
定义:ORM的全称是Object Relational Mapping(对象关系映射),其主要作用是在编程中,把面向对象的概念跟数据库中表的概念对应起来。
实际上,通过ORM实现数据表操作,主要是基于<类、对象>和<数据表字段、数据表存储的数据>之间有这样的相似性:
因此,在本文后续可以看到:
models.py
文件中,通过继承Model
类来定义类(一般称为模型类),且在类中通过model
模块中类来定义类属性,再结合一系列操作,则可以创建一个数据表;Model
类创建实例对象,进而为实例对象添加与类属性同名的实例属性,则可以向数据表中插入数据。即:在ORM框架中,一个类对应一张数据表,一个对象对应数据表中的一条记录,可以通过操作对象的方式来操作数据表的记录。
首先,在models.py
文件中继承Model
类创建一个BookInfo
模型类,在模型类中通过django.db.models
中的字段类型类创建模型类的类属性:
from django.db import models
# Create your models here.
# 创建图书类
class BookInfo(models.Model):
"""图书模型类"""
# 图书名称,CharField说明是该类属性是一个字符串,max_length指定最大长度
book_title = models.CharField(max_length=20)
# 出版日期,DateField说明该类属性是一个日期
book_pub_date = models.DateField()
上述代码中的CharField
、DateField
都是字段类型类,还可以通过这些类的参数来指定选项,关于Django的django.db.models
中常用的字段类型类和参数选项,请见:【Django入门】——django.db.models中常用字段类型类和参数选项。
使用如下命令生成迁移文件:
python manage.py makemigrations
结果为:
(virtualenv4_django) william@ubuntu:~/django_intro/test1$ python manage.py makemigrations
Migrations for 'booktest':
booktest/migrations/0001_initial.py
- Create model BookInfo
执行下列命令,可以创建数据表:
python manage.py migrate
当出现下列结果,表示数据表创建成功:
(virtualenv4django) williamaubuntu:-django-introtest1s python manage.py migrate
Operations to perform:
AppLy all migrations: admin, auth, booktest, contenttypes, sessions
Running migrations:
Applying contenttypes.0001 initial... OK
Applying auth.0001 initial... OK
Applying admin.0001 initial... OK
Applying admin.0002_Logentry_remove_auto_add... OK
Applying admin.0003 Logentry add action flag choices... OK
Applying contenttvpes.0002 remove content type name... OK
Applying auth.0002 alter permission name max Length... OK
Applying auth.0o03 alter user email max Length... OK
Applying auth.0004 alter user username opts... OK
Applying auth.0005 alter user last Login null... OK
Applying auth.0006 require contenttypes 0002.... OK
Applying auth.0007 alter validators add error messages... OK
Applying auth.0008 alter user username max length.... OK
Applying auth.0009 alter user last name_max Length... OK
Applying auth.0010 alter group name max Length... OK
Applying auth.0011 update proxy permissions... OK
Applying booktest.0001 initial... OK
Applying sessions.0o01 initial... OK
你可能有疑惑的是,我们并没有显式安装数据库,为什么可以创建数据表,这是因为在安装Django时,会默认安装SQLite数据库1,你可以看到项目中的settings.py
文件中有如下配置文件:
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.sqlite3',
'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
}
}
事实上,此时在项目目录下有一个名为db.sqlite3
的数据库文件,为了可以使用可视化软件sqliteman打开该文件,需要用下列命令来下载软件(笔者在使用下列命令下载时遇到了和这篇文章相同的问题,并采用了文章中的方法得到了解决):
sudo apt install sqliteman
下载完成之后,在终端输入sqliteman
先打开数据库可视化软件,然后打开db.sqlite3
文件:
~/django_intro/test1$ python manage.py shell
Python 3.6.9 (default, Apr 18 2020, 01:56:04)
[GCC 8.4.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
(InteractiveConsole)
>>> from booktest.models import BookInfo # 导入模型类
>>> book = BookInfo() # 创建模型类的实例对象
>>> book.book_title = '天龙八部' # 为实例对象添加属性
>>> from datetime import date # 导入日期模块
>>> book.book_pub_date = date(1960, 1, 1) # 为实例对象添加属性
>>> book.save() # 将数据插入数据表中
通过上述安装的SQLite数据库可视化软件sqliteman可以看到在数据表booktest_bookinfo
中成功插入了一条数据:
如前所示,因为在ORM框架中,一个用来创建数据表的类所创建的对象对应了数据表中的一条记录,所以为了查询数据表中的记录,可以先获取对应记录的对象,然后通过访问对象的属性来达到数据表中对应记录的目的。
>>> book = BookInfo.objects.get(id=2)
>>> book
<BookInfo: BookInfo object (2)>
>>> type(book)
<class 'booktest.models.BookInfo'>
>>> book.book_title
'天龙八部'
>>> book.book_pub_date
datetime.date(1960, 1, 1)
修改表中的数据,其方式和查询表中的数据方法类似,只是需要注意的是,为了是得修改操作于数据表中生效,需要调用对象的save()
方法。
>>> book.book_pub_date = date(1960, 10, 10)
>>> book.save()
>>> book.delete()
(1, {'booktest.BookInfo': 1})
在数据库中可能会存在多张表,表与表之间可能存在关联关系。现在假设有这样一个需求:
BookInfo
模型类创建了一个对应的数据表;HeroInfo
模型类,同时也创建处对应的数据表;booktest_heroinfo
中的每条数据都对应booktest_bookinfo
表中的一条记录(即一本金庸的武侠小说中可能会有多个英雄人物,但是每一个英雄人物一般仅属于一本小说)。为了实现上述需求,在定义HeroInfo
模型类时,需要指定二者的关系,具体方法是:在HeroInfo
模型类中定义一个名为hero_book
的类属性,该类属性是一个外键类ForeignKey
使用'BookInfo'
作为参数实例化得到的对象。
# 创建英雄人物类
class HeroInfo(models.Model):
"""英雄人物模型类"""
# 英雄姓名字段
hero_name = models.CharField(max_length=20)
# 英雄性别字段,性别默认为男
hero_gender = models.BooleanField(default=False)
# 英雄描述信息字段
hero_desc = models.CharField(max_length=128)
# 关系属性字段,通过外键建立图书类和英雄人物类之间的一对多关系,在数据表中的名称格式为:关键属性字段名_id
hero_book = models.ForeignKey('BookInfo', models.CASCADE)
(virtualenv4_django) william@ubuntu:~/django_intro/test1$ python manage.py makemigrations
Migrations for 'booktest':
booktest/migrations/0002_heroinfo.py
- Create model HeroInfo
(virtualenv4_django) william@ubuntu:~/django_intro/test1$ python manage.py migrate
Operations to perform:
Apply all migrations: admin, auth, booktest, contenttypes, sessions
Running migrations:
Applying booktest.0002_heroinfo... OK
(virtualenv4_django) william@ubuntu:~/django_intro/test1$ python manage.py shell
Python 3.6.9 (default, Apr 18 2020, 01:56:04)
[GCC 8.4.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
(InteractiveConsole)
>>>
>>> from booktest.models import BookInfo, HeroInfo
>>> book = BookInfo.objects.get(id=2)
>>> book
<BookInfo: BookInfo object (2)>
>>> book.book_title
'天龙八部'
>>> hero = HeroInfo()
>>> hero.hero_name = '段誉'
>>> hero.hero_gender = False
>>> hero.hero_desc = '六脉神剑'
>>> hero.hero_book = book # 将BookInfo对应数据表的主键关联为HeroInfo数据表的外键
>>> hero.save()
>>> hero2 = HeroInfo() # 在英雄信息表中增加一条记录
>>> hero2.hero_name = '乔峰'
>>> hero2.hero_desc = '降龙十八掌'
>>> hero2.hero_book = book
>>> hero2.save()
>>> hero3 = HeroInfo.objects.get(id=2)
>>> hero3
<HeroInfo: HeroInfo object (2)>
>>> hero3.hero_book.book_title # 通过英雄信息表查询图书信息表数据
'天龙八部'
>>> book.heroinfo_set.all() # 通过图书表数据查询英雄信息表的数据
<QuerySet [<HeroInfo: HeroInfo object (1)>, <HeroInfo: HeroInfo object (2)>]>
>>> query_set = book.heroinfo_set.all()
>>> query_set[0]
<HeroInfo: HeroInfo object (1)>
>>> query_set[0].hero_name
'段誉'
上述案例仅涉及了数据表之间一对多的关系,即两个数据表中,一个表中的一条数据关联了另一个数据表中多条数据,而另一个数据表中的一条数据仅关联前者的一条数据。
实际上,模型类数据表之间还存在多对多和一对一这样的关系,具体请见:【Django入门】——模型类的关联关系简介。
这是一种轻量级的数据库,一般用于手机等设备上。 ↩︎