【Django ORM】三万字了解Django ORM的基本概念和基本使用
第一章 Django ORM 概述
1.1 什么是Django ORM
1.1.1 ORM的基本概念
ORM 即对象关系映射(Object Relational Mapping),它是一种编程技术,用于在面向对象编程语言(如 Python)和关系型数据库(如 MySQL、PostgreSQL 等)之间建立一座桥梁 🌉。
在传统的数据库操作中,我们需要编写 SQL 语句来完成数据的增删改查操作。而 ORM 则允许我们使用面向对象的方式来操作数据库,将数据库中的表映射为 Python 中的类,表中的每一行记录映射为类的一个实例对象,表中的列映射为类的属性。
例如,假设有一个用户表 users
,包含 id
、name
、age
三列。使用 ORM 后,我们可以创建一个 User
类:
class User:def __init__(self, id, name, age):self.id = idself.name = nameself.age = age
这样,我们就可以通过操作 User
类的对象来间接操作数据库中的 users
表,而不需要直接编写 SQL 语句。
1.1.2 Django ORM的特点
- 简洁易用:Django ORM 提供了非常简洁的 API,让我们可以用很少的代码完成复杂的数据库操作。例如,要查询所有年龄大于 18 岁的用户,只需要这样写:
from myapp.models import User
users = User.objects.filter(age__gt=18)
- 自动生成 SQL:我们不需要手动编写复杂的 SQL 语句,Django ORM 会根据我们的操作自动生成相应的 SQL 语句并执行。这样可以减少出错的概率,提高开发效率。
- 支持事务处理:Django ORM 支持数据库事务,确保一组数据库操作要么全部成功,要么全部失败。例如:
from django.db import transaction@transaction.atomic
def transfer_money(from_account, to_account, amount):from_account.balance -= amountfrom_account.save()to_account.balance += amountto_account.save()
- 支持模型迁移:当我们修改了模型类的定义(如添加或删除字段),Django ORM 可以自动生成数据库迁移脚本,帮助我们更新数据库结构。
1.2 Django ORM的优势
1.2.1 提高开发效率
- 减少代码量:使用 Django ORM 可以避免编写大量重复的 SQL 语句,开发人员可以专注于业务逻辑的实现。例如,在创建一个新用户时,只需要创建一个
User
类的实例并保存即可:
user = User(name='John', age=20)
user.save()
- 快速开发:Django ORM 的简洁 API 使得开发人员可以快速完成数据库操作的开发,缩短开发周期。
1.2.2 数据库无关性
Django ORM 支持多种数据库,如 MySQL、PostgreSQL、SQLite 等。我们可以在不修改业务代码的情况下,轻松地切换数据库。例如,在开发阶段可以使用 SQLite 进行快速开发和测试,在生产环境中可以切换到 MySQL 或 PostgreSQL。
只需要在 settings.py
文件中修改数据库配置:
DATABASES = {'default': {'ENGINE': 'django.db.backends.mysql','NAME': 'mydatabase','USER': 'myuser','PASSWORD': 'mypassword','HOST': 'localhost','PORT': '3306',}
}
1.2.3 安全性
- 防止 SQL 注入:Django ORM 会自动对用户输入进行转义,避免 SQL 注入攻击。例如,当我们使用
filter
方法进行查询时,用户输入的数据会被安全地处理:
username = request.GET.get('username')
users = User.objects.filter(username=username)
- 权限管理:Django 提供了完善的权限管理系统,可以对数据库操作进行细粒度的权限控制,确保只有授权的用户才能进行敏感的数据库操作。
总之,Django ORM 为我们提供了一种高效、安全、便捷的方式来操作数据库,是 Django 框架的重要组成部分 🚀。
第二章 Django ORM 环境搭建
2.1 安装Django
2.1.1 使用pip安装
pip
是 Python 的包管理工具,使用它可以方便快捷地安装 Django。在安装之前,请确保你的 Python 环境已经安装并且 pip
可以正常使用。
在命令行中输入以下命令来安装 Django:
pip install django
执行这个命令后,pip
会自动从 Python Package Index(PyPI)下载 Django 的最新版本并安装到你的 Python 环境中。安装过程中,你会看到命令行输出下载和安装的进度信息,当看到类似“Successfully installed django-xxx”的提示时,就表示 Django 已经安装成功啦😎。
2.1.2 版本选择
Django 有不同的版本,每个版本都有其特点和适用场景。在选择版本时,需要考虑以下因素:
- 稳定性:如果你是用于生产环境,建议选择长期支持(LTS)版本,这些版本会得到官方更长时间的维护和更新,稳定性更高。例如 Django 3.2 就是一个长期支持版本。
- 新特性:如果你想尝试新的功能和特性,可以选择较新的版本。不过新的版本可能存在一些潜在的问题,需要谨慎使用。
- 兼容性:要确保你选择的 Django 版本与你使用的 Python 版本以及其他依赖库兼容。
如果你想安装指定版本的 Django,可以在安装命令中指定版本号,例如:
pip install django==3.2
这样就会安装 Django 3.2 版本。
2.2 配置数据库
2.2.1 支持的数据库类型
Django 支持多种数据库类型,你可以根据项目的需求选择合适的数据库:
- SQLite:这是 Django 默认使用的数据库,它是一个轻量级的嵌入式数据库,不需要单独的服务器进程,适合开发和测试环境。SQLite 的文件存储在本地,使用起来非常方便,就像一个小型的文件数据库一样🤗。
- PostgreSQL:是一个功能强大的开源关系型数据库,支持高级的 SQL 特性和并发操作。它在性能、可靠性和扩展性方面都表现出色,常用于生产环境。
- MySQL:也是一种广泛使用的开源关系型数据库,具有高性能、稳定性和易用性等特点。很多大型网站和应用都使用 MySQL 作为数据库。
- Oracle:是一种商业数据库,具有强大的功能和高可用性,适用于大型企业级应用。
2.2.2 数据库连接配置
在 Django 项目中,数据库连接配置通常在 settings.py
文件中进行。以下是不同数据库的配置示例:
SQLite 配置
DATABASES = {'default': {'ENGINE': 'django.db.backends.sqlite3','NAME': BASE_DIR / 'db.sqlite3',}
}
这里 ENGINE
指定了使用的数据库引擎,NAME
指定了 SQLite 数据库文件的路径。
PostgreSQL 配置
DATABASES = {'default': {'ENGINE': 'django.db.backends.postgresql','NAME': 'your_database_name','USER': 'your_username','PASSWORD': 'your_password','HOST': 'your_host','PORT': 'your_port',}
}
需要将 your_database_name
、your_username
、your_password
、your_host
和 your_port
替换为你自己的数据库信息。
MySQL 配置
DATABASES = {'default': {'ENGINE': 'django.db.backends.mysql','NAME': 'your_database_name','USER': 'your_username','PASSWORD': 'your_password','HOST': 'your_host','PORT': 'your_port',}
}
同样,要将相应的信息替换为你自己的数据库信息。
2.3 创建Django项目和应用
2.3.1 创建项目命令
在命令行中,使用以下命令来创建一个新的 Django 项目:
django-admin startproject your_project_name
其中 your_project_name
是你想要创建的项目名称。执行这个命令后,Django 会在当前目录下创建一个新的项目目录,目录结构如下:
your_project_name/
├── manage.py
└── your_project_name/├── __init__.py├── settings.py├── urls.py└── wsgi.py
manage.py
:是一个命令行工具,用于与 Django 项目进行交互,例如启动开发服务器、创建数据库表等。settings.py
:包含了项目的配置信息,如数据库配置、中间件配置等。urls.py
:定义了项目的 URL 路由规则。wsgi.py
:是一个 WSGI 兼容的 Web 服务器的入口点。
2.3.2 创建应用命令
在 Django 中,一个项目可以包含多个应用,每个应用负责不同的功能。使用以下命令来创建一个新的应用:
python manage.py startapp your_app_name
其中 your_app_name
是你想要创建的应用名称。执行这个命令后,Django 会在项目目录下创建一个新的应用目录,目录结构如下:
your_app_name/
├── __init__.py
├── admin.py
├── apps.py
├── migrations/
│ └── __init__.py
├── models.py
├── tests.py
└── views.py
admin.py
:用于配置 Django 管理界面。models.py
:定义了应用的数据模型。views.py
:包含了处理用户请求的视图函数。
2.3.3 注册应用
创建好应用后,需要在项目的 settings.py
文件中注册应用,这样 Django 才能识别并使用这个应用。打开 settings.py
文件,找到 INSTALLED_APPS
列表,将你的应用名称添加到列表中:
INSTALLED_APPS = ['django.contrib.admin','django.contrib.auth','django.contrib.contenttypes','django.contrib.sessions','django.contrib.messages','django.contrib.staticfiles','your_app_name', # 添加你的应用名称
]
这样,你的应用就成功注册到项目中啦🎉。
第三章 模型定义
在软件开发中,尤其是使用 Django 等框架进行数据库操作时,模型定义是非常重要的一环,它就像是数据库表结构的蓝图,规定了数据的存储方式和关系。下面我们来详细了解模型定义的各个方面。
3.1 模型类的创建
3.1.1 模型类的基本结构
模型类就像是一个模板,它定义了数据库表中的字段和行为。在 Django 中,一个基本的模型类通常包含类名、字段定义和一些可选的方法。以下是一个简单的示例:
from django.db import modelsclass Book(models.Model):title = models.CharField(max_length=100)author = models.CharField(max_length=50)publication_date = models.DateField()def __str__(self):return self.title
from django.db import models
:导入 Django 的模型模块。class Book(models.Model)
:定义一个名为Book
的模型类,继承自models.Model
。title = models.CharField(max_length=100)
:定义一个字符型字段title
,最大长度为 100。author = models.CharField(max_length=50)
:定义一个字符型字段author
,最大长度为 50。publication_date = models.DateField()
:定义一个日期型字段publication_date
。def __str__(self)
:定义一个方法,用于返回对象的字符串表示,方便在调试和管理界面中显示。
3.1.2 继承关系
在 Django 中,模型类可以通过继承来复用代码和实现特定的功能。常见的继承方式有以下几种:
- 抽象基类继承:抽象基类是一种不能直接实例化的类,它主要用于定义一些通用的字段和方法,供其他模型类继承。示例如下:
from django.db import modelsclass BaseModel(models.Model):created_at = models.DateTimeField(auto_now_add=True)updated_at = models.DateTimeField(auto_now=True)class Meta:abstract = Trueclass Book(BaseModel):title = models.CharField(max_length=100)author = models.CharField(max_length=50)
在这个例子中,BaseModel
是一个抽象基类,它定义了 created_at
和 updated_at
字段,用于记录数据的创建和更新时间。Book
类继承自 BaseModel
,并添加了自己的字段。
- 多表继承:多表继承是指一个模型类继承自另一个具体的模型类,每个模型类对应一个数据库表。示例如下:
from django.db import modelsclass Person(models.Model):name = models.CharField(max_length=50)class Student(Person):student_id = models.CharField(max_length=20)
在这个例子中,Student
类继承自 Person
类,Person
和 Student
分别对应两个数据库表,Student
表会包含 Person
表的所有字段以及自己的 student_id
字段。
3.2 字段类型
3.2.1 常见字段类型
在 Django 中,有多种字段类型可供选择,用于存储不同类型的数据。以下是一些常见的字段类型:
-
字符型字段:
CharField
:用于存储较短的字符串,需要指定max_length
参数。例如:title = models.CharField(max_length=100)
。TextField
:用于存储较长的文本,不需要指定最大长度。例如:description = models.TextField()
。
-
数值型字段:
IntegerField
:用于存储整数。例如:age = models.IntegerField()
。FloatField
:用于存储浮点数。例如:price = models.FloatField()
。
-
日期和时间型字段:
DateField
:用于存储日期。例如:birth_date = models.DateField()
。DateTimeField
:用于存储日期和时间。例如:created_at = models.DateTimeField(auto_now_add=True)
。
-
布尔型字段:
BooleanField
:用于存储布尔值(True
或False
)。例如:is_published = models.BooleanField(default=False)
。
3.2.2 字段选项
每个字段类型都可以有一些选项,用于进一步定义字段的行为。以下是一些常见的字段选项:
null
:如果设置为True
,表示该字段在数据库中可以为空。例如:email = models.CharField(max_length=100, null=True)
。blank
:如果设置为True
,表示该字段在表单中可以为空。例如:phone = models.CharField(max_length=20, blank=True)
。default
:设置字段的默认值。例如:status = models.CharField(max_length=20, default='active')
。unique
:如果设置为True
,表示该字段的值在表中必须唯一。例如:username = models.CharField(max_length=50, unique=True)
。
3.3 模型元数据
3.3.1 Meta 类的作用
在 Django 模型类中,可以定义一个内部类 Meta
,用于提供模型的元数据。元数据是指与模型本身相关的一些信息,而不是模型的字段信息。Meta
类的主要作用包括:
- 定义模型的数据库表名。
- 定义模型的排序方式。
- 定义模型的可读名称。
3.3.2 常用元数据选项
以下是一些常用的 Meta
类选项:
db_table
:指定模型对应的数据库表名。例如:
class Book(models.Model):title = models.CharField(max_length=100)class Meta:db_table = 'my_books'
在这个例子中,Book
模型对应的数据库表名是 my_books
。
ordering
:指定模型的排序方式。例如:
class Book(models.Model):title = models.CharField(max_length=100)publication_date = models.DateField()class Meta:ordering = ['-publication_date']
在这个例子中,Book
模型的对象将按照 publication_date
字段降序排列。
verbose_name
和verbose_name_plural
:定义模型的可读名称和复数形式的可读名称。例如:
class Book(models.Model):title = models.CharField(max_length=100)class Meta:verbose_name = '图书'verbose_name_plural = '图书列表'
在这个例子中,Book
模型在管理界面中显示为“图书”,复数形式显示为“图书列表”。
3.4 模型关系
3.4.1 一对一关系
一对一关系是指一个模型的实例与另一个模型的实例之间存在一一对应的关系。在 Django 中,可以使用 OneToOneField
来定义一对一关系。示例如下:
from django.db import modelsclass Profile(models.Model):user = models.OneToOneField('auth.User', on_delete=models.CASCADE)bio = models.TextField()
在这个例子中,Profile
模型与 auth.User
模型之间存在一对一关系,即每个用户只能有一个个人资料。on_delete=models.CASCADE
表示当关联的用户被删除时,对应的个人资料也会被删除。
3.4.2 一对多关系
一对多关系是指一个模型的实例可以与另一个模型的多个实例相关联。在 Django 中,可以使用 ForeignKey
来定义一对多关系。示例如下:
from django.db import modelsclass Author(models.Model):name = models.CharField(max_length=50)class Book(models.Model):title = models.CharField(max_length=100)author = models.ForeignKey(Author, on_delete=models.CASCADE)
在这个例子中,Book
模型与 Author
模型之间存在一对多关系,即一个作者可以有多本书。on_delete=models.CASCADE
表示当关联的作者被删除时,对应的书籍也会被删除。
3.4.3 多对多关系
多对多关系是指一个模型的多个实例可以与另一个模型的多个实例相关联。在 Django 中,可以使用 ManyToManyField
来定义多对多关系。示例如下:
from django.db import modelsclass Tag(models.Model):name = models.CharField(max_length=20)class Book(models.Model):title = models.CharField(max_length=100)tags = models.ManyToManyField(Tag)
在这个例子中,Book
模型与 Tag
模型之间存在多对多关系,即一本书可以有多个标签,一个标签也可以应用于多本书。Django 会自动创建一个中间表来管理这种关系。
通过以上内容,我们详细了解了模型定义的各个方面,包括模型类的创建、字段类型、模型元数据和模型关系。这些知识对于使用 Django 等框架进行数据库操作非常重要。🎉
第四章 数据库迁移
在 Django 项目中,数据库迁移是一个非常重要的功能,它允许我们在模型发生变化时,安全地更新数据库结构,而不会丢失数据。下面我们来详细了解数据库迁移的相关内容。
4.1 迁移文件的生成
4.1.1 makemigrations 命令
在 Django 里,makemigrations
命令就像是一个“变化探测器”😎。当我们对模型(models.py
文件中的类)进行了修改,比如添加了新的字段、删除了某个字段或者修改了字段的属性,就需要使用这个命令来生成迁移文件。
- 使用方法:在命令行中,进入项目的根目录,然后输入以下命令:
python manage.py makemigrations
- 执行过程:Django 会自动检测模型的变化,然后将这些变化转换为 Python 代码,保存到一个新的迁移文件中。这些迁移文件通常存放在应用的
migrations
文件夹下。 - 指定应用:如果你的项目中有多个应用,你也可以指定只对某个应用生成迁移文件,命令如下:
python manage.py makemigrations app_name
这里的 app_name
就是你要指定的应用名称。
4.1.2 迁移文件的结构
迁移文件是一个 Python 文件,它包含了对数据库结构的修改信息。下面是一个简单的迁移文件示例:
# -*- coding: utf-8 -*-
from django.db import migrations, modelsclass Migration(migrations.Migration):initial = Truedependencies = []operations = [migrations.CreateModel(name='Book',fields=[('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),('title', models.CharField(max_length=100)),('author', models.CharField(max_length=100)),],),]
initial
:如果这个值为True
,表示这是该应用的第一个迁移文件。dependencies
:指定该迁移文件依赖的其他迁移文件。如果没有依赖,列表为空。operations
:这是迁移文件的核心部分,它包含了一系列的操作,比如创建模型、添加字段、删除字段等。在上面的示例中,migrations.CreateModel
表示创建一个名为Book
的模型。
4.2 执行迁移
4.2.2 migrate 命令
migrate
命令就像是一个“数据库更新器”🚀,它会将之前生成的迁移文件应用到数据库中,从而更新数据库的结构。
- 使用方法:在命令行中,进入项目的根目录,然后输入以下命令:
python manage.py migrate
- 执行过程:Django 会检查所有应用的迁移文件,找出那些还没有应用到数据库中的迁移文件,然后按照依赖关系依次执行这些迁移文件中的操作。
- 指定应用:和
makemigrations
命令一样,你也可以指定只对某个应用执行迁移,命令如下:
python manage.py migrate app_name
4.2.2 迁移历史记录
Django 会在数据库中创建一个名为 django_migrations
的表,用来记录所有的迁移历史。这个表包含了迁移文件的名称、应用名称和应用时间等信息。
- 查看迁移历史:你可以使用以下命令查看某个应用的迁移历史:
python manage.py showmigrations app_name
在输出结果中,[X]
表示该迁移文件已经应用到数据库中,[ ]
表示还没有应用。
4.3 回滚迁移
4.3.1 回滚到指定版本
有时候,我们可能需要将数据库回滚到某个之前的版本。这时可以使用 migrate
命令加上迁移文件的名称来实现。
- 使用方法:在命令行中,进入项目的根目录,然后输入以下命令:
python manage.py migrate app_name migration_name
这里的 app_name
是应用名称,migration_name
是你要回滚到的迁移文件的名称。
4.3.2 撤销未应用的迁移
如果有一些迁移文件还没有应用到数据库中,你可以使用 migrate
命令加上 zero
来撤销这些未应用的迁移。
- 使用方法:在命令行中,进入项目的根目录,然后输入以下命令:
python manage.py migrate app_name zero
这个命令会撤销该应用所有未应用的迁移文件。
通过以上这些操作,我们可以灵活地管理数据库的迁移,确保数据库结构和模型保持一致。😃
第五章 数据查询
5.1 查询集的基本操作
5.1.1 获取查询集
查询集(QuerySet)是 Django 中用于从数据库中获取对象集合的一种方式😃。可以通过模型类名直接调用方法来获取查询集。例如,假设有一个 Book
模型类,以下代码可以获取所有的 Book
对象组成的查询集:
from yourapp.models import Bookbooks = Book.objects.all()
这里的 Book.objects.all()
就返回了一个查询集,它包含了 Book
模型在数据库中的所有记录📚。查询集就像是一个可迭代的对象列表,你可以对它进行各种操作。
5.1.2 过滤查询集
过滤查询集可以让你从查询集中筛选出符合特定条件的对象。可以使用 filter()
方法来实现过滤。例如,要筛选出出版年份为 2023 年的 Book
对象:
from yourapp.models import Bookbooks_2023 = Book.objects.filter(pub_year=2023)
这里的 pub_year
是 Book
模型中的一个字段,filter(pub_year=2023)
表示筛选出 pub_year
字段值为 2023 的对象。还可以使用多个条件进行过滤,多个条件之间是“与”的关系:
books_2023_and_price_50 = Book.objects.filter(pub_year=2023, price=50)
5.1.3 排序查询集
排序查询集可以按照指定的字段对查询集中的对象进行排序。可以使用 order_by()
方法来实现排序。例如,要按照价格从低到高对 Book
对象进行排序:
from yourapp.models import Bookbooks_ordered_by_price = Book.objects.all().order_by('price')
这里的 order_by('price')
表示按照 price
字段进行升序排序。如果要进行降序排序,可以在字段名前加一个负号:
books_ordered_by_price_desc = Book.objects.all().order_by('-price')
5.1.4 切片查询集
切片查询集可以从查询集中获取指定范围的对象,类似于 Python 列表的切片操作。例如,要获取前 5 本 Book
对象:
from yourapp.models import Bookfirst_five_books = Book.objects.all()[:5]
这里的 [:5]
表示从查询集的开头开始取 5 个对象。也可以指定起始和结束位置,例如获取第 3 到第 6 本 Book
对象:
third_to_sixth_books = Book.objects.all()[2:6]
5.2 常用查询方法
5.2.1 get()方法
get()
方法用于从数据库中获取满足特定条件的单个对象。如果查询结果有且只有一个对象,则返回该对象;如果查询结果为空或有多个对象,则会抛出异常。例如,要获取 id
为 1 的 Book
对象:
from yourapp.models import Booktry:book = Book.objects.get(id=1)print(book.title)
except Book.DoesNotExist:print("没有找到该书籍!")
except Book.MultipleObjectsReturned:print("查询结果有多个对象!")
5.2.2 filter()方法
filter()
方法前面已经介绍过,它用于从查询集中筛选出符合特定条件的对象,返回一个新的查询集。可以使用各种字段和条件进行筛选,例如:
from yourapp.models import Book# 筛选出价格大于 50 的书籍
expensive_books = Book.objects.filter(price__gt=50)
这里的 __gt
是 Django 中的查询字段修饰符,表示“大于”的意思。
5.2.3 exclude()方法
exclude()
方法用于从查询集中排除符合特定条件的对象,返回一个新的查询集。例如,要排除价格为 50 的 Book
对象:
from yourapp.models import Bookbooks_not_50 = Book.objects.exclude(price=50)
5.2.4 all()方法
all()
方法用于获取模型在数据库中的所有对象组成的查询集。例如:
from yourapp.models import Bookall_books = Book.objects.all()
5.3 复杂查询
5.3.1 Q对象
Q
对象用于在查询中实现复杂的逻辑组合,例如“或”关系。在 Django 中,默认的多个条件是“与”关系,使用 Q
对象可以实现“或”关系。例如,要筛选出价格大于 50 或者出版年份为 2023 年的 Book
对象:
from yourapp.models import Book
from django.db.models import Qbooks = Book.objects.filter(Q(price__gt=50) | Q(pub_year=2023))
这里的 |
表示“或”关系,Q(price__gt=50)
和 Q(pub_year=2023)
是两个 Q
对象。
5.3.2 F对象
F
对象用于在查询中引用模型的字段,实现字段之间的比较。例如,要筛选出价格大于库存数量的 Book
对象:
from yourapp.models import Book
from django.db.models import Fbooks = Book.objects.filter(price__gt=F('stock'))
这里的 F('stock')
表示引用 Book
模型中的 stock
字段。
5.3.3 聚合查询
聚合查询用于对查询集中的对象进行统计计算,例如求和、平均值、最大值、最小值等。可以使用 aggregate()
方法来实现聚合查询。例如,要计算所有 Book
对象的平均价格:
from yourapp.models import Book
from django.db.models import Avgresult = Book.objects.aggregate(Avg('price'))
average_price = result['price__avg']
print(f"平均价格为: {average_price}")
5.3.4 分组查询
分组查询用于按照指定的字段对查询集中的对象进行分组,并对每个组进行聚合计算。可以使用 values()
和 annotate()
方法来实现分组查询。例如,要按照出版年份对 Book
对象进行分组,并计算每个组的平均价格:
from yourapp.models import Book
from django.db.models import Avggrouped_books = Book.objects.values('pub_year').annotate(avg_price=Avg('price'))
for group in grouped_books:print(f"出版年份: {group['pub_year']}, 平均价格: {group['avg_price']}")
这里的 values('pub_year')
表示按照 pub_year
字段进行分组,annotate(avg_price=Avg('price'))
表示对每个组计算平均价格。
第六章 数据操作
在数据库操作中,数据操作是非常重要的一部分,它主要包括创建数据、更新数据和删除数据。下面我们来详细了解每一种操作的具体方法。
6.1 创建数据
创建数据是向数据库中添加新记录的过程,这里我们介绍两种常用的方法。
6.1.1 使用 create()
方法
create()
方法是一种便捷的创建数据的方式,它可以一次性完成数据的创建和保存操作。以下是使用 create()
方法的步骤和示例:
-
步骤:
- 定义数据模型:首先需要定义一个数据模型,它规定了数据的结构和类型。
- 调用
create()
方法:在数据模型上调用create()
方法,并传入要创建的数据对象。
-
示例:假设我们有一个用户模型
User
,包含name
和age
两个字段。
# 定义用户模型
from django.db import modelsclass User(models.Model):name = models.CharField(max_length=100)age = models.IntegerField()# 使用 create() 方法创建新用户
new_user = User.objects.create(name='Alice', age=25)
在这个示例中,我们通过 User.objects.create()
方法创建了一个新的用户记录,并将其保存到数据库中。😃
6.1.2 使用 save()
方法
save()
方法是另一种创建数据的方式,它需要先创建一个数据对象,然后调用 save()
方法将其保存到数据库中。以下是使用 save()
方法的步骤和示例:
-
步骤:
- 定义数据模型:同样需要先定义一个数据模型。
- 创建数据对象:根据数据模型创建一个数据对象,并设置其属性。
- 调用
save()
方法:在数据对象上调用save()
方法,将其保存到数据库中。
-
示例:还是以用户模型
User
为例。
# 定义用户模型
from django.db import modelsclass User(models.Model):name = models.CharField(max_length=100)age = models.IntegerField()# 创建新用户对象
new_user = User(name='Bob', age=30)
# 保存新用户对象到数据库
new_user.save()
在这个示例中,我们先创建了一个 User
对象 new_user
,然后调用 save()
方法将其保存到数据库中。🤗
6.2 更新数据
更新数据是对数据库中已有的记录进行修改的过程,这里我们也介绍两种常用的方法。
6.2.1 使用 save()
方法
save()
方法不仅可以用于创建数据,还可以用于更新数据。以下是使用 save()
方法更新数据的步骤和示例:
-
步骤:
- 查询要更新的记录:使用查询方法找到要更新的记录。
- 修改记录的属性:在查询到的记录对象上修改其属性。
- 调用
save()
方法:在修改后的记录对象上调用save()
方法,将修改保存到数据库中。
-
示例:假设我们要将用户
Alice
的年龄更新为 26。
# 查询用户 Alice
user = User.objects.get(name='Alice')
# 修改用户的年龄
user.age = 26
# 保存修改到数据库
user.save()
在这个示例中,我们先通过 get()
方法查询到用户 Alice
,然后修改其 age
属性,最后调用 save()
方法将修改保存到数据库中。😎
6.2.2 使用 update()
方法
update()
方法可以直接在查询集上进行批量更新操作。以下是使用 update()
方法更新数据的步骤和示例:
-
步骤:
- 查询要更新的记录集:使用查询方法找到要更新的记录集。
- 调用
update()
方法:在查询集上调用update()
方法,并传入要更新的字段和值。
-
示例:假设我们要将所有用户的年龄都加 1。
# 查询所有用户记录集
users = User.objects.all()
# 批量更新用户的年龄
users.update(age=models.F('age') + 1)
在这个示例中,我们先通过 all()
方法查询到所有用户记录集,然后使用 update()
方法将所有用户的年龄都加 1。这里使用了 models.F()
来引用字段的值。😜
6.3 删除数据
删除数据是从数据库中移除记录的过程,这里我们介绍两种常用的方法。
6.3.1 使用 delete()
方法
delete()
方法可以用于删除单个记录或记录集。以下是使用 delete()
方法删除数据的步骤和示例:
-
步骤:
- 查询要删除的记录或记录集:使用查询方法找到要删除的记录或记录集。
- 调用
delete()
方法:在查询到的记录或记录集上调用delete()
方法,将其从数据库中删除。
-
示例:假设我们要删除用户
Bob
。
# 查询用户 Bob
user = User.objects.get(name='Bob')
# 删除用户 Bob
user.delete()
在这个示例中,我们先通过 get()
方法查询到用户 Bob
,然后调用 delete()
方法将其从数据库中删除。😢
6.3.2 级联删除
级联删除是指当删除一个记录时,与之关联的其他记录也会被自动删除。在 Django 中,可以通过设置外键的 on_delete
参数来实现级联删除。以下是一个级联删除的示例:
from django.db import models# 定义文章模型
class Article(models.Model):title = models.CharField(max_length=200)# 定义评论模型,与文章模型关联
class Comment(models.Model):content = models.TextField()article = models.ForeignKey(Article, on_delete=models.CASCADE)
在这个示例中,Comment
模型通过外键 article
与 Article
模型关联,并且设置了 on_delete=models.CASCADE
。这意味着当删除一篇文章时,与之关联的所有评论也会被自动删除。🤯
通过以上介绍,我们了解了数据操作中创建数据、更新数据和删除数据的常用方法,这些方法可以帮助我们更好地管理数据库中的数据。🎉
第七章 高级特性
7.1 事务处理
7.1.1 事务的基本概念
1. 什么是事务
事务是数据库管理系统执行过程中的一个逻辑单位,它由一组不可再分的数据库操作序列组成,这些操作要么全部成功执行,要么全部不执行。就好比你去超市购物🛒,从挑选商品、结账到离开超市,这一系列动作可以看作一个事务。如果结账成功,那么整个购物过程就完成了;如果结账时出现问题(比如钱不够),那么之前挑选的商品也不会被买走,就好像什么都没发生一样。
2. 事务的特性(ACID)
- 原子性(Atomicity):事务中的所有操作要么全部完成,要么全部不完成,不会结束在中间某个环节。就像发射火箭🚀,一旦点火启动,要么成功发射,要么发射失败,不会出现发射到一半就停止的情况。
- 一致性(Consistency):事务执行前后,数据库的状态必须保持一致。例如,在银行转账业务中,从一个账户向另一个账户转账,无论转账操作是否成功,两个账户的总金额应该保持不变。
- 隔离性(Isolation):多个事务并发执行时,一个事务的执行不能被其他事务干扰。就好像在图书馆里,每个人都在自己的座位上安静地看书📖,互不干扰。
- 持久性(Durability):事务一旦提交,它对数据库的改变就是永久性的,即使数据库发生故障也不会丢失。就像在纸上写下的文字✍️,一旦写上去,就很难抹去。
7.1.2 Django中的事务管理
1. Django事务管理的默认行为
在 Django 中,默认情况下,每个数据库操作都是一个独立的事务。也就是说,每个数据库查询都会自动提交。例如,当你执行一个 save()
方法保存一个模型实例时,这个操作会立即被提交到数据库。
2. 手动管理事务
transaction.atomic()
:这是 Django 中用于管理事务的上下文管理器。使用它可以将一组数据库操作封装在一个事务中。例如:
from django.db import transactionwith transaction.atomic():# 一系列数据库操作obj1.save()obj2.delete()
在这个例子中,obj1.save()
和 obj2.delete()
这两个操作会被封装在一个事务中。如果在执行过程中出现异常,整个事务会回滚,数据库不会发生任何改变。
@transaction.atomic
装饰器:如果你想将一个视图函数或方法中的所有数据库操作封装在一个事务中,可以使用这个装饰器。例如:
from django.db import transaction@transaction.atomic
def my_view(request):# 一系列数据库操作...
7.2 自定义管理器
7.2.1 管理器的作用
1. 什么是管理器
在 Django 中,管理器是模型与数据库交互的接口。每个 Django 模型都至少有一个管理器,默认情况下是 objects
管理器。它提供了一系列方法来查询和操作数据库中的数据。
2. 管理器的作用
- 提供自定义查询方法:可以根据业务需求,为模型添加自定义的查询方法。例如,如果你有一个
Book
模型,你可以添加一个get_popular_books()
方法来查询所有受欢迎的书籍。 - 封装业务逻辑:将一些与模型相关的业务逻辑封装在管理器中,使代码更加模块化和可维护。例如,在创建新的
User
模型实例时,可以在管理器中添加一些初始化操作。
7.2.2 自定义管理器的创建
1. 创建自定义管理器类
要创建自定义管理器,需要继承 django.db.models.Manager
类,并在其中定义自己的方法。例如:
from django.db import modelsclass BookManager(models.Manager):def get_popular_books(self):return self.filter(popularity__gt=100)class Book(models.Model):title = models.CharField(max_length=100)popularity = models.IntegerField()objects = BookManager()
在这个例子中,我们创建了一个 BookManager
类,它继承自 models.Manager
类,并定义了一个 get_popular_books()
方法,用于查询所有受欢迎的书籍(受欢迎程度大于 100)。然后,我们将 BookManager
实例赋值给 Book
模型的 objects
属性,这样就可以使用自定义的管理器了。
2. 使用自定义管理器
使用自定义管理器就像使用默认的 objects
管理器一样。例如:
popular_books = Book.objects.get_popular_books()
7.3 原始SQL查询
7.3.1 使用 raw()
方法
1. 什么是 raw()
方法
raw()
方法是 Django 提供的一个用于执行原始 SQL 查询的方法。它允许你直接使用 SQL 语句来查询数据库,并将查询结果封装成模型实例。
2. 使用示例
假设我们有一个 Person
模型:
from django.db import modelsclass Person(models.Model):name = models.CharField(max_length=100)age = models.IntegerField()
我们可以使用 raw()
方法来执行原始 SQL 查询:
people = Person.objects.raw('SELECT * FROM myapp_person WHERE age > 18')
for person in people:print(person.name)
在这个例子中,我们使用 raw()
方法执行了一个 SQL 查询,查询所有年龄大于 18 岁的人,并将查询结果封装成 Person
模型实例。
7.3.2 直接执行 SQL 语句
1. 使用 connection.cursor()
除了使用 raw()
方法,还可以使用 django.db.connection.cursor()
来直接执行 SQL 语句。例如:
from django.db import connectionwith connection.cursor() as cursor:cursor.execute('SELECT * FROM myapp_person WHERE age > 18')rows = cursor.fetchall()for row in rows:print(row)
在这个例子中,我们使用 connection.cursor()
创建了一个数据库游标,然后使用 execute()
方法执行了一个 SQL 查询,并使用 fetchall()
方法获取查询结果。
2. 注意事项
- SQL 注入风险:直接执行 SQL 语句时,要注意防止 SQL 注入攻击。可以使用参数化查询来避免这个问题。
- 数据库兼容性:不同的数据库系统可能对 SQL 语法有不同的支持,因此在编写 SQL 语句时要考虑数据库的兼容性。
第八章 性能优化
在开发过程中,性能优化是至关重要的,它可以显著提升系统的响应速度和用户体验。接下来我们将详细介绍查询优化和数据库优化两方面的内容。
8.1 查询优化
8.1.1 减少查询次数
在进行数据库操作时,频繁的查询会增加数据库的负担,降低系统性能。因此,我们要尽量减少不必要的查询。
示例场景
假设我们要展示一个文章列表,每篇文章都有作者信息。如果我们在循环中每次都去查询作者信息,就会产生大量的查询。
# 不好的示例
articles = Article.objects.all()
for article in articles:author = Author.objects.get(id=article.author_id)print(f"文章: {article.title}, 作者: {author.name}")
在这个示例中,每遍历一篇文章就会进行一次作者信息的查询,如果有 100 篇文章,就会产生 100 次额外的查询😫。
优化方法
我们可以通过一次查询获取所有需要的数据。
# 优化后的示例
articles = Article.objects.select_related('author').all()
for article in articles:print(f"文章: {article.title}, 作者: {article.author.name}")
这样,我们只进行了一次查询,就获取了文章和作者的信息,大大减少了查询次数👏。
8.1.2 使用 select_related() 和 prefetch_related()
1. select_related()
select_related()
主要用于处理一对一和外键关联的查询优化。它通过 SQL 的 JOIN
操作,在一次查询中获取相关联的数据。
示例
# 假设 Book 模型有一个外键关联到 Author 模型
books = Book.objects.select_related('author').all()
for book in books:print(f"书名: {book.title}, 作者: {book.author.name}")
在这个示例中,select_related('author')
会在查询 Book
时,通过 JOIN
操作同时获取 Author
的信息,避免了后续的额外查询。
2. prefetch_related()
prefetch_related()
用于处理多对多和反向关联的查询优化。它会分别执行多个查询,然后在 Python 层面进行关联。
示例
# 假设 Book 模型有多对多关联到 Tag 模型
books = Book.objects.prefetch_related('tags').all()
for book in books:for tag in book.tags.all():print(f"书名: {book.title}, 标签: {tag.name}")
在这个示例中,prefetch_related('tags')
会先查询所有的 Book
,再查询所有相关的 Tag
,最后在 Python 中进行关联,避免了在循环中多次查询 Tag
。
8.2 数据库优化
8.2.1 索引的使用
索引是数据库中一种特殊的数据结构,它可以加快数据的查询速度。就像书籍的目录一样,通过索引可以快速定位到所需的数据。
1. 何时使用索引
- 经常用于
WHERE
子句、JOIN
子句中的列。 - 经常用于排序的列。
2. 示例
假设我们有一个 User
模型,经常根据 username
进行查询。
from django.db import modelsclass User(models.Model):username = models.CharField(max_length=100, db_index=True)email = models.EmailField()# 其他字段...
在这个示例中,db_index=True
表示为 username
字段创建索引。这样,当我们根据 username
进行查询时,数据库可以更快地找到匹配的记录。
3. 注意事项
- 索引会占用额外的存储空间。
- 过多的索引会影响数据的插入、更新和删除操作的性能,因为每次操作都需要更新索引。
8.2.2 数据库配置优化
合理的数据库配置可以提高数据库的性能。以下是一些常见的配置优化建议:
1. 内存分配
根据服务器的内存情况,合理分配数据库的内存。例如,对于 MySQL 数据库,可以调整 innodb_buffer_pool_size
参数,它决定了 InnoDB 存储引擎用于缓存数据和索引的内存大小。
2. 并发设置
调整数据库的并发连接数,避免过多的连接导致数据库性能下降。例如,对于 PostgreSQL 数据库,可以通过 max_connections
参数来控制最大连接数。
3. 日志配置
合理配置数据库的日志,避免日志记录过多影响性能。例如,对于 MySQL 数据库,可以根据实际情况调整 log_bin
(二进制日志)和 slow_query_log
(慢查询日志)的相关参数。
通过以上的查询优化和数据库优化方法,可以显著提升系统的性能,让你的应用更加流畅😃。
第九章 测试与调试
9.1 单元测试
9.1.1 测试框架的选择
在进行单元测试时,选择合适的测试框架至关重要,它能帮助我们更高效地编写和运行测试用例。以下为你介绍几种常见的测试框架😃:
1. unittest(Python 内置)
- 特点:是 Python 标准库的一部分,无需额外安装,提供了基础的测试功能,如测试用例的组织、断言方法等。它遵循面向对象的设计,使用起来比较规范。
- 适用场景:适合初学者入门,以及对项目依赖要求较低的场景。例如,小型 Python 脚本的单元测试。
- 示例代码:
import unittestdef add(a, b):return a + bclass TestAdd(unittest.TestCase):def test_add(self):result = add(2, 3)self.assertEqual(result, 5)if __name__ == '__main__':unittest.main()
2. pytest
- 特点:功能强大,语法简洁,支持参数化测试、fixture 等高级特性,能自动发现测试用例,并且有丰富的插件生态系统。
- 适用场景:适用于各种规模的项目,尤其是需要进行复杂测试的场景。例如,大型 Web 应用的单元测试。
- 示例代码:
def add(a, b):return a + bdef test_add():assert add(2, 3) == 5
3. Jasmine(JavaScript)
- 特点:是一个行为驱动开发(BDD)的测试框架,无需外部依赖,自带断言库和测试运行器,适合测试 JavaScript 代码。
- 适用场景:常用于前端 JavaScript 代码的单元测试,如网页中的脚本。
- 示例代码:
function add(a, b) {return a + b;
}describe('add function', function() {it('should return the sum of two numbers', function() {expect(add(2, 3)).toBe(5);});
});
9.1.2 编写测试用例
编写高质量的测试用例是单元测试的核心,以下是编写测试用例的步骤和要点🧐:
1. 明确测试目标
在编写测试用例之前,需要明确要测试的功能或方法,确定其输入和预期输出。例如,要测试一个函数 calculate_average
,它接收一个数字列表作为输入,返回这些数字的平均值。
2. 选择合适的测试数据
- 正常数据:选择符合函数预期输入的数据进行测试。例如,对于
calculate_average
函数,可以使用[1, 2, 3]
作为测试数据。 - 边界数据:考虑输入的边界情况,如空列表、只有一个元素的列表等。对于
calculate_average
函数,空列表是一个边界情况。 - 异常数据:测试函数在遇到异常输入时的处理能力。例如,传入非数字列表。
3. 使用断言
断言是测试用例中用于验证实际输出是否符合预期输出的语句。不同的测试框架提供了不同的断言方法,如 unittest
中的 assertEqual
,pytest
中的 assert
,Jasmine
中的 expect
。
4. 示例代码
以下是使用 pytest
为 calculate_average
函数编写的测试用例:
def calculate_average(numbers):if not numbers:return 0return sum(numbers) / len(numbers)def test_calculate_average_normal():numbers = [1, 2, 3]result = calculate_average(numbers)assert result == 2def test_calculate_average_empty():numbers = []result = calculate_average(numbers)assert result == 0
9.2 调试技巧
9.2.1 使用 Django 调试工具
Django 提供了一些强大的调试工具,能帮助我们快速定位和解决问题😎:
1. DEBUG 模式
- 开启方法:在
settings.py
文件中,将DEBUG
设置为True
。
DEBUG = True
- 作用:当应用出现错误时,Django 会显示详细的错误页面,包含错误类型、错误堆栈信息、请求信息等,方便我们定位问题。
2. Django Debug Toolbar
- 安装方法:使用
pip install django-debug-toolbar
进行安装,然后在settings.py
中进行配置。
INSTALLED_APPS = [# ...'debug_toolbar',# ...
]MIDDLEWARE = [# ...'debug_toolbar.middleware.DebugToolbarMiddleware',# ...
]INTERNAL_IPS = ['127.0.0.1',
]
- 作用:在开发环境中,它会在页面右侧显示一个工具栏,提供了 SQL 查询、模板渲染、请求信息等详细信息,帮助我们优化代码性能。
3. 日志记录
可以在 settings.py
中配置日志记录,将关键信息记录到日志文件中,方便后续分析。
LOGGING = {'version': 1,'disable_existing_loggers': False,'handlers': {'file': {'level': 'DEBUG','class': 'logging.FileHandler','filename': 'debug.log',},},'loggers': {'django': {'handlers': ['file'],'level': 'DEBUG','propagate': True,},},
}
9.2.2 日志记录
日志记录是调试过程中非常重要的一环,它可以帮助我们记录程序的运行状态和关键信息📝:
1. 日志级别
常见的日志级别有 DEBUG
、INFO
、WARNING
、ERROR
、CRITICAL
,从低到高表示不同的严重程度。
2. 配置日志
在 Python 中,可以使用内置的 logging
模块进行日志配置。以下是一个简单的配置示例:
import logging# 配置日志
logging.basicConfig(level=logging.DEBUG,format='%(asctime)s - %(levelname)s - %(message)s',filename='app.log'
)# 记录日志
logging.debug('This is a debug message')
logging.info('This is an info message')
logging.warning('This is a warning message')
logging.error('This is an error message')
logging.critical('This is a critical message')
3. 日志的使用场景
- DEBUG 级别:用于开发和调试阶段,记录详细的程序运行信息。
- INFO 级别:记录程序的正常运行状态,如用户登录、数据更新等。
- WARNING 级别:表示可能存在问题,但不影响程序的正常运行,如文件读取失败但有默认值。
- ERROR 级别:记录程序中出现的错误,如数据库连接失败。
- CRITICAL 级别:表示严重的错误,可能导致程序无法继续运行,如系统崩溃。