在 Django 中,查询底层数据库这项操作由对象关系映射器(Object Relational Mapper,ORM)负责。在 ORM 中,存储在一个数据库表中的数据封装为一个模型。模型则是描述数据库表中数据的 Python 对象。Django 不直接通过 SQL 查询数据库,而是使用相应的 Python 模型对象操纵。
5.1 Rango 的要求
5.2 设置数据库
创建模型之前要设置数据库。新建项目时,Django 已经自动在 settings.py 模块中添加了 DATABASES变量,其值类似下面这样:
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.sqlite3',
'NAME': BASE_DIR / 'db.sqlite3',
}
}
对 Rango 应用来说,使用默认值基本就行了。可以看到,default 数据库由一个轻量级数据库引擎驱动,即 SQLite(见 ENGINE 键)。NAME 键的值是数据库文件的路径,默认为 Django 项目根目录中的 db.sqlite3。
5.3 创建模型
什么是模型
- 模型:
模型准确且唯一的描述了数据。它包含您储存的数据的重要字段和行为。一般来说,每一个模型都映射一张数据库表。 - 基础:
每个模型都是一个 Python 的类,这些类继承 django.db.models.Model
模型类的每个属性都相当于一个数据库的字段。
利用这些,Django 提供了一个自动生成访问数据库的 API。
创建模型
在 settings.py 模块中配置好数据库之后,下面创建 Rango 应用所需的两个数据模型。Django 应用的模型保存在应用目录中的 models.py 模块里。因此,Rango 应用的模型保存在 rango/models.py文件中。
我们要定义两个类,一个类对应一个模型。这两个类都要继承基类django.db.models.Model,分别表示分类和网页。参照下述代码片段定义 Category 和 Page 模型。
在我们定义的模型中,Page 模型中的 category 字段是 ForeignKey 类型。我们在字段的构造方法中指定,与 Category 模型建立一对多关系。
from django.db import models
# Create your models here.
class Category(models.Model):
name = models.CharField(max_length=128,unique=True)
def __str__(self):
return self.name
class Page(models.Model):
category = models.ForeignKey(Category,on_delete=models.CASCADE)
title = models.CharField(max_length=128)
url = models.URLField()
views = models.IntegerField(default=0)
def __str__(self):
return self.title
5.4 创建和迁移数据库
设置
首先,数据库必须预置,即创建数据库及相关的表。请打开终端或命令提示符,进入项目的根目录(manage.py 文件所在的目录),执行下述命令。
执行这个命令后,项目中安装的所有应用都会更新各自的数据库表。这个命令执行完毕后,Django 项目的根目录中会出现 db.sqlite3 文件。
python manage.py migrate
创建一个超级用户(superuser),用于管理数据库。执行下述命令:
python manage.py createsuperuser
创建和更新模型/表
每次修改应用的模型都要通过 manage.py 中的 makemigrations 命令登记改动。在 Django 项目的根目录中执行下述命令,指明目标为 rango 应用:
上述命令执行完毕后,rango/migrations 目录中会出现一个 Python 脚本,名为 0001_initial.py。这个脚本中包含此次迁移创建数据库模式所需的一切信息。
python manage.py makemigrations rango
创建好迁移后,要提交到数据库。为此要再次执行 migrate 命令。
python manage.py migrate
5.5 Django 模型和 shell
下面说明如何在 shell 中创建 Category 实例。为了打开 shell,我们要再次在项目的根目录中运行 manage.py 脚本。执行下述命令:
python manage.py shell
# 从 Rango 应用中导入 Category 模型
from rango.models import Category
# 显示目前的所有分类
print(Category.objects.all())
<QuerySet []> # Django 1.10 的输出:一个空 QuerySet 对象
# 创建一个分类对象,存入数据库
c = Category(name="Test")
c.save()
# 再次列出所有分类对象
print(Category.objects.all())
<QuerySet [<Category: Test>] # Django 1.10
# 退出 Django shell
quit()
*不执行这一步,后面的填充数据将会报错Class ‘Category’ has no ‘objects’ member
5.6 配置管理界面
执行下述命令,启动 Django 开发服务器:
python manage.py runserver
然后打开 Web 浏览器,访问 http://127.0.0.1:8000/admin/。你会看到登录界面。
看起来不错,但是没有 Rango 应用的 Category 和 Page 模型。为了显示这两个模型,我们要给Django 一些提示。打开 rango/admin.py 文件,注册想在管理界面显示的类。下述代码注册 Category 和 Page 两个类。
from django.contrib import admin
from rango.models import Category, Page
admin.site.register(Category)
admin.site.register(Page)
Meta 类
管理界面中有拼写错误(应该是 Categories,而不是 Categorys)?为了修正这
个错误,可以在模型定义中添加嵌套的 Meta 类,在里面声明 verbose_name_plural 属性。下面是rango/models.py修改后的 Category 模型。
class Category(models.Model):
name = models.CharField(max_length=128,unique=True)
class Meta:
verbose_name_plural = 'Categories'
def __str__(self):
return self.name
5.7 编写一个填充脚本
下面为 Rango 应用编写一个填充脚本。在 Django 项目的根目录中新建一个 Python 文件,命名为populate_rango.py,写入下述代码:
import os
os.environ.setdefault('DJANGO_SETTINGS_MODULE','tango_with_django_project_v1.settings')
import django
django.setup()
from rango.models import Category,Page
def populate():
# 首先创建一些字典,列出想添加到各分类的网页
# 然后创建一个嵌套字典,设置各分类
# 这么做看起来不易理解,但是便于迭代,方便为模型添加数据
python_pages = [
{
"title": "Official Python Tutorial",
"url":"http://docs.python.org/2/tutorial/"},
{
"title":"How to Think like a Computer Scientist",
"url":"http://www.greenteapress.com/thinkpython/"},
{
"title":"Learn Python in 10 Minutes",
"url":"http://www.korokithakis.net/tutorials/python/"}]
django_pages = [
{
"title":"Official Django Tutorial",
"url":"https://docs.djangoproject.com/en/1.9/intro/tutorial01/"},
{
"title":"Django Rocks",
"url":"http://www.djangorocks.com/"},
{
"title":"How to Tango with Django",
"url":"http://www.tangowithdjango.com/"}]
other_pages = [
{
"title":"Bottle",
"url":"http://bottlepy.org/docs/dev/"},
{
"title":"Flask",
"url":"http://flask.pocoo.org"}]
cats = {
"Python": {
"pages": python_pages},
"Django": {
"pages": django_pages},
"Other Frameworks": {
"pages": other_pages}}
# 下述代码迭代 cats 字典,添加各分类,并把相关的网页添加到分类中
for cat,cat_data in cats.items():
c = add_cat(cat)
for p in cat_data["pages"]:
add_page(c,p["title"],p["url"])
# 打印添加的分类
for c in Category.objects.all():
for p in Page.objects.filter(category=c):
print("-{0}-{1}".format(str(c),str(p)))
def add_page(cat,title,url,views=0):
p = Page.objects.get_or_create(category=cat,title=title)[0]
p.url = url
p.views = views
p.save()
return p
def add_cat(name):
c = Category.objects.get_or_create(name=name)[0]
c.save()
return c
if __name__ == '__main__':
print("Starting Rango population script...")
populate()
5.8 基本流程
设置数据库
新建项目后应该告诉 Django 你想用什么数据库(settings.py 模块中的 DATABASES 设置)。此外,
还可以在 admin.py 模块中注册模型,以便在管理界面中管理。
添加模型
添加模型的过程可以分为以下 5 步。
➊ 首先在 Django 应用的 models.py 文件中定义模型。
➋ 更新 admin.py,注册新增的模型。
➌ 生成迁移:python manage.py makemigrations <app_name>。
➍ 运行迁移:python manage.py migrate。在数据库中创建模型所需的表和字段。
➎ 创建或编辑填充脚本。
有时你可能想删除数据库,重头再来。
具体步骤如下。注意,本书使用的是 SQLite 数据库,此外Django 还支持其他数据库引擎。
➊ 如果 Django 开发服务器正在运行,停止。
➋ 如果使用的是 SQLite 数据库,删除 Django 项目根目录中的 db.sqlite3 文件。这个文件与manage.py 脚本位于同一级目录中。
➌ 如果修改过应用的模型,执行 python manage.py makemigrations <app_name> 命令,记得把<app_name> 替换成 Django 应用的名称(例如 rango。)如果未修改模型,跳过这一步。
➍ 执行 python manage.py migrate 命令,新建数据库文件(使用 SQLite 的话),并创建数据库表。
➎ 执行 python manage.py createsuperuser 命令,创建一个超级用户。
➏ 最后,运行填充脚本,在新数据库中插入可信的测试数据。
练习题
❏ 更新 Category 模型,加上 views 和 likes 字段,二者的默认值均是零。
rango/models.py
class Category(models.Model):
name = models.CharField(max_length=128,unique=True)
views = models.IntegerField(default=0)
likes = models.IntegerField(default=0)
class Meta:
verbose_name_plural = 'Categories'
def __str__(self):
return self.name
❏ 创建迁移,然后执行,提交此次改动。
❏ 更新填充脚本,把“Python”分类的查看次数设为 128、点赞次数设为 64,把“Django”分类的查看次数设为 64、点赞次数设为 32,把“Other Frameworks”分类的查看次数设为 32、点赞次数设为 16。
- rango/models.py
class Category(models.Model):
name = models.CharField(max_length=128,unique=True)
views = models.IntegerField(default=0)
likes = models.IntegerField(default=0)
class Meta:
verbose_name_plural = 'Categories'
def __str__(self):
return self.name
- populate_rango.py
def add_cat(name,views,likes):
c = Category.objects.get_or_create(name=name)[0]
c.views = views
c.likes = likes
c.save()
return c
- populate_rango.py
def populate():
cats = {
"Python": {
"pages": python_pages,'views':128,'likes':64},
"Django": {
"pages": django_pages,'views':64,'likes':32},
"Other Frameworks": {
"pages": other_pages,'views':32,'likes':16}}
- populate_rango.py
def populate():
# 下述代码迭代 cats 字典,添加各分类,并把相关的网页添加到分类中
for cat,cat_data in cats.items():
c = add_cat(cat,cat_data['views'],cat_data['likes'])
for p in cat_data["pages"]:
add_page(c,p["title"],p["url"])
❏ 删除数据库,然后重新创建,再使用填充脚本填充数据。
- 如果使用的是 SQLite 数据库,删除 Django 项目根目录中的 db.sqlite3 文件。这个文件与manage.py 脚本位于同一级目录中。
- 如果修改过应用的模型,执行 python manage.py makemigrations <app_name> 命令,记得把<app_name> 替换成 Django 应用的名称(例如 rango。)如果未修改模型,跳过这一步。
- 执行 python manage.py migrate 命令,新建数据库文件(使用 SQLite 的话),并创建数据库表。
- 执行 python manage.py createsuperuser 命令,创建一个超级用户。
- 最后,运行填充脚本,在新数据库中插入可信的测试数据。
❏ 阅读 Django 官方教程的第二部分和第七部分。这两部分能巩固本章所学的数据库处理知识,还涉及一些定制 Django 管理界面的技术。
❏ 定制管理界面,访问 Page 模型时显示网页的分类、名称和 URL,就像图 5-5 那样。你要做完前一题才知道怎么解答这一题。 - rango/admin.py
from django.contrib import admin
from rango.models import Category, Page
# Register your models here.
class PageAdmin(admin.ModelAdmin):
list_display = ('title','category','url')
admin.site.register(Category)
#admin.site.register(Page)
admin.site.register(Page,PageAdmin)
小结
- category = models.ForeignKey(Category,on_delete=models.CASCADE)
- python manage.py createsuperuser此界面密码是看不到的,但可输入。
- shell 中创建 Category 实例。不执行这一步,后面的填充数据将会报错Class ‘Category’ has no ‘objects’ member