建html5响应式网站的工具,宁夏建设厅网站旧版,深圳软件公司招聘,网络推广讲师培训目录 一、模型的定义二、数据迁移三、数据表关系四、数据表操作4.1 Shell工具4.2 数据新增4.3 数据修改4.4 数据删除4.5 数据查询 Django 对各种数据库提供了很好的支持#xff0c;包括 PostgreSQL、MySQL、SQLite 和 Oracle#xff0c;而且为这些数据库提供了统一的 API 方法… 目录 一、模型的定义二、数据迁移三、数据表关系四、数据表操作4.1 Shell工具4.2 数据新增4.3 数据修改4.4 数据删除4.5 数据查询 Django 对各种数据库提供了很好的支持包括 PostgreSQL、MySQL、SQLite 和 Oracle而且为这些数据库提供了统一的 API 方法这些 API 统称为 ORM 框架。通过使用 Django 内置的 ORM 框架可以实现数据库连接和读写操作。本文以 MySQL 数据库为例分别讲述 Django 的模型定义与数据迁移、数据表关系、数据表操作和多数据库的连接与使用。 关于数据库的基础知识可以参考专栏https://blog.csdn.net/xw1680/category_12277246.html?spm1001.2014.3001.5482
一、模型的定义
ORM 框架是一种程序技术用于实现面向对象编程语言中不同类型系统的数据之间的转换。从效果上说它创建了一个可在编程语言中使用的 虚拟对象数据库 通过对虚拟对象数据库的操作从而实现对目标数据库的操作虚拟对象数据库与目标数据库是相互对应的。在 Django 中虚拟对象数据库也称为模型通过模型实现对目标数据库的读写操作实现方法如下
配置目标数据库在 settings.py 中设置配置属性配置步骤可参考 Django 2024全栈开发指南二Django项目配置详解 一文的 四、数据库配置 小节。构建虚拟对象数据库在 App 的 models.py 文件中以类的形式定义模型。通过模型在目标数据库中创建相应的数据表。在其他模块如视图函数里使用模型来实现目标数据库的读写操作。
在项目的配置文件 settings.py 里设置数据库配置信息。以 Django5Study 项目为例其配置信息如下
DATABASES {# 默认数据库default: {ENGINE: django.db.backends.mysql,OPTIONS: {read_default_file: str(BASE_DIR / my.cnf)},},
}我们通过终端登录 mysql查看 django5study 数据库数据库中只有内置的迁移文件生成的数据表如下图所示 我们想要的数据表可以通过模型创建因为 Django 对模型和目标数据库之间有自身的映射规则如果自己在数据库中创建数据表就可能不符合 Django 的建表规则从而导致模型和目标数据库无法建立有效的通信联系。大概了解项目的环境后在 Django5Study 项目中新建子应用 chapter03_Model命令如下
(django5_study) D:\Code\dream\PythonStudy\Django5Studypython manage.py startapp chapter03_Model接下来我们在子应用 chapter03_Model 目录下的 models.py 文件中定义图书模型字段名称包括书名、发布日期、阅读量、评论量、与是否下架示例代码如下
from django.db import models# Create your models here.
# 准备书籍列表信息的模型类
# chapter03_Model的models.py
class BookInfo(models.Model):# 创建字段字段类型...verbose_name主要是在admin站点中使用book_name models.CharField(max_length20, verbose_name名称)pub_date models.DateField(verbose_name发布日期, nullTrue)read_count models.IntegerField(default0, verbose_name阅读量)comment_count models.IntegerField(default0, verbose_name评论量)is_delete models.BooleanField(defaultFalse, verbose_name逻辑删除)class Meta:db_table book_info # 指明数据库表名verbose_name 图书 # 在admin站点中显示的名称def __str__(self):定义每个数据对象的显示信息return self.book_name模型 BookInfo 定义了5个字段分别代表字符类型、日期类型、整型、整型和布尔类型。但在实际开发中我们需要定义不同的字段类型来满足各种开发需求因此Django划分了多种字段类型在源码目录 django\db\models\fields 的 __init__.py 和 files.py 文件里找到各种模型字段如下图所示 说明如下
# AutoField: 自增长类型数据表的字段类型为整数长度为11位
# BigAutoField: 自增长类型数据表的字段类型为bigint长度为20位
# CharField: 字符类型
# BooleanField: 布尔类型
# CommaSeparatedIntegerField: 用逗号分隔的整数类型
# DateField: 日期Date类型
# DateTimeField: 日期时间Datetime类型
# Decimal: 十进制小数类型
# EmailField: 字符类型存储邮箱格式的字符串
# FloatField: 浮点数类型数据表的字段类型变成Double类型
# IntegerField: 整数类型数据表的字段类型为11位的整数
# BigIntegerField: 长整数类型
# IPAddressField: 字符类型存储Ipv4地址的字符串
# GenericIPAddressField: 字符类型存储Ipv4和Ipv6地址的字符串
# NullBooleanField: 允许为空的布尔类型
# PositiveIntegerFiel: 正整数的整数类型
# PositiveSmallIntegerField: 小正整数类型取值范围为032767
# SlugField: 字符类型包含字母、数字、下画线和连字符的字符串
# SmallIntegerField: 小整数类型取值范围为-32,76832,767
# TextField: 长文本类型
# TimeField: 时间类型显示时分秒HH:MM[:ss[.uuuuuu]]
# URLField: 字符类型存储路由格式的字符串
# BinaryField: 二进制数据类型
# FileField: 字符类型存储文件路径的字符串
# ImageField: 字符类型存储图片路径的字符串
# FilePathField: 字符类型从特定的文件目录选择某个文件每个模型字段都允许设置参数这些参数来自父类 Field我们在源码里查看 Field 的定义过程 django\db\models\fields\__init__.py 如下图所示 对模型字段的参数进行分析
# verbose_name: 默认为None在Admin站点管理设置字段的显示名称
# primary_key: 默认为False若为True则将字段设置成主键
# max_length: 默认为None设置字段的最大长度
# unique: 默认为False若为True则设置字段的唯一属性
# blank: 默认为False若为True则字段允许为空值数据库将存储空字符串
# null: 默认为False若为True则字段允许为空值数据库表现为NULL
# db_index: 默认为False若为True则以此字段来创建数据库索引
# default: 默认为NOT_PROVIDED对象设置字段的默认值
# editable: 默认为True允许字段可编辑用于设置Admin的新增数据的字段
# serialize: 默认为True允许字段序列化可将数据转化为JSON格式
# unique_for_date: 默认为None设置日期字段的唯一性
# unique_for_month: 默认为None设置日期字段月份的唯一性
# unique_for_year: 默认为None设置日期字段年份的唯一性
# choices: 默认为空列表设置字段的可选值
# help_text: 默认为空字符串用于设置表单的提示信息
# db_column: 默认为None设置数据表的列名称若不设置则将字段名作为数据表的列名
# db_tablespace: 默认为None如果字段已创建索引那么数据库的表空间名称将作为该字段的索引名注意: 部分数据库不支持表空间
# auto_created: 默认为False若为True则自动创建字段用于一对一的关系模型
# validators: 默认为空列表设置字段内容的验证函数
# error_messages: 默认为None设置错误提示上述参数适用于所有模型字段但不同类型的字段会有些特殊参数每个字段的特殊参数可以在字段的初始化方法 __init__ 里找到比如字段 DateField 和 TimeField 的特殊参数 auto_now_add 和 auto_now字段 FileField 和 ImageField 的特殊参数 upload_to。 在定义模型时一般情况下都会重写函数 __str__ 这是设置模型的返回值默认情况下返回值为模型名主键。函数 __str__ 可用于外键查询比如模型A设有外键字段F外键字段F关联模型B当查询模型A时外键字段F会将模型B的函数 __str__ 返回值作为字段内容。需要注意的是函数 __str__ 只允许返回字符类型的字段如果字段是整型或日期类型的就必须使用 Python 的 str() 函数将其转化成字符类型。模型除了定义模型字段和重写函数 __str__ 之外还有 Meta 选项这三者是定义模型的基本要素。Meta 选项里设有19个属性每个属性的说明如下
# abstract: 若设为True则该模型为抽象模型不会在数据库里创建数据表
# app_label: 属性值为字符串将模型设置为指定的项目应用比如将index的models.py定义的模型A指定到其他App里
# db_table: 属性值为字符串设置模型所对应的数据表名称
# db_teblespace: 属性值为字符串设置模型所使用数据库的表空间
# get_latest_by: 属性值为字符串或列表设置模型数据的排序方式
# managed: 默认值为True支持Django命令执行数据迁移若为False则不支持数据迁移功能# order_with_respect_to: 属性值为字符串用于多对多的模型关系指向某个关联模型的名称
# 并且模型名称必须为英文小写比如模型A和模型B模型A的一条数据对应模型B的多条数据两个模型关联后
# 当查询模型A的某条数据时可使用get_b_order()和set_b_order()来获取模型B的关联数据
# 这两个方法名称的b为模型名称小写此外get_next_in_order()和get_previous_in_order()可以
# 获取当前数据的下一条和上一条的数据对象# ordering: 属性值为列表将模型数据以某个字段进行排序
# permissions: 属性值为元组设置模型的访问权限默认设置添加、删除和修改的权限
# proxy: 若设为True则为模型创建代理模型即克隆一个与模型A相同的模型B
# required_db_features: 属性值为列表声明模型依赖的数据库功能比如[gis_enabled]表示模型依赖GIS功能
# required_db_vendor: 属性值为列表声明模型支持的数据库默认支持SQLite、PostgreSQLMySQL和Oracle
# select_on_save: 数据新增修改算法通常无须设置此属性默认值为False
# indexes: 属性值为列表定义数据表的索引列表
# unique_together: 属性值为元组多个字段的联合唯一等于数据库的联合约束
# verbose_name: 属性值为字符串设置模型直观可读的名称并以复数形式表示
# verbose_name_plural: 与verbose_name相同以单数形式表示
# label: 只读属性属性值为app_label.object_name如index的模型PersonInfo值为index.PersonInfo
# label_lower: 与label相同但其值为字母小写如index.personinfo综上所述模型字段、函数 __str__ 和 Meta 选项是模型定义的基本要素模型字段的类型、函数 __str__ 和 Meta 选项的属性设置需由开发需求而定。在定义模型时还可以在模型里定义相关函数如 get_absolute_url()当视图类没有设置属性 success_url 时视图类的重定向路由地址将由模型定义的 get_absolute_url() 提供。除此之外Django 支持开发者自定义模型字段从源码文件得知所有模型字段继承 Field 类只要将自定义模型字段继承 Field 类并重写父类某些属性或方法即可完成自定义过程具体的自定义过程不再详细讲述可以参考内置模型字段的定义过程。
二、数据迁移
数据迁移是将项目里定义的模型生成相应的数据表本小节将会深入讲述数据迁移的操作包括数据表的创建和更新。以上一小节的 chapter03_Model 子应用为例项目所配置的数据库中只有内置的迁移文件生成的数据表我们想要通过模型创建数据表 BookInfo可使用 Django 的操作指令完成创建过程。
中间有个小插曲执行迁移命令一直不成功提示No changes detected我还以为数据库连接等出现了问题结果发现是在上一小节中创建的 chapter03_Model 子应用没有在 settings.py 文件中注册要仔细呀注册如下
# Application definition
# 子应用列表
INSTALLED_APPS [django.contrib.admin,django.contrib.auth,django.contrib.contenttypes,django.contrib.sessions,django.contrib.messages,django.contrib.staticfiles,# TODO 1.添加注册子应用 chapter01_HelloDjango5chapter01_HelloDjango5,# TODO 2024-11-14.添加注册子应用 chapter02_DjangoSettingschapter02_DjangoSettings,# TODO 2024-11-14 注册关于模型学习的子应用chapter03_Model,
]注册完成之后在终端下输入 Django 的操作指令
(django5_study) D:\Code\dream\PythonStudy\Django5Studypython manage.py makemigrations
Migrations for chapter03_Model:chapter03_Model\migrations\0001_initial.py Create model BookInfo(django5_study) D:\Code\dream\PythonStudy\Django5Study当 makemigrations 指令执行成功后在项目子应用 chapter03_Model 的 migrations 文件夹里创建 0001_initial.py 文件如果项目里有多个子应用并且每个子应用 的 models.py 文件里定义了模型对象当首次执行 makemigrations 指令时Django 就在每个子应用的 migrations 文件夹里创建 0001_initial.py 文件。打开查看 0001_initial.py 文件文件内容如下图所示 0001_initial.py 文件将 models.py 定义的模型生成数据表的脚本代码该文件的脚本代码可被 migrate 指令执行migrate 指令会根据脚本代码的内容在数据库里创建相应的数据表在终端下输入 migrate 指令即可完成数据表的创建代码如下
(django5_study) D:\Code\dream\PythonStudy\Django5Studypython manage.py migrate
Operations to perform:Apply all migrations: admin, auth, chapter03_Model, contenttypes, sessions
Running migrations:Applying chapter03_Model.0001_initial... OK(django5_study) D:\Code\dream\PythonStudy\Django5Study指令运行完成后打开数据库就能看到新建的数据表其中数据表 book_info 由项目应用 chapter03_Model 定义的模型 BookInfo 创建而其他数据表是 Django 内置的功能所使用的数据表分别是会话 Session、用户认证管理和 Admin 后台系统等。 在开发过程中开发者因为开发需求而经常调整数据表的结构比如新增功能、优化现有功能等。假如在上述例子里新增模型 PeopleInfo 及其数据表为了保证不影响现有的数据表如何通过新增的模型创建相应的数据表针对上述问题我们只需再次执行 makemigrations 和 migrate 指令即可比如在 chapter03_Model 的 models.py 里定义模型 PeopleInfo代码如下
# 准备人物列表信息的模型类
class PeopleInfo(models.Model):GENDER_CHOICES ((0, male),(1, female))name models.CharField(max_length20, verbose_name名称)gender models.SmallIntegerField(choicesGENDER_CHOICES, default0, verbose_name性别)description models.CharField(max_length200, nullTrue, verbose_name描述信息)book models.ForeignKey(BookInfo, on_deletemodels.CASCADE, verbose_name图书) # 外键is_delete models.BooleanField(defaultFalse, verbose_name逻辑删除)class Meta:db_table people_infoverbose_name 人物信息def __str__(self):return self.name在终端下输入并运行 makemigrations 指令Django 会在 chapter03_Model 的 migrations 文件夹里创建 0002_peopleinfo.py 文件然后输入并运行 migrate 指令即可完成数据表 people_info 的创建。
(django5_study) D:\Code\dream\PythonStudy\Django5Studypython manage.py makemigrations
Migrations for chapter03_Model:chapter03_Model\migrations\0002_peopleinfo.py Create model PeopleInfo(django5_study) D:\Code\dream\PythonStudy\Django5Studypython manage.py migrate
Operations to perform:Apply all migrations: admin, auth, chapter03_Model, contenttypes, sessions
Running migrations:Applying chapter03_Model.0002_peopleinfo... OKmakemigrations 和 migrate 指令还支持模型的修改从而修改相应的数据表结构比如在模型 PeopleInfo 里新增字段 age代码如下
# 准备人物列表信息的模型类
class PeopleInfo(models.Model):GENDER_CHOICES ((0, male),(1, female))name models.CharField(max_length20, verbose_name名称)# 增加字段年龄age models.PositiveSmallIntegerField(verbose_name年龄, default0)gender models.SmallIntegerField(choicesGENDER_CHOICES, default0, verbose_name性别)description models.CharField(max_length200, nullTrue, verbose_name描述信息)book models.ForeignKey(BookInfo, on_deletemodels.CASCADE, verbose_name图书) # 外键is_delete models.BooleanField(defaultFalse, verbose_name逻辑删除)class Meta:db_table people_infoverbose_name 人物信息def __str__(self):return self.name新增模型字段必须将属性 null 和 blank 设为 True 或者为模型字段设置默认值设置属性 default否则执行 makemigrations 指令会提示字段修复信息。当 makemigrations 指令执行完成后在 index 的 migrations 文件夹创建相应的 .py 文件只要再次执行 migrate 指令即可完成数据表结构的修改。 每次执行 migrate 指令时Django 都能精准运行 migrations 文件夹尚未被执行的 .py 文件它不会对同一个 .py 文件重复执行因为每次执行时Django 会将该文件的执行记录保存在数据表 django_migrations 中数据表的数据信息如下图所示 如果要重复执行 migrations 文件夹的某个 .py 文件就只需在数据表里删除相应的文件执行记录。一般情况下不建议采用这种操作因为这样很容易出现异常比如数据表已存在的情况下再次执行相应的 .py 文件会提示 table xxx already exists 异常。migrate 指令还可以单独执行某个 .py 文件首次在项目中使用 migrate 指令时Django 会默认创建内置功能的数据表如果只想执行 chapter03_Model 的 migrations 文件夹的某个 .py 文件那么可以在 migrate 指令里指定文件名代码如下
python manage.py migrate chapter03_Model 0001_initial在 migrate 指令末端设置项目应用名称 chapter03_Model 和 migrations 文件夹的 0001_initial 文件名三者migrate 指令、项目应用名称 chapter03_Model 和 0001_initial 文件名之间使用空格隔开即可指令执行完成后数据库只有数据表 django_migrations 和 people_info。我们知道migrate 指令根据 migrations 文件夹的 .py 文件创建数据表但在数据库里数据表的创建和修改离不开 SQL 语句的支持因此 Django 提供了 sqlmigrate 指令该指令能将 .py 文件转化成相应的 SQL 语句。以 chapter03_Model 的 0001_initial.py 文件为例在终端输入 sqlmigrate 指令指令末端必须设置项目应用名称和 migrations 文件夹的某个 .py 文件名三者之间使用空格隔开即可指令输出结果如下图所示 除此之外Django 还提供了很多数据迁移指令如 squashmigrations、inspectdb、showmigrations、sqlflush、sqlsequencereset 和 remove_stale_contenttypes这些指令在 Django 2024全栈开发指南一框架简介、环境搭建与项目结构 一文中的 2.5.1 Django的操作指令 小节里已说明过了此处不再重复讲述。当我们在操作数据迁移时Django 会对整个项目的代码进行检测它首先执行 check 指令只要项目里某个功能文件存在异常Django 就会终止数据迁移操作。也就是说在执行数据迁移之前可以使用 check 指令检测整个项目项目检测成功后再执行数据迁移操作如下图所示
三、数据表关系
一个模型对应数据库的一张数据表但是每张数据表之间是可以存在外键关联的表与表之间有3种关联一对一、一对多和多对多。一对一关系存在于两张数据表中第一张表的某一行数据只与第二张表的某一行数据相关同时第二张表的某一行数据也只与第一张表的某一行数据相关这种表关系被称为一对一关系以下面两张表为例进行说明 上面两张表中的字段 ID 分别是一一对应的并且不会在同一表中有重复 ID使用这种外键关联通常是一张数据表设有太多字段将常用的字段抽取出来并组成一张新的数据表。在模型中可以通过 OneToOneField 来构建数据表的一对一关系代码如下
class Performer(models.Model):id models.IntegerField(primary_keyTrue)name models.CharField(max_length20)nationality models.CharField(max_length20)masterpiece models.CharField(max_length50)class PerformerInfo(models.Model):id models.IntegerField(primary_keyTrue)performer models.OneToOneField(Performer, on_deletemodels.CASCADE)birth models.CharField(max_length20)elapse models.CharField(max_length20)对上述模型执行数据迁移在数据库中分别创建数据表 chapter03_model_performer 和 chapter03_model_performerinfo打开 Navicat Premium 查看两张数据表的表关系如下图所示 一对多关系存在于两张或两张以上的数据表中第一张表的某一行数据可以与第二张表的一到多行数据进行关联但是第二张表的每一行数据只能与第一张表的某一行进行关联以下面两张表为例进行说明。 第一张表的字段 ID 是唯一的但是第二张表字段 ID 允许重复字段 ID 相同的数据对应第一张表某一行数据这种表关系在日常开发中最为常见。在模型中可以通过 ForeignKey 来构建数据表的一对多关系代码如下
class Performer(models.Model):id models.IntegerField(primary_keyTrue)name models.CharField(max_length20)nationality models.CharField(max_length20)class Program(models.Model):id models.IntegerField(primary_keyTrue)performer models.ForeignKey(Performer, on_deletemodels.CASCADE)name models.CharField(max_length20)对上述模型执行数据迁移在数据库中分别创建数据表 Performer 和 Program然后打开 Navicat Premium 查看两张数据表的表关系如下图所示 多对多关系存在于两张或两张以上的数据表中第一张表的某一行数据可以与第二张表的一到多行数据进行关联同时第二张表中的某一行数据也可以与第一张表的一到多行数据进行关联以下面的表为例进行说明
从3张数据表中可以发现一个演员可以参加多个节目而一个节目也可以由多个演员来共同演出。每张表的字段 ID 都是唯一的。从最后一张表中可以发现节目 ID 和演员 ID 出现了重复的数据分别对应表2和表1的字段 ID多对多关系需要使用新的数据表来管理两张表的数据关系。在模型中可以通过 ManyToManyField 来构建数据表的多对多关系代码如下
class Performer(models.Model):id models.IntegerField(primary_keyTrue)name models.CharField(max_length20)nationality models.CharField(max_length20)class Program(models.Model):id models.IntegerField(primary_keyTrue)name models.CharField(max_length20)performer models.ManyToManyField(Performer)数据表之间创建多对多关系时只需在项目里定义两个模型对象即可在执行数据迁移时Django 自动生成3张数据表来建立多对多关系如下图所示 综上所述模型之间的关联是由 OneToOneField、ForeignKey 和 ManyToManyField 外键字段实现的每个字段设有特殊的参数参数说明如下
to必选参数关联的模型名称。on_delete必选参数设置数据的删除模式删除模型包括CASCADE、PROTECT、SET_NULL、SET_DEFAULT、SET和DO_NOTHING。 CASCADE 级联删除主表数据时连通一起删除外键表中数据PROTECT 保护通过抛出 ProtectedError 异常来阻止删除主表中被外键应用的数据SET_NULL 设置为 NULL仅在该字段 nullTrue 允许为 null 时可用SET_DEFAULT 设置为默认值仅在该字段设置了默认值时可用SET() 设置为特定值或者调用特定方法DO_NOTHING 不做任何操作如果数据库前置指明级联性此选项会抛出 IntegrityError 异常 limit_choices_to设置外键的下拉框选项用于模型表单和 Admin 后台系统。related_name用于模型之间的关联查询如反向查询。related_query_name设置模型的查询名称用于 filter 或 get 查询若设置参数 related_name则以该参数为默认值若没有设置则以模型名称的小写为默认值。to_field设置外键与其他模型字段的关联性默认关联主键若要关联其他字段则该字段必须具有唯一性。db_constraint在数据库里是否创建外键约束默认值为 True。swappable设置关联模型的替换功能默认值为 True比如模型A关联模型B想让模型C继承并替换模型B使得模型A与模型C之间关联。symmetrical仅限于 ManyToManyField设置多对多字段之间的对称模式。through仅限于 ManyToManyField设置自定义模型C用于管理和创建模型A和B的多对多关系。through_fields仅限于 ManyToManyField设置模型C的字段确认模型C的哪些字段用于管理模型A和B的多对多关系。db_table仅限于 ManyToManyField为管理和存储多对多关系的数据表设置表名称。
四、数据表操作
本小节讲述如何使用 ORM 框架实现数据新增、修改、删除、查询、执行 SQL 语句和实现数据库事务等操作具体说明如下
数据新增由模型实例化对象调用内置方法实现数据新增比如单数据新增调用 create查询与新增调用 get_or_create修改与新增调用 update_or_create批量新增调用 bulk_create。数据修改必须执行一次数据查询再对查询结果进行修改操作常用方法有模型实例化、update 方法和批量更新 bulk_update。数据删除必须执行一次数据查询再对查询结果进行删除操作若删除的数据设有外键字段则删除结果由外键的删除模式决定。数据查询分为单表查询和多表查询Django 提供多种不同查询的 API 方法以满足开发需求。执行 SQL 语句有3种方法实现extra、raw 和 execute其中 extra 和 raw 只能实现数据查询具有一定的局限性而 execute 无须经过 ORM 框架处理能够执行所有 SQL 语句但很容易受到 SQL 注入攻击。数据库事务是指作为单个逻辑执行的一系列操作这些操作具有原子性即这些操作要么完全执行要么完全不执行常用于银行转账和火车票抢购等。
4.1 Shell工具
Django 的 manage 工具提供了 shell 命令帮助我们配置好当前工程的运行环境如连接好数据库等以便可以直接在终端中执行测试 python 语句通过如下命令进入 shell
(django5_study) D:\Code\dream\PythonStudy\Django5Studypython manage.py shell
Python 3.12.7 | packaged by Anaconda, Inc. | (main, Oct 4 2024, 13:17:27) [MSC v.1929 64 bit (AMD64)]
Type copyright, credits or license for more information
IPython 8.29.0 -- An enhanced Interactive Python. Type ? for help.In [1]: # 导入两个模型类以便后续使用In [2]: from chapter03_Model.models import BookInfo, PeopleInfo这样可以更好地演示数据库的增、删、改操作该模式非常方便开发人员开发和调试程序。
4.2 数据新增
Django 对数据库的数据进行增、删、改操作是借助内置 ORM 框架所提供的 API 方法实现的简单来说ORM 框架的数据操作 API 是在 QuerySet 类里面定义的然后由开发者自定义的模型对象调用 QuerySet 类从而实现数据操作。
先在 Navicat 中添加测试数据
insert into book_info(book_name, pub_date, read_count,comment_count, is_delete) values
(射雕英雄传, 1980-5-1, 12, 34, 0),
(天龙八部, 1986-7-24, 36, 40, 0),
(笑傲江湖, 1995-12-24, 20, 80, 0),
(雪山飞狐, 1987-11-11, 58, 24, 0);select * from book_info;insert into people_info(name, gender, book_id, description, is_delete,age) values(郭靖, 1, 1, 降龙十八掌, 0,25),(黄蓉, 0, 1, 打狗棍法, 0,23),(黄药师, 1, 1, 弹指神通, 0,45),(欧阳锋, 1, 1, 蛤蟆功, 0,47),(梅超风, 0, 1, 九阴白骨爪, 0,33),(乔峰, 1, 2, 降龙十八掌, 0,37),(段誉, 1, 2, 六脉神剑, 0,28),(虚竹, 1, 2, 天山六阳掌, 0,32),(王语嫣, 0, 2, 神仙姐姐, 0,25),(令狐冲, 1, 3, 独孤九剑, 0,19),(任盈盈, 0, 3, 弹琴, 0,18),(岳不群, 1, 3, 华山剑法, 0,30),(东方不败, 0, 3, 葵花宝典, 0,33),(胡斐, 1, 4, 胡家刀法, 0,28),(苗若兰, 0, 4, 黄衣, 0,26),(程灵素, 0, 4, 医术, 0,20),(袁紫衣, 0, 4, 六合拳, 0,20);在 Shell 模式下若想对数据表 people_info 插入数据则可输入以下代码实现
In [11]: p1 PeopleInfo()
In [12]: p1.name洪七公
In [13]: p1.gender1
In [14]: p1.book_id1
In [15]: p1.description降龙十八掌
In [16]: p1.is_deleteFalse
In [17]: p1.age48
In [18]: p1.save()上述代码是对模型 PeopleInfo 进行实例化再对实例化对象的属性进行赋值从而实现数据表 people_info 的数据插入代码说明如下
从项目子应用 chapter03_Model 的 models.py 文件中导入模型 PeopleInfo对模型 PeopleInfo 声明并实例化生成对象 p1对对象 p1 的属性进行逐一赋值对象 p1 的属性来自于模型 PeopleInfo 所定义的字段。完成赋值后再由对象 p1 调用 save 方法进行数据保存
需要注意的是模型 PeopleInfo 的外键命名为 book但在数据表 people_info 中变为 book_id因此对象 p1 设置外键字段 book 的时候外键字段应以数据表的字段名为准。上述代码运行结束后在数据表 people_info 里查看数据的插入情况如下图所示 除了上述方法外数据插入还有以下3种常见方法代码如下
In [19]: from chapter03_Model.models import BookInfo, PeopleInfo
In [20]: # 方法一: 使用create方法实现数据插入
In [21]: p BookInfo.objects.create(book_name红楼梦,pub_date1978-03-05,
read_count50,comment_count70,is_delete0)
In [22]: # 数据新增后获取新增数据的主键id
In [23]: p.id
Out[23]: 5
In [24]: # 方法二: 同样使用create方法但数据以字典格式表示
In [25]: d dict(book_name西游记,
pub_date1988-05-03,read_count150,comment_count90,is_delete0)
In [26]: p BookInfo.objects.create(**d)
In [27]: # 数据新增后获取新增数据的主键id
In [28]: p.id
Out[28]: 6
In [29]: # 方法三: 在实例化时直接设置属性值
In [30]: p BookInfo(book_name水浒传,pub_date1988-10-23,read_count45,comment_count38,is_delete0)
In [31]: p.save()
In [32]: # 数据新增后获取新增数据的主键id
In [33]: p.id
Out[33]: 7执行数据插入时为了保证数据的有效性我们需要对数据进行去重判断确保数据不会重复插入。以往的方案都是对数据表进行查询操作如果查询的数据不存在就执行数据插入操作。为了简化这一过程Django 提供了 get_or_create 方法使用如下
In [34]: d dict(book_name西游记,pub_date1988-05-03,read_count150,comment_count90,is_delete0)In [35]: p BookInfo.objects.get_or_create(**d)In [39]: p
Out[39]: (BookInfo: 西游记, False)In [40]: d dict(book_name西游记1,pub_date1988-05-03,read_count150,comment_count90,is_delete0)In [41]: p BookInfo.objects.get_or_create(**d)In [42]: p
Out[42]: (BookInfo: 西游记1, True)get_or_create 根据每个模型字段的值与数据表的数据进行判断判断方式如下
只要有一个模型字段的值与数据表的数据不相同除主键之外就会执行数据插入操作。如果每个模型字段的值与数据表的某行数据完全相同就不执行数据插入而是返回这行数据的数据对象。若执行结果显示为 False则数据表已存在数据不再执行数据插入若执行结果显示为 True则代表数据插入。
除了 get_or_create 之外Django 还定义了 update_or_create 方法这是判断当前数据在数据表里是否存在若存在则进行更新操作否则在数据表里新增数据使用说明如下
In [43]: # 第一次是新增数据In [44]: d dict(book_name三国演义,pub_date1985-11-15,read_count800,comment_count520,is_delete0)In [45]: p BookInfo.objects.update_or_create(**d)In [46]: p
Out[46]: (BookInfo: 三国演义, True)In [47]: # 第二次是修改数据In [48]: p BookInfo.objects.update_or_create(**d, defaults{book_name: 三国演义2})In [50]: p[0].book_name
Out[50]: 三国演义2update_or_create 是根据字典 d 的内容查找数据表的数据如果能找到相匹配的数据就执行数据修改修改内容以字典格式传递给参数 defaults 即可如果在数据表找不到匹配的数据就将字典 d 的数据插入数据表里。如果要对某个模型执行数据批量插入操作那么可以使用 bulk_create 方法实现只需将数据对象以列表或元组的形式传入 bulk_create 方法即可
In [55]: b1 BookInfo(book_name遮天,pub_date2008-10-23,read_count40,comment_count30,is_delete0)In [56]: b2 BookInfo(book_name遮天2,pub_date2012-01-10,read_count68,comment_count88,is_delete0)In [57]: book_list [b1, b2]In [58]: BookInfo.objects.bulk_create(book_list)
Out[58]: [BookInfo: 遮天, BookInfo: 遮天2]在使用 bulk_create 之前数据类型为模型 BookInfo 的实例化对象并且在实例化过程中设置每个字段的值最后将所有实例化对象放置在列表或元组里以参数的形式传递给 bulk_create从而实现数据的批量插入操作。
4.3 数据修改
数据修改的步骤与数据插入的步骤大致相同唯一的区别在于数据对象来自数据表因此需要执行一次数据查询查询结果以对象的形式表示并将对象的属性进行赋值处理代码如下
In [59]: b2 BookInfo.objects.get(id11)In [60]: b2
Out[60]: BookInfo: 遮天2In [61]: b2.read_count
Out[61]: 68In [62]: b2.read_count100In [63]: b2.save()上述代码获取数据表 people_info 里主键 id 等于 11 的数据对象 b2然后修改数据对象 b2 的 read_count 属性从而完成数据修改操作。打开数据表 people_info 查看数据修改情况如下图所示 除此之外还可以使用 update 方法实现数据修改使用方法如下
In [64]: # 批量更新一条或多条数据查询方法使用filter
In [65]: # filter以列表格式返回查询结果可能是一条或多条数据
In [66]: BookInfo.objects.filter(id11).update(book_name遮天2-1)
Out[66]: 1In [67]: # 更新数据以字典格式表示
In [68]: d dict(book_name遮天2-2)
In [69]: BookInfo.objects.filter(book_name遮天2-1).update(**d)
Out[69]: 1In [70]: # 不使用查询方法默认对全表的数据进行更新
In [71]: BookInfo.objects.update(read_count666)
Out[71]: 11In [72]: # 使用内置F方法实现数据的自增或自减
In [73]: # F方法还可以在annotate或filter方法里使用In [75]: from django.db.models import FIn [76]: b1 BookInfo.objects.filter(id11)In [77]: # 将read_count字段原有的数据自增加一In [78]: b1.update(read_countF(read_count)1)
Out[78]: 1In [80]: print(b1)
QuerySet [BookInfo: 遮天2-2]在 Django 2.2 或以上版本新增了数据批量更新方法 bulk_update它的使用与批量新增方法 bulk_create 相似使用说明如下
# 新增两行数据
b1 BookInfo.objects.create(book_namexx1, pub_date1978-03-05,read_count5, comment_count7, is_delete0)
b2 BookInfo.objects.create(book_namexx2, pub_date1977-04-05,read_count3, comment_count6, is_delete0)# 修改字段read_count和book_name的数据
b1.read_count6
b2.book_namexx3In [87]: # 批量修改字段read_count和book_name的数据
In [88]: BookInfo.objects.bulk_update([b1,b2], fields[read_count,book_name])
Out[88]: 24.4 数据删除
数据删除有3种方式删除数据表的全部数据、删除一行数据和删除多行数据实现方式如下
In [91]: # 删除一条id为13的数据In [92]: BookInfo.objects.get(id13).delete()
Out[92]: (1, {chapter03_Model.BookInfo: 1})In [93]: # 删除多条数据In [94]: BookInfo.objects.filter(read_count70).delete()
Out[94]: (0, {})In [95]: BookInfo.objects.filter(comment_count70).delete()
Out[95]: (2, {chapter03_Model.BookInfo: 2})In [96]: # 删除数据表中的全部数据In [97]: BookInfo.objects.all().delete()
Out[97]: (21, {chapter03_Model.PeopleInfo: 12, chapter03_Model.BookInfo: 9})删除数据的过程中如果删除的数据设有外键字段就会同时删除外键关联的数据因为我们建立模型的时候使用的是 比如删除数据表 book_info 里主键等于 2 的数据简称为数据A在数据表 people_info 里有些数据简称为数据B关联了数据A那么在删除数据A时也会同时删除数据B。
4.5 数据查询
首先将测试数据恢复至原样如下图所示 以数据表 book_info 和 people_info 为例在 Django5Study 项目的 Shell 模式下使用 ORM 框架提供的 API 方法实现数据查询代码如下
In [1]: from chapter03_Model.models import *
# 全表查询
# SQL: Select * from book_info数据以列表返回
In [2]: b1 BookInfo.objects.all()
# 查询第一条数据序列从0开始
In [3]: b1[0]
Out[3]: BookInfo: 射雕英雄传
# 查询前3条数据
# SQL: Select * from book_info LIMIT 3
# SQL语句的LIMIT方法在Django中使用列表截取即可
In [4]: b_list BookInfo.objects.all()[:3]
In [5]: print(b_list)
QuerySet [BookInfo: 射雕英雄传, BookInfo: 天龙八部, BookInfo: 笑傲江湖]# 查询某个字段
# SQL: Select book_name from book_info
# values方法数据以列表返回列表元素以字典表示
In [6]: b BookInfo.objects.values(book_name)
In [7]: print(b)
In [8]: b[1].get(book_name)
Out[8]: 天龙八部
# values_list方法数据以列表返回列表元素以元组表示
In [9]: BookInfo.objects.values_list(read_count)[:2]
Out[9]: QuerySet [(12,), (36,)]
# 使用get方法查询数据
# SQL: select * from book_info where id2
In [10]: b1 BookInfo.objects.get(id1)
In [11]: b1.book_name
Out[11]: 射雕英雄传
# 使用filter方法查询数据注意区分get和filter的差异
In [12]: b2 BookInfo.objects.filter(id2)
In [13]: print(b2)
QuerySet [BookInfo: 天龙八部]
In [14]: print(b1)
射雕英雄传
In [15]: b2[0].book_name
Out[15]: 天龙八部
# SQL的and查询主要在filter里面添加多个查询条件
In [17]: b BookInfo.objects.filter(book_name射雕英雄传, read_count12)
In [18]: b
Out[18]: QuerySet [BookInfo: 射雕英雄传]
# filter的查询条件可设为字典格式
In [19]: d dict(book_name天龙八部, read_count36)
In [20]: b BookInfo.objects.filter(**d)
In [21]: b
Out[21]: QuerySet [BookInfo: 天龙八部]# SQL的or查询需要引入Q编写格式Q(fieldvalue)|Q(fieldvalue)
# 多个Q之间使用|隔开即可
# SQL: select * from book_info where id1 or book_name雪山飞狐from django.db.models import Q
In [23]: b1 BookInfo.objects.filter(Q(id1)|Q(book_name雪山飞狐))
In [24]: b1
Out[24]: QuerySet [BookInfo: 射雕英雄传, BookInfo: 雪山飞狐]# SQL的不等于查询在Q查询前面使用~即可
# SQL语句: select * from book_info where not (id3)
In [25]: b2 BookInfo.objects.filter(~Q(id3))
In [26]: b2
Out[26]: QuerySet [BookInfo: 射雕英雄传, BookInfo: 天龙八部, BookInfo: 雪山飞狐]
# 还可以使用exclude实现不等于查询
In [27]: b3 BookInfo.objects.exclude(id3)
In [28]: b3
Out[28]: QuerySet [BookInfo: 射雕英雄传, BookInfo: 天龙八部, BookInfo: 雪山飞狐]
# 使用count方法统计查询数据的数据量
In [31]: BookInfo.objects.exclude(id3).count()
Out[31]: 3
# 去重查询distinct方法无须设置参数去重方式根据values设置的字段执行
# SQL: select distinct gender from people_info;
In [32]: PeopleInfo.objects.values(gender).distinct()
Out[32]: QuerySet [{gender: 1}, {gender: 0}]
# 根据字段id降序排列降序只要在order_by里面的字段前面加-即可
# order_by可设置多字段排列如BookInfo.objects.order_by(-id, job)
In [43]: BookInfo.objects.order_by(-id).all()[:2]
Out[43]: QuerySet [BookInfo: 雪山飞狐, BookInfo: 笑傲江湖]
# 聚合查询实现对数据值求和、求平均值等。由annotate和aggregate方法实现
# annotate类似于SQL里面的GROUP BY方法
#如果不设置values默认对主键进行GROUP BY分组
# SQL: select gender,count(gender) as gender__count from people_info group by gender
In [44]: from django.db.models import Sum, Count
In [50]: b PeopleInfo.objects.values(gender).annotate(Count(gender))
In [51]: print(b.query) # 返回查询的sql语句# aggregate是计算某个字段的值并只返回计算结果
# SQL: select count(id) as id_count from PeopleInfo
In [52]: b PeopleInfo.objects.aggregate(count_idCount(id))
In [53]: b
Out[53]: {count_id: 17}# union、intersection和difference语法
# 每次查询结果的字段必须相同
# 第一次查询结果v1
In [55]: p1 PeopleInfo.objects.filter(gender1)
# 第二次查询结果v2
In [57]: p2 PeopleInfo.objects.filter(gender0)
# 使用SQL的UNION来组合两个或多个查询结果的并集
# 获取两次查询结果的并集
In [58]: p1.union(p2)
# 使用SQL的INTERSECT来获取两个或多个查询结果的交集
# 获取两次查询结果的交集
In [60]: p1 PeopleInfo.objects.filter(gender1)
In [61]: p2 PeopleInfo.objects.filter(age__lt26)
In [62]: p1.intersection(p2)
Out[62]: QuerySet [PeopleInfo: 郭靖, PeopleInfo: 令狐冲]
# 使用SQL的EXCEPT来获取两个或多个查询结果的差
# 以p1为目标数据去除p1和p2的共同数据
In [63]: p1.difference(p2)上述例子讲述了开发中常用的数据查询方法但有时需要设置不同的查询条件来满足多方面的查询要求。上述的查询条件 filter 和 get 是使用等值的方法来匹配结果。若想使用大于、不等于或模糊查询的匹配方法则可在查询条件 filter 和 get 里使用下表所示的匹配符实现
综上所述在查询数据时可以使用查询条件 get 或 filter 实现但是两者的执行过程存在一定的差异说明如下
查询条件 get查询字段必须是主键或者唯一约束的字段并且查询的数据必须存在如果查询的字段有重复值或者查询的数据不存在程序就会抛出异常信息。查询条件 filter查询字段没有限制只要该字段是数据表的某一字段即可。查询结果以列表形式返回如果查询结果为空查询的数据在数据表中找不到就返回空列表。