《精通Django》 第4章 Django模型

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服务器都要内省数据库,那消耗是无法接受的
    • 更易做版本控制,跟踪数据布局的变化
    • SQL对数据布局的元数据只有部分支持
    • 不同的数据库平台使用的SQL不一致
  • 缺点

    • 模型的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 shell

    In [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.name

    class 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 # 52
    • p.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()

关注我,后台有好东西!

相关推荐
CHANG_THE_WORLD9 小时前
switch case 二分搜索风格
前端·数据库
m0_598177239 小时前
SQL核心(1)
数据库·sql
DarkAthena9 小时前
【ORACLE】分区表数据倾斜会发生什么
数据库·oracle
indexsunny9 小时前
互联网大厂Java求职面试实战:微服务与Spring Boot在电商场景中的应用
java·数据库·spring boot·微服务·kafka·hibernate·电商
DarkAthena9 小时前
【GaussDB】数据静止状态下同一个SQL或同一个存储过程执行第6次报错的问题排查
数据库·sql·gaussdb
huwei8539 小时前
QT 连接数据库类
数据库·qt·oracle
wangbing112510 小时前
平台介绍-开放API后台微服务
数据库·微服务·架构
高一要励志成为佬10 小时前
【数据库】第三章 关系数据库标准语言SQL
数据库·sql
尽兴-10 小时前
MySQL执行UPDATE语句的全流程深度解析
数据库·mysql·innodb·dba·存储引擎·update