4.1 在视图中执行数据库查询的"愚蠢"方式
- 数据库服务器应该有一层抽象
4.2 配置数据库
- 初始配置
python
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.sqlite3',
'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
}
}
- 初始配置说明
- ENGINE 告诉Django使用哪个数据库引擎
- NAME告诉Django数据库的名称
4.3 第一个应用
-
项目与应用之间的区别:配置和代码
- 一个项目是一系列DJango应用的实例,外加那些应用的配置
- 一个应用是一系列便携的Django功能,通常包含模型和视图。打包在一个Python包里
-
创建应用:
python manage.py startapp books├─books
│ │ admin.py
│ │ apps.py
│ │ models.py
│ │ tests.py
│ │ views.py
│ │ init.py
│ │
│ └─migrations
│ init.py
4.4 使用Python定义模型
-
Django模型是使用Python代码对数据库中数据的描述,是数据的结构,等效于SQL中的CREATE TABLE语句,不过是用python代码而非SQL表述的
-
Django通过模型在背后执行SQL,返回遍历的python数据结构,表示数据库表中的行
-
用python代替SQL定义数据模型的理由
- 内省(introspection)有开销,而且不完美
- 为了提供便利的数据访问API,Django需要以某种方式知晓数据库布局,而这一需求有两种实现方式
- 第一种使用Python明确描述数据
- 第二种是在运行时内省数据库,推知数据库模型
- 需要在一个地方存储表的元数据,
- 首先,运行时内省数据库肯定有消耗
- 如果每次执行请求,或者只是初始化Web服务器都要内省数据库,那消耗是无法接受的
- 需要在一个地方存储表的元数据,
- 为了提供便利的数据访问API,Django需要以某种方式知晓数据库布局,而这一需求有两种实现方式
- 更易做版本控制,跟踪数据布局的变化
- SQL对数据布局的元数据只有部分支持
- 不同的数据库平台使用的SQL不一致
- 内省(introspection)有开销,而且不完美
-
缺点
- 模型的Python代码可能与数据库的真正结构脱节
-
注意
- DJango提供了一个实用程序,可以内省现有的数据库,生成模型。有了这个程序,可以快速让旧代码运行起来
4.4.1 第一个模型
- 本章实现一个基本的"图书-作者-出版社"数据布局
mysite\books\models.py内容
python
from django.db import models
# Create your models here.
'''
CREATE TABLE "books_publisher" (
"id" serial NOT NULL PRIMARY KEY,
"name" varchar(30) NOT NULL,
"address" varchar(50) NOT NULL,
"city" varchar(60) NOT NULL,
"state_province" varchar(30) NOT NULL,"country" varchar(50) NOT NULL,
"website" varchar(200) NOT NULL
);
'''
class Publisher(models.Model):
name = models.CharField(max_length=30)
address = models.CharField(max_length=50)
city = models.CharField(max_length=60)
state_province = models.CharField(max_length=30)
country = models.CharField(max_length=50)
website = models.URLField()
class Author(models.Model):
first_name = models.CharField(max_length=30)
last_name = models.CharField(max_length=40)
email = models.EmailField()
class Book(models.Model):
title = models.CharField(max_length=100)
authors = models.ManyToManyField(Author) # 一本书有一个或多位作者
# 但是book数据库表没有authors列,此时,Django会创建一个额外的表,
# 一个多对多联结表(join table),处理书与作者之间的对应关系
publisher = models.ForeignKey(Publisher)
publication_date = models.DateField()
4.4.2 安装模型
-
要在数据库中创建表
- 第一步是在Django项目中激活那些模型
-
激活方法,在
settings.py中,更改
python
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'books'
]
-
然后,运行:
python manage.py check- check命令运行Django系统检查框架,验证Django项目的一系列静态检查
- 如果正常,会出现:
System check identified no issues (0 silenced)。
-
确认模型有效后,运行下列命令:
python manage.py makemigrations books- 告诉Django你对模型做了修改
- Django把对模型的改动存储在迁移中,迁移就是磁盘上的文件。
- 运行上述命令后,books应用的migrations文件夹里会出现一个名为0001_initial.py的文件。
- migrate命令会去查看最新的迁移文件,自动更新数据库模式
4.5 基本的数据访问
-
运行
python manage.py shellIn [1]: from books.models import Publisher
In [2]: p1 = Publisher(name='Apress', address='2855 Telegraph Avenue',
...: ... city='Berkeley', state_province='CA', country='U.S.A.',
...: ... website='http://www.apress.com/')In [3]: p1.save()
In [4]: p2 = Publisher(name="O'Reilly", address='10 Fawcett St.',
...: ... city='Cambridge', state_province='MA', country='U.S.A.',
...: ... website='http://www.oreilly.com/')In [5]: p2.save()
In [6]: publisher_list = Publisher.objects.all()
In [7]: publisher_list
Out[7]: <QuerySet [<Publisher: Publisher object (1)>, <Publisher: Publisher object (2)>]> -
首先导入Publisher模型类
-
挺各个字段的值
-
为了把对象保存到数据库中,调用save()方法,背后执行SQL SELECT 语句
-
如果想一步创建对象并保存,用objects.create()方法
In [8]: p1 = Publisher.objects.create(name='Apress',
...: ... address='2855 Telegraph Avenue',
...: ... city='Berkeley', state_province='CA', country='U.S.A.',
...: ... website='http://www.apress.com/')In [9]: p2 = Publisher.objects.create(name="O'Reilly",
...: ... address='10 Fawcett St.', city='Cambridge',
...: ... state_province='MA', country='U.S.A.',
...: ... website='http://www.oreilly.com/')In [10]: publisher_list = Publisher.objects.all()
In [11]: publisher_list
Out[11]: <QuerySet [<Publisher: Publisher object (1)>, <Publisher: Publisher object (2)>, <Publisher: Publisher object (3)>, <Publisher: Publisher object (4)>]>
4.5.1 添加模型的字符串表示形式
-
models.py如下from django.db import models
Create your models here.
'''
CREATE TABLE "books_publisher" (
"id" serial NOT NULL PRIMARY KEY,
"name" varchar(30) NOT NULL,
"address" varchar(50) NOT NULL,
"city" varchar(60) NOT NULL,
"state_province" varchar(30) NOT NULL,"country" varchar(50) NOT NULL,
"website" varchar(200) NOT NULL
);
'''
class Publisher(models.Model):
name = models.CharField(max_length=30)
address = models.CharField(max_length=50)
city = models.CharField(max_length=60)
state_province = models.CharField(max_length=30)
country = models.CharField(max_length=50)
website = models.URLField()
def str(self):
return self.nameclass Author(models.Model):
first_name = models.CharField(max_length=30)
last_name = models.CharField(max_length=40)
email = models.EmailField()
def str(self):
return u'%s %s'%(self.first_name, self.last_name)class Book(models.Model):
title = models.CharField(max_length=100)
authors = models.ManyToManyField(Author) # 一本书有一个或多位作者
# 但是book数据库表没有authors列,此时,Django会创建一个额外的表,
# 一个多对多联结表(join table),处理书与作者之间的对应关系
publisher = models.ForeignKey(Publisher, on_delete=models.CASCADE,)
# 注意django2.0需要加 ", on_delete=models.CASCADE,"
publication_date = models.DateField()
def str(self):
return self.title -
退出shell后重新进入才能生效
python
In [1]: from books.models import Publisher
In [2]: publisher_list = Publisher.objects.all()
In [3]: publisher_list
Out[3]: <QuerySet [<Publisher: Apress>, <Publisher: O'Reilly>, <Publisher: Apress>, <Publisher: O'Reilly>]>
插入和更新数据
- 先使用关键字参数创建模型的实例
python
>>> p = Publisher(name='Apress',
... address='2855 Telegraph Ave.',
... city='Berkeley',
... state_province='CA',
... country='U.S.A.',
... website='http://www.apress.com/')
- 然后把记录保存到数据库
p.save()- 这一步相当于:
sql
INSERT INTO books_publisher
(name, address, city, state_province, country, website)
VALUES
('Apress', '2855 Telegraph Ave.', 'Berkeley', 'CA',
'U.S.A.', 'http://www.apress.com/');
- Publisher 模型有个自增量主键id,调用save()方法后还会做:
- 计算记录的主键值,把它赋值给实例的id属性
- 后续再调用save()方法就将就地保存记录,而不是新建记录(执行SQL UPDATE语句,而不是INSERT语句)
p.id # 52p.name = 'Xu jie'p.save()
sql
UPDATE books_publisher SET
name = 'Apress Publishing',
address = '2855 Telegraph Ave.',
city = 'Berkeley',
state_province = 'CA',
country = 'U.S.A.',
website = 'http://www.apress.com'
WHERE id = 52;
sql
UPDATE books_publisher SET
name = 'Apress Publishing'
WHERE id=52;
4.5.3 选择对象
- 检索指定模型中的每个记录
Publisher.objects.all()- 相当于
SELECT id, name, address, city, state_province, country, website FROM books_publisher; - 注意,查找数据时,Django 使用的不是SELECT *,而是把所有字段列出来。这样做是有原因的:某些情况下,SELECT * 较慢,而(更重要的是)列出字段更接近"Zen of Python"(Python之禅)中的一个原则------"Explicit is better than implicit"(明了胜于晦涩)。
- 分析代码
- Publisher是我们定义的模型
- 访问objects属性,叫管理器,管理器负责所有"表层"数据操作,包括数据查询,所有模型都自动获得一个objects管理器,需要查询模型实例时都要使用它
- 调用all()方法,返回数据库中的所有行,虽然返回的对象看似一个列表,但其实是一个查询集合(QuerySet)--表示数据库中一系列行的对象
4.5.4 过滤数据
- 用filter()方法过滤数据
Publisher.objects.filter(name='Apress')
sql
SELECT id, name, address, city, state_province, country, website
FROM books_publisher
WHERE name = 'Apress';
- 把多个参数传给filter()方法:
Publisher.objects.filter(country="U.S.A.",state_province="CA")
sql
SELECT id, name, address, city, state_province, country, website
FROM books_publisher
WHERE country = 'U.S.A.'
AND state_province = 'CA';
- 查找操作默认使用SQL = 运算符做精确匹配查找,其他还有:
Publisher.objects.filter(name__contains="press")- 注意,name 和contains 之间有两个下划线。与 Python 一样,Django 使用双下划线表示"魔法"操作。这里,Django 把__contains 部分转换成 SQL LIKE 语句:
sql
SELECT id, name, address, city, state_province, country, website
FROM books_publisher
WHERE name LIKE '%press%';
- 支持的其他查找类型有:icontains(不区分大小写的LIKE),startswith 和endswith,以及range(SQL BETWEEN 语句)
4.5.5 检索单个对象
- 获取单个对象用get()方法
Publisher.objects.get(name="Apress")- 得到多个对象的查询会导致异常
- 不返回对象的查询也导致异常:
- DoesNotExist 异常是模型类的属性------Publisher.DoesNotExist
python
try:
p = Publisher.objects.get(name='Apress')
except Publisher.DoesNotExist:
print ("Apress isn't in the database yet.")
else:
print ("Apress is in the database.")
4.5.6 排序数据
- 使用order_by() 方法
Publisher.objects.order_by("name")
sql
SELECT id, name, address, city, state_province, country, website
FROM books_publisher
ORDER BY name;
- 根据多个字段排序(以第一个字段排不出顺序时使用第二个字段),提供多个参数
Publisher.objects.order_by("state_province", "address")
- 反向排序。方法是在字段名称前面加上"-"(减号)
Publisher.objects.order_by("-name")
- 在模型中指定默认排序
- 内嵌在Publisher 类定义体中的class Meta
- 任何模型都可以使用Meta 类指定多个针对所在模型的选项
python
class Publisher(models.Model):
name = models.CharField(max_length=30)
address = models.CharField(max_length=50)
city = models.CharField(max_length=60)
state_province = models.CharField(max_length=30)
country = models.CharField(max_length=50)
website = models.URLField()
def __str__(self):
return self.name
class Meta:
ordering = ['name']
4.5.7 链式查找
Publisher.objects.filter(country="U.S.A.").order_by("-name")
sql
SELECT id, name, address, city, state_province, country, website
FROM books_publisher
WHERE country = 'U.S.A'
ORDER BY name DESC;
4.5.8 切片数据
- 只查找固定数量的行
- 只想显示第一个:
Publisher.objects.order_by('name')[0]
sql
SELECT id, name, address, city, state_province, country, website
FROM books_publisher
ORDER BY name
LIMIT 1;
- 可以使用 Python 的范围切片句法检索数据子集
Publisher.objects.order_by('name')[0:2]
sql
SELECT id, name, address, city, state_province, country, website
FROM books_publisher
ORDER BY name
OFFSET 0 LIMIT 2;
- 注意,不支持使用负数
- 解决办法:
Publisher.objects.order_by('-name')[0]
- 解决办法:
4.5.9 在一个语句中更新多个对象
Publisher.objects.filter(id=52).update(name='Apress Publishing')
sql
UPDATE books_publisher
SET name = 'Apress Publishing'
WHERE id = 52;
Publisher.objects.all().update(country='USA')- update() 方法有返回值,是一个整数,表示修改的记录数量
4.5.10 删除对象
-
若想从数据库中删除一个对象,只需在对象上调用delete() 方法:
p = Publisher.objects.get(name="O'Reilly")p.delete()
-
还可以在任何QuerySet 对象上调用delete() 方法,批量删除对象。
Publisher.objects.filter(country='USA').delete()Publisher.objects.all().delete()Publisher.objects.all()- 为了防止不小心把表中的数据都删除,想删除表中的一切数据时,Django 要求必须显式调用all() 方法。
- 无效代码:
Publisher.objects.delete()
- 无效代码:

关注我,后台有好东西!