ORM
ORM 概述
ORM,对象关系映射,对象和关系之间的映射,使用面向对象的方式来操作数据库。
- 对象指的是面向对象中的对象,关系指的是关系型数据库。
关系模型和python对象之间的映射:
table -> class # 表映射为类
row -> object # 行映射为实例
column -> property # 字段映射为属性例如,有表student,字段为id int,name varchar,age int,映射到python为:
class Student:
id = ?某类型字段
name = ?某类型字段
age = ?某类型字段
## 最终得到实例:
class Student:
def __init__(self):
self.id = ?
self.name = ?
self.age = ?—
ORM,即对象关系映射(Object-Relational Mapping),是一种程序设计技术,它在面向对象编程语言和关系数据库之间建立了一种映射关系,使得数据库中的数据可以以对象的形式进行操作,从而简化了开发人员在应用程序中对数据的处理。
ORM 的主要作用包括:
- 将数据库中的表映射为面向对象编程语言中的类,表中的每一行数据都对应着类的一个实例对象。
- 通过简单的 API(应用程序接口)或语言特性,开发人员可以像操作对象一样操作数据库中的数据,无需编写复杂的 SQL 查询语句。
- 提高了代码的可维护性和可读性,使得开发人员可以更加专注于业务逻辑的实现,而无需过多关注数据库操作的细节。
- ORM 框架通常提供了一些额外功能,如数据校验、缓存管理、事务控制等,简化了开发过程。
常见的 ORM 框架包括 Django 的 ORM、SQLAlchemy、Hibernate(用于 Java)等。这些框架通常提供了丰富的功能和灵活的配置选项,可以根据项目的需求选择合适的框架。
在使用 ORM 框架时,开发人员需要定义好对象与数据库表之间的映射关系,并配置好数据库连接信息。然后就可以通过框架提供的 API 或语言特性来进行数据操作,例如插入、更新、删除和查询等操作。
尽管 ORM 技术简化了开发过程,但在处理大量数据或对性能要求较高的场景下,开发人员仍需注意 ORM 操作可能带来的性能损耗,并根据实际情况进行优化。
Django ORM
Django 的 ORM(Object-Relational Mapping)是 Django 框架中的一个核心组件,它提供了一种将数据库表映射到 Python 对象的方式,使得开发人员可以使用面向对象的方式操作数据库,而无需直接编写 SQL 查询语句。下面是 Django ORM 的一些重要特性和用法详解:
- 模型定义:在 Django 中,每个数据库表都对应着一个模型(Model),模型是一个 Python 类,通过继承
django.db.models.Model类来定义。模型类中的属性代表数据库表的字段,例如 CharField、IntegerField、DateTimeField 等。
from django.db import models
class MyModel(models.Model):
name = models.CharField(max_length=100)
age = models.IntegerField()
created_at = models.DateTimeField(auto_now_add=True)-
数据库迁移:通过 Django 的迁移工具可以将模型类的定义转换成对应的数据库表结构,并同步到数据库中。开发人员只需运行
python manage.py makemigrations和python manage.py migrate命令即可完成数据库迁移过程。 -
数据查询:Django 提供了 QuerySet API 来执行数据库查询操作,QuerySet 是一个可迭代的对象,支持链式调用。开发人员可以使用各种方法来过滤、排序、限制查询结果等。
## 查询所有数据
MyModel.objects.all()
## 根据条件过滤数据
MyModel.objects.filter(age__gte=18)
## 排序查询结果
MyModel.objects.order_by('-created_at')
## 获取单个对象
MyModel.objects.get(pk=1)- 数据操作:Django 的模型类提供了一些方法来执行数据操作,如创建、更新、删除等。
## 创建对象
obj = MyModel.objects.create(name='John', age=25)
## 更新对象
obj.age = 26
obj.save()
## 删除对象
obj.delete()- 关联查询:Django 支持定义模型之间的关联关系,如一对多、多对一、多对多等。通过定义外键、一对一字段等来建立关联关系,并可以通过双下划线语法进行关联查询。
class Author(models.Model):
name = models.CharField(max_length=100)
class Book(models.Model):
title = models.CharField(max_length=100)
author = models.ForeignKey(Author, on_delete=models.CASCADE)
## 查询某个作者的所有书籍
author = Author.objects.get(name='John Doe')
author.books.all()Django ORM 提供了丰富的功能和灵活的查询语法,使得开发人员可以高效地进行数据库操作。同时,Django 还提供了一些性能优化的方式,如使用 select_related()、prefetch_related() 方法进行关联查询优化,以及使用 defer()、only() 方法选择需要查询的字段,从而提升查询效率。
前期准备
1. 创建项目
安装 Django 后,打开命令行终端,导航到你想要创建项目的目录,并执行以下命令来创建一个新的 Django 项目:
django-admin startproject <project_name>
这里的 <project_name> 是你想要给项目起的名字,可以是任何合法的 Python 标识符。
执行上述命令后,会在当前目录下创建一个新的目录,目录名就是你指定的项目名称。在这个目录中,会生成一些默认的文件和子目录,这些文件和目录的功能如下:
<project_name>/:项目的根目录,以你指定的项目名称命名。manage.py:Django 项目的命令行工具,用于执行各种管理任务。<project_name>/:项目的 Python 包,包含项目的设置、URL 配置等。__init__.py:一个空文件,表示该目录是一个 Python 包。settings.py:Django 项目的设置文件,包含项目的配置信息。urls.py:URL 路由配置文件,定义了 URL 和视图函数之间的映射关系。wsgi.py:WSGI 兼容的 Web 服务器入口,用于部署项目到 WSGI 兼容的服务器。
2. 创建应用
创建 Django 应用是 Django 开发中的重要步骤,它允许你将不同功能或模块拆分成独立的组件,以便更好地组织代码和逻辑。下面是创建 Django 应用的详细步骤:
-
进入 Django 项目目录: 首先,确保你已经在 Django 项目的根目录下。如果你还没有创建 Django 项目,可以使用
django-admin startproject <project_name>命令创建一个新的项目。 -
使用
startapp命令创建应用: 在命令行中执行以下命令来创建一个新的 Django 应用:python manage.py startapp <app_name>这里的
<app_name>是你想为应用指定的名称。执行上述命令后,Django 将在项目目录下创建一个新的应用程序目录,其中包含了一些基本的文件和文件夹结构。 -
查看新应用的文件结构: 创建应用后,你会在项目目录中看到一个新的文件夹,其名称与你指定的应用名称相同。在这个文件夹中,你将看到一些文件和子文件夹,包括:
models.py:用于定义应用程序的数据模型。views.py:包含应用程序的视图函数,用于处理请求并生成响应。urls.py:定义了应用程序的 URL 路由映射。admin.py:用于注册模型以在 Django 管理界面中进行管理。apps.py:包含应用程序的配置信息。migrations/:用于存储数据库迁移文件。tests.py:包含应用程序的单元测试。
-
注册应用到项目中: 一旦应用程序创建完成,你需要将其注册到项目中。为此,你需要编辑项目目录下的
settings.py文件,并在INSTALLED_APPS配置项中添加你的应用名称。例如:INSTALLED_APPS = [ ... 'myapp', ]这里的
'myapp'是你创建的应用的名称。
3. 配置项目
编辑settings.py:
## 配置连接数据库,注释掉默认的 `DATABASES` 字段,如使用MySQL,可添加类似如下配置:
## https://docs.djangoproject.com/en/4.2/ref/settings/#databases
DATABASES = {
"default": {
"ENGINE": "django.db.backends.mysql",
"NAME": "hellodb",
"USER": "azheng",
"PASSWORD": "123456",
"HOST": "10.0.0.123",
"PORT": "3306",
}
}
## 设置时区
TIME_ZONE = 'Asia/Shanghai'
## 设置日志输出
## https://docs.djangoproject.com/zh-hans/4.2/topics/logging/
LOGGING = {
"version": 1,
"disable_existing_loggers": False,
"handlers": {
"file": {
"level": "DEBUG",
"class": "logging.FileHandler",
"filename": "/var/log/django.log",
},
},
"loggers": {
"django.db.backends": {
"handlers": ["file"],
"level": "DEBUG",
"propagate": True,
},
},
}4. 定义模型(可选)
如果你的应用需要与数据库交互,你可以在 models.py 文件中定义应用的数据模型。通过定义模型,你可以定义数据的结构和行为,并使用 Django 的 ORM 来处理数据库操作。
5. 编写视图和 URL 映射
在 views.py 文件中编写视图函数,用于处理来自客户端的请求。然后,在 urls.py 文件中定义 URL 路由映射,将 URL 请求与相应的视图函数关联起来。
6. 运行开发服务器
在完成以上步骤后,你可以使用 python manage.py runserver 命令来启动 Django 的开发服务器,并在浏览器中访问应用程序,进行测试和调试。
示例
-
创建项目:
-
也可使用
django-admin startproject test_django_project .避免创建子目录 -
# django-admin startproject test_django_project # tree . └── test_django_project ├── manage.py └── test_django_project ├── __init__.py ├── asgi.py ├── settings.py ├── urls.py └── wsgi.py
-
-
创建应用
-
# cd test_django_project/ # python3 manage.py startapp app1 # tree . ├── app1 │ ├── __init__.py │ ├── admin.py │ ├── apps.py │ ├── migrations │ │ └── __init__.py │ ├── models.py │ ├── tests.py │ └── views.py ├── manage.py └── test_django_project ├── __init__.py ├── __pycache__ │ ├── __init__.cpython-38.pyc │ └── settings.cpython-38.pyc ├── asgi.py ├── settings.py ├── urls.py └── wsgi.py # 注册应用到项目中 # vim test_django_project/settings.py INSTALLED_APPS = [ ... 'app1', ]
-
创建 Model 类
models.py:
from django.db import models
## Create your models here.
## CREATE TABLE `students` (
## `StuID` int unsigned NOT NULL AUTO_INCREMENT,
## `Name` varchar(50) NOT NULL,
## `Age` tinyint unsigned NOT NULL,
## `Gender` enum('F','M') NOT NULL,
## `ClassID` tinyint unsigned DEFAULT NULL,
## `TeacherID` int unsigned DEFAULT NULL,
## PRIMARY KEY (`StuID`)
## ) ENGINE=InnoDB AUTO_INCREMENT=26 DEFAULT CHARSET=utf8mb3;
class Employee(models.Model): # metaclass=ModelBase
class Meta:
db_table = 'students'
StuID = models.IntegerField(primary_key=True)
Name = models.CharField(null=False, max_length=50)
Age = models.IntegerField(null=False)
Gender = models.SmallIntegerField(null=False)
ClassID = models.IntegerField(null=True)
TeacherID = models.IntegerField(null=True)
def __repr__(self):
return "<Employee: {} {} {}>".format(
self.StuID, self.Name, self.Age
)
__str__ = __repr__t1.py(引用):
import os
import django
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "salary.settings")
django.setup()
from employee.models import Employee
emps = Employee.objects.all() # 懒查询
print(emps)
for emp in emps:
print(type(emp), emp) # 返回的每行都是Employee的各个实例字段映射含义
当你在 Django 中创建一个 Model 类时,你使用的字段(Field)定义了数据库表中的列以及这些列的数据类型。下面是一些常用的字段以及它们的含义:
-
AutoField: 一个自动增长的整数字段,通常用作主键。
-
BigAutoField: 一个自动增长的64位整数字段,用作大型主键。
-
BigIntegerField: 一个64位整数字段。
-
BinaryField: 用于存储二进制数据的字段。
-
BooleanField: 一个布尔字段,存储 True 或 False。
-
CharField: 一个字符串字段,需要指定最大长度。
-
DateField: 一个日期字段。
-
DateTimeField: 一个日期时间字段。
-
DecimalField: 一个十进制数字段。
-
DurationField: 一个持续时间字段,用于存储时间间隔。
-
EmailField: 一个字符串字段,验证输入是否为有效的电子邮件地址。
-
FileField: 用于上传文件的字段。
-
FloatField: 一个浮点数字段。
-
ImageField: 用于上传图像文件的字段。
-
IntegerField: 一个整数字段。
-
GenericIPAddressField: 一个存储 IPv4 或 IPv6 地址的字段。
-
NullBooleanField: 一个允许存储 Null 值的布尔字段。
-
PositiveIntegerField: 一个正整数字段。
-
PositiveSmallIntegerField: 一个正整数字段,范围较小。
-
SlugField: 一个用于URLs的字符串字段,通常用于友好的URL。
-
SmallAutoField: 一个自动增长的16位整数字段,用作较小的主键。
-
SmallIntegerField: 一个16位整数字段。
-
TextField: 一个文本字段,通常用于长文本。
-
TimeField: 一个时间字段。
这些字段是 Django 中最常用的一些字段类型,它们允许你在模型中定义各种类型的数据,并且 Django 会根据这些字段自动创建相应的数据库结构。
管理器对象 objects
在Django中,管理器对象(objects)是模型(Model)的一个关键部分,用于管理数据库中模型实例的创建、查询、更新和删除等操作。下面是一些关于Django管理器对象的详解:
-
默认管理器(objects):
- Django模型中默认情况下会自动创建一个名为
objects的管理器,用于执行数据库操作。 - 如果你没有明确地为模型指定自定义的管理器,
objects管理器将会被自动创建并添加到模型中。
- Django模型中默认情况下会自动创建一个名为
-
自定义管理器:
- 你可以通过在模型中定义自定义管理器来扩展模型的查询功能。
- 自定义管理器是一个继承自
models.Manager的类,你可以在其中定义额外的方法来执行特定的查询操作。 - 通过自定义管理器,你可以轻松地封装常用的查询逻辑,使代码更加清晰和可维护。
-
使用自定义管理器:
- 一旦你在模型中定义了自定义管理器,你可以通过调用该管理器的方法来执行自定义的查询操作。
- 例如,如果你定义了一个名为
CustomManager的自定义管理器,你可以通过MyModel.custom_manager_name.method()的方式来使用它。
-
修改默认管理器:
- 你可以通过在模型中添加
objects属性并将其设置为你自定义的管理器的实例来覆盖默认的objects管理器。 - 这样做可以让你在模型中使用自定义的查询逻辑而不是默认的
objects管理器。
- 你可以通过在模型中添加
-
链式调用:
- 管理器方法通常支持链式调用,这意味着你可以在单个查询中连续调用多个管理器方法。
- 这种方法允许你构建复杂的查询,同时保持代码的可读性和简洁性。
-
延迟查询执行:
- 在Django中,查询通常是延迟执行的。这意味着当你调用管理器方法时,实际的数据库查询不会立即执行,直到需要获取查询结果时才会执行。
- 这种延迟执行的特性使得可以构建高效的查询链,只有在必要时才会触发数据库操作。
总的来说,Django管理器对象(objects)提供了一个强大的接口来管理模型实例在数据库中的操作。通过默认管理器和自定义管理器,你可以方便地执行各种数据库查询操作,并且可以根据需要轻松地扩展和定制查询功能。
查询
查询集
在Django中,查询集(QuerySet)是对数据库中数据进行查询的对象。它允许你以一种Pythonic的方式来构建和执行数据库查询,同时提供了一系列方法来过滤、排序、限制和聚合数据。以下是关于Django查询集的详细解释:
-
概述:
- 查询集是一个包含数据库中数据的集合,可以通过模型的管理器(objects)或相关对象的关联属性进行访问。
- 查询集允许你在不触发实际数据库查询的情况下构建查询,并在需要时才执行实际的数据库操作。
- 查询集是惰性的,这意味着它们不会立即执行查询,直到需要获取查询结果或对查询集进行迭代时才会执行。
-
创建查询集:
- 你可以通过使用模型的管理器(objects)或相关对象的关联属性来创建查询集。
- 例如,
MyModel.objects.all()会返回一个包含模型MyModel中所有对象的查询集。
-
过滤数据:
- 你可以使用
filter()方法来过滤查询集中的数据,根据指定的条件筛选出满足条件的对象。 - 例如,
MyModel.objects.filter(name='John')会返回一个只包含name字段为’John’的对象的查询集。
- 你可以使用
-
排序数据:
- 使用
order_by()方法可以对查询集中的数据进行排序。 - 例如,
MyModel.objects.order_by('name')会按照name字段的值对查询集中的对象进行升序排序。
- 使用
-
限制数据:
- 你可以使用
limit()、offset()和slice()等方法来限制查询集返回的结果数量。 - 例如,
MyModel.objects.all()[:5]会返回查询集中的前5个对象。
- 你可以使用
-
聚合数据:
- 使用
aggregate()方法可以对查询集中的数据进行聚合操作,如计数、求和、平均值等。 - 例如,
MyModel.objects.aggregate(Count('id'))会返回查询集中对象数量的统计信息。
- 使用
-
链式调用:
- 查询集方法通常支持链式调用,这意味着你可以在单个查询中连续调用多个方法。
- 例如,
MyModel.objects.filter(category='A').order_by('-date')会先对分类为’A’的对象进行过滤,然后按照日期降序排序。
-
延迟执行:
- 查询集是惰性执行的,它们不会立即执行查询,而是在需要获取查询结果时才会执行实际的数据库操作。
- 这种延迟执行的特性使得可以构建高效的查询链,只有在必要时才会触发数据库操作。
-
查询集方法:
- Django提供了丰富的查询集方法,包括
filter()、exclude()、get()、order_by()、annotate()等,用于执行不同类型的数据库操作。
- Django提供了丰富的查询集方法,包括
总的来说,Django查询集提供了一个强大而灵活的接口来与数据库进行交互。通过使用查询集方法,你可以轻松地构建复杂的数据库查询,并在需要时执行实际的数据库操作,从而实现高效的数据检索和处理。
注意事项
还有注意是否使用了缓存:
## 查询了三次:
emps = Employee.objects.all() # 懒查询
print(emps)
print(emps)
print(emps)
## 只查询了一次
emps = list(Employee.objects.all()) # 非查询,查询后将查询结果包在了列表当中
print(emps)
print(emps)
print(emps)限制查询集(切片)
在Django中,你可以使用切片来限制查询集(QuerySet)返回的结果数量。切片允许你获取查询集中的一部分数据,可以用于分页或限制结果集大小。以下是如何在Django中使用切片来限制查询集的方法:
-
使用索引切片: 你可以使用Python中的索引切片语法来限制查询集的结果数量。索引切片使用
[start:stop]的形式,其中start是起始索引,stop是结束索引(不包括在结果中)。# 获取前5个对象 objects = MyModel.objects.all()[:5] # 获取第6到第10个对象 objects = MyModel.objects.all()[5:10] -
获取前N个对象: 如果你只需要获取查询集中的前N个对象,你可以简单地通过索引切片获取。
# 获取前10个对象 objects = MyModel.objects.all()[:10] -
跳过前N个对象: 除了限制返回结果的数量,你也可以使用切片来跳过查询集中的前N个对象。
# 跳过前5个对象,获取后面的对象 objects = MyModel.objects.all()[5:] -
结合其他查询: 切片可以与其他查询方法链式调用,例如过滤、排序等。
# 获取前5个满足特定条件的对象 objects = MyModel.objects.filter(category='A')[:5] # 获取按时间降序排序的前10个对象 objects = MyModel.objects.order_by('-date')[:10]
通过使用切片,你可以轻松地限制查询集返回的结果数量,以满足不同的需求,如分页显示或者获取前N个对象。
结果集方法
all()
注意:all() 会返回结果集的所有对象,因此通常不建议使用,如果使用,通常也应配合切片使用
在Django中,all() 方法用于获取查询集(QuerySet)中的所有对象。这个方法可以用于检索模型中的所有记录,无论是单个模型还是关联模型的集合。
基本用法如下:
objects = MyModel.objects.all()这将返回模型 MyModel 中所有对象的查询集。你可以通过迭代或其他查询集方法来处理这个查询集。
例如,你可以对结果集进行迭代:
for obj in objects:
print(obj)或者你可以对结果集进行其他查询操作,比如过滤、排序等:
## 获取所有名字以'A'开头的对象
objects_starting_with_a = MyModel.objects.all().filter(name__startswith='A')
## 对结果集按照日期降序排序
objects_ordered_by_date = MyModel.objects.all().order_by('-date')总的来说,all() 方法用于检索模型中的所有对象,并返回一个查询集,你可以进一步对这个查询集进行处理和操作。
all() 背后的SQL:
all() 方法返回的是一个包含了所有数据库表中数据的查询集(QuerySet)。这个方法在内部并不会生成具体的 SQL 查询,因为它只是返回了一个未执行的查询集对象。但是,当你对这个查询集进行实际的操作(比如迭代、序列化等),Django 会自动执行相应的 SQL 查询以获取数据。
在执行 all() 方法时,Django 会生成类似于以下的 SQL 查询:
SELECT * FROM app_mymodel;这条 SQL 查询会选取指定模型 MyModel 对应的数据库表中的所有数据,并将其返回给 Django 的查询集对象。
值得注意的是,all() 方法返回的查询集可能会包含大量数据,因此在使用时要注意性能问题。如果可能的话,最好根据需要进行过滤或者分页,以减少返回数据的数量,提高系统的性能。
filter()
过滤,返回满足条件的数据
filter() 方法用于在查询集(QuerySet)中过滤出符合指定条件的对象。这个方法允许你根据指定的条件来筛选查询集中的数据,并返回一个新的查询集,其中包含满足条件的对象。
基本用法如下:
filtered_objects = MyModel.objects.filter(field_name=value)其中,field_name 是模型中的字段名,value 是要匹配的值。filter() 方法将返回一个新的查询集,其中包含模型 MyModel 中满足条件的对象。
你可以使用不同的条件来过滤数据,例如:
- 精确匹配:
filter(field_name=value) - 大小写敏感的精确匹配:
filter(field_name__exact=value) - 不区分大小写的匹配:
filter(field_name__iexact=value) - 包含:
filter(field_name__contains=value) - 区分大小写的包含:
filter(field_name__contains=value) - 不区分大小写的包含:
filter(field_name__icontains=value),慎用!性能很差! - 不区分大小写匹配开头:
filter(field_name__istartswith=value) - 不区分大小写匹配结尾:
filter(field_name__iendswith=value) - 大于:
filter(field_name__gt=value) - 小于:
filter(field_name__lt=value) - 大于或等于:
filter(field_name__gte=value) - 小于或等于:
filter(field_name__lte=value) - 在列表中:
filter(field_name__in=[value1, value2, ...]) - 不在列表中:
filter(field_name__notin=[value1, value2, ...]) - 等于 NULL:
filter(field_name__isnull=True) - 不等于 NULL:
filter(field_name__isnull=False) - 外键过滤:
filter(foreign_key__field_name=value)
例如,如果要过滤出名字为 “John” 的所有对象,可以这样做:
john_objects = MyModel.objects.filter(name='John')这将返回一个包含所有名字为 “John” 的对象的查询集。
filter() 背后的SQL:
filter() 方法用于根据指定的条件过滤数据库中的数据,并返回满足条件的对象的查询集(QuerySet)。当你调用 filter() 方法时,Django 会生成相应的 SQL 查询来执行过滤操作。
下面是一个示例模型和使用 filter() 方法的示例:
class MyModel(models.Model):
name = models.CharField(max_length=100)
category = models.CharField(max_length=50)
price = models.DecimalField(max_digits=10, decimal_places=2)
## 使用 filter() 方法进行过滤
objects = MyModel.objects.filter(category='A')在这个示例中,filter() 方法用于根据 category 字段的值为 'A' 来过滤数据。当你执行这个查询时,Django 将生成以下类似的 SQL 查询:
SELECT * FROM app_mymodel WHERE category = 'A';这条 SQL 查询会选取指定模型 MyModel 对应的数据库表中 category 字段值为 'A' 的所有数据,并将其返回给 Django 的查询集对象。
根据 filter() 方法的参数不同,生成的 SQL 查询会相应地变化,以满足你的过滤条件。
示例
result = mgr.filter(StuID=1)
print(*result)
'''
(0.000) SELECT `students`.`StuID`, `students`.`Name`, `students`.`Age`, `students`.`Gender`, `students`.`ClassID`, `students`.`TeacherID` FROM `students` WHERE `students`.`StuID` = 1; args=(1,); alias=default
<Employee: 1 Shi Zhongyu 22>
'''
result = mgr.filter(Age__gt=10, Age__lt=30)
print(*result)
"""
(0.000) SELECT `students`.`StuID`, `students`.`Name`, `students`.`Age`, `students`.`Gender`, `students`.`ClassID`, `students`.`TeacherID` FROM `students` WHERE (`students`.`Age` > 10 AND `students`.`Age` < 30); args=(10, 30); alias=default
"""exclude()
过滤,返回满足条件的数据
exclude() 方法与 filter() 方法相似,但是它用于排除满足指定条件的对象,而不是返回这些对象。这个方法允许你在查询集(QuerySet)中排除符合指定条件的对象,并返回一个新的查询集,其中包含不满足条件的对象。
基本用法如下:
excluded_objects = MyModel.objects.exclude(field_name=value)其中,field_name 是模型中的字段名,value 是要排除的值。exclude() 方法将返回一个新的查询集,其中包含模型 MyModel 中不满足条件的对象。
你可以使用不同的条件来排除数据,例如:
- 精确匹配:
exclude(field_name=value) - 大小写敏感的精确匹配:
exclude(field_name__exact=value) - 不区分大小写的匹配:
exclude(field_name__iexact=value) - 包含:
exclude(field_name__contains=value) - 不区分大小写的包含:
exclude(field_name__icontains=value) - 大于:
exclude(field_name__gt=value) - 小于:
exclude(field_name__lt=value) - 大于或等于:
exclude(field_name__gte=value) - 小于或等于:
exclude(field_name__lte=value) - 在列表中:
exclude(field_name__in=[value1, value2, ...]) - 不在列表中:
exclude(field_name__notin=[value1, value2, ...]) - 等于 NULL:
exclude(field_name__isnull=True) - 不等于 NULL:
exclude(field_name__isnull=False) - 外键过滤:
exclude(foreign_key__field_name=value)
例如,如果要排除名字为 “John” 的所有对象,可以这样做:
not_john_objects = MyModel.objects.exclude(name='John')这将返回一个包含所有名字不为 “John” 的对象的查询集。
exclude() 背后的SQL:
exclude() 方法用于在数据库查询中排除满足指定条件的对象,并返回不满足条件的对象的查询集(QuerySet)。当你调用 exclude() 方法时,Django 会生成相应的 SQL 查询来执行排除操作。
下面是一个示例模型和使用 exclude() 方法的示例:
class MyModel(models.Model):
name = models.CharField(max_length=100)
category = models.CharField(max_length=50)
price = models.DecimalField(max_digits=10, decimal_places=2)
## 使用 exclude() 方法进行排除
objects = MyModel.objects.exclude(category='A')在这个示例中,exclude() 方法用于排除 category 字段的值为 'A' 的数据。当你执行这个查询时,Django 将生成以下类似的 SQL 查询:
SELECT * FROM app_mymodel WHERE NOT (category = 'A');这条 SQL 查询会选取指定模型 MyModel 对应的数据库表中 category 字段值不为 'A' 的所有数据,并将其返回给 Django 的查询集对象。
根据 exclude() 方法的参数不同,生成的 SQL 查询会相应地变化,以满足你的排除条件。
示例
result = mgr.exclude(StuID__in=[6, 7, 8])
print(result)
'''
<QuerySet [<Employee: 1 Shi Zhongyu 22>, <Employee: 2 Shi Potian 22>, <Employee: 3 Xie Yanke 53>, <Employee: 4 Ding Dian 32>, <Employee: 5 Yu Yutong 26>, <Employee: 9 Ren Yingying 20>, ...
'''order_by()
排序,注意参数是字符串
order_by() 方法用于对查询集(QuerySet)中的结果进行排序。它允许你根据指定的字段对查询集中的对象进行排序,并返回一个新的查询集,其中包含按照指定字段排序后的对象。
基本用法如下:
ordered_objects = MyModel.objects.order_by(field_name)其中,field_name 是你想要排序的字段名。order_by() 方法将返回一个新的查询集,其中包含模型 MyModel 中按照指定字段排序后的对象。
你可以对结果进行升序排序(默认)或者降序排序。例如,要对结果按照 name 字段进行升序排序,可以这样做:
ordered_objects = MyModel.objects.order_by('name')如果要对结果按照 name 字段进行降序排序,可以在字段名前加上减号(-):
ordered_objects = MyModel.objects.order_by('-name')除了单个字段外,你还可以同时指定多个字段进行排序。例如,要先按照 category 字段升序排序,然后再按照 name 字段降序排序,可以这样做:
ordered_objects = MyModel.objects.order_by('category', '-name')这将返回一个按照 category 字段升序排序,然后按照 name 字段降序排序的查询集。
总的来说,order_by() 方法用于对查询集中的结果进行排序,并返回一个新的查询集。你可以根据需要指定单个字段或多个字段进行排序,并选择升序或降序排序。
order_by() 背后的SQL:
order_by() 方法用于对查询集中的结果进行排序,并返回按照指定条件排序后的对象的查询集(QuerySet)。当你调用 order_by() 方法时,Django 会生成相应的 SQL 查询来执行排序操作。
下面是一个示例模型和使用 order_by() 方法的示例:
class MyModel(models.Model):
name = models.CharField(max_length=100)
category = models.CharField(max_length=50)
price = models.DecimalField(max_digits=10, decimal_places=2)
## 使用 order_by() 方法进行排序
objects = MyModel.objects.order_by('price')在这个示例中,order_by() 方法用于按照 price 字段的值对数据进行升序排序。当你执行这个查询时,Django 将生成以下类似的 SQL 查询:
SELECT * FROM app_mymodel ORDER BY price ASC;这条 SQL 查询会选取指定模型 MyModel 对应的数据库表中的所有数据,并按照 price 字段的值进行升序排序,并将排序后的结果返回给 Django 的查询集对象。
根据 order_by() 方法的参数不同,生成的 SQL 查询会相应地变化,以满足你的排序需求。你可以指定单个字段或多个字段进行排序,并可以选择升序或降序排序。
values()
返回一个对象字典的列表,列表的元素是字典,字典内是字段和值的键值对。
values() 方法用于将查询集(QuerySet)中的对象转换为字典形式的值,其中键是模型字段名,值是相应字段的值。这个方法允许你获取模型对象的特定字段值,而不是整个对象本身。
基本用法如下:
values_list = MyModel.objects.values(field1, field2, ...)其中,field1, field2, … 是你想要包含在字典中的字段名。values() 方法将返回一个包含查询集中对象的字段值的字典列表。
你也可以将所有字段的值都包含在结果中,只需简单调用 values() 而不传递任何参数:
values_list = MyModel.objects.values()这将返回一个包含查询集中所有对象的所有字段值的字典列表。
除了获取字段值之外,values() 方法还允许你对结果进行过滤、排序等操作,就像在查询集上使用其他方法一样。例如,你可以先对结果进行过滤,然后再获取特定字段的值:
values_list = MyModel.objects.filter(category='A').values('name', 'age')这将返回一个包含分类为 ‘A’ 的对象的名字和年龄字段值的字典列表。
总的来说,values() 方法用于将查询集中的对象转换为字典形式的值,并返回一个包含字段值的字典列表。这个方法对于获取特定字段的值,并将其用于进一步处理或序列化非常有用。
values() 背后的SQL:
values() 方法用于将查询集中的对象转换为字典形式的值,并返回一个包含字段值的字典列表。当你调用 values() 方法时,Django 会生成相应的 SQL 查询来执行这种转换操作。
下面是一个示例模型和使用 values() 方法的示例:
class MyModel(models.Model):
name = models.CharField(max_length=100)
category = models.CharField(max_length=50)
price = models.DecimalField(max_digits=10, decimal_places=2)
## 使用 values() 方法获取字段值
values_list = MyModel.objects.values('name', 'price')在这个示例中,values() 方法用于获取模型 MyModel 中每个对象的 name 和 price 字段的值。当你执行这个查询时,Django 将生成以下类似的 SQL 查询:
SELECT name, price FROM app_mymodel;这条 SQL 查询会选取指定模型 MyModel 对应的数据库表中的所有数据,并仅返回 name 和 price 字段的值,并将其转换为字典形式的列表返回给 Django 的查询集对象。
根据 values() 方法的参数不同,生成的 SQL 查询会相应地变化,以满足你的需要获取的字段值。
示例:取列(投影)
values() 放到后面是投影
在 Django ORM 中,values() 方法用于指定查询结果所需返回的字段,即进行投影操作。当 values() 方法放在查询链的最后,它将选择指定的字段,并返回包含这些字段的字典列表。
下面是一个示例,演示了如何在查询中使用 values() 方法进行投影操作:
假设有一个模型 Transaction,其中包含了用户的交易记录,包括用户ID、交易金额等字段:
from django.db import models
class Transaction(models.Model):
user_id = models.IntegerField()
amount = models.DecimalField(max_digits=10, decimal_places=2)
date = models.DateField()现在,假设我们只需要获取每个用户的ID和总交易金额,并且不需要其他字段。我们可以将 values() 方法放在查询链的最后,以进行投影操作:
from django.db.models import Sum
## 进行投影操作,只选择'user_id'和'total_amount'字段
queryset = Transaction.objects.annotate(
total_amount=Sum('amount')
).values('user_id', 'total_amount')
## 打印结果
for data in queryset:
print("User ID:", data['user_id'])
print("Total Amount:", data['total_amount'])
print()在这个示例中,values('user_id', 'total_amount') 指定了只选择 'user_id' 和 'total_amount' 字段进行投影操作。因此,查询结果将只包含这两个字段,并返回一个字典列表,每个字典代表一个对象,包含所选择的字段及其对应的值。
通过这种方式,我们可以灵活地选择所需的字段进行查询,并得到相应的投影结果。
示例:分组
values() 放到前面是group by
注意:分组只能通过annotate() 方法,aggregate() 不可以实现分组。
-
是的,对于在 Django ORM 中对查询集进行分组聚合操作,通常只能使用
annotate()方法,而不能使用aggregate()方法。、 -
aggregate()方法用于对整个查询集进行聚合操作,返回一个包含聚合结果的字典。它不会按照某个字段的值进行分组,而是将所有对象的相关字段值汇总为一个值(如总和、平均值、最大值等)。因此,aggregate()方法适用于获取整个数据集的总体聚合结果,而不是对数据进行分组聚合。 -
相反,
annotate()方法用于对每个对象添加聚合函数的计算结果作为新的字段,并返回包含这些新字段的查询集。这允许你在每个对象上访问聚合函数的计算结果,而不是仅仅获取一个总体的聚合结果。通过结合values()方法,annotate()方法可以实现对数据进行分组,并对每个分组进行聚合计算。因此,通常情况下,如果需要对数据进行分组聚合操作,应该使用
annotate()方法。
在 Django ORM 中,虽然 values() 方法通常用于指定查询结果所需返回的字段,即进行投影操作,但是当 values() 方法放在查询链的最前面时,它还可以与 annotate() 方法结合实现分组聚合操作,类似于 SQL 中的 GROUP BY 功能。
下面是一个示例,演示了如何在查询中使用 values() 方法结合 annotate() 方法实现分组聚合操作:
假设有一个模型 Transaction,其中包含了用户的交易记录,包括用户ID、交易金额等字段:
from django.db import models
class Transaction(models.Model):
user_id = models.IntegerField()
amount = models.DecimalField(max_digits=10, decimal_places=2)
date = models.DateField()现在,假设我们想要按照用户ID分组,并计算每个用户的总交易金额。我们可以将 values('user_id') 方法放在查询链的最前面,结合 annotate() 方法进行分组聚合操作:
from django.db.models import Sum
## 按用户ID分组,并计算每个用户的总交易金额
queryset = Transaction.objects.values('user_id').annotate(
total_amount=Sum('amount')
)
## 打印结果
for data in queryset:
print("User ID:", data['user_id'])
print("Total Amount:", data['total_amount'])
print()在这个示例中,values('user_id') 将指定了按照 user_id 字段进行分组,然后 annotate() 方法结合 Sum() 函数对每个分组进行聚合计算,计算了每个用户的总交易金额,并将计算结果添加到每个分组中。
通过这种方式,我们可以实现对数据进行分组聚合操作,类似于 SQL 中的 GROUP BY 功能。
1
在Django中,annotate() 方法结合 values() 方法可以实现分组聚合操作。这样可以根据某个字段的值进行分组,并对每个分组进行聚合计算,然后将聚合结果作为新的字段添加到每个对象中。
下面是一个示例,演示了如何使用 annotate() 方法实现分组聚合:
假设有一个模型 Transaction,其中包含了用户的交易记录,包括用户ID、交易金额等字段:
from django.db import models
class Transaction(models.Model):
user_id = models.IntegerField()
amount = models.DecimalField(max_digits=10, decimal_places=2)现在,假设我们想要按照用户ID分组,并计算每个用户的总交易金额和交易次数。
我们可以使用 values() 方法指定需要分组的字段(用户ID),然后结合 annotate() 方法进行聚合计算:
from django.db.models import Sum, Count
## 按用户ID分组,并计算每个用户的总交易金额和交易次数
queryset = Transaction.objects.values('user_id').annotate(
total_amount=Sum('amount'),
transaction_count=Count('id')
)
## 打印结果
for data in queryset:
print("User ID:", data['user_id'])
print("Total Amount:", data['total_amount'])
print("Transaction Count:", data['transaction_count'])
print()在这个示例中,values('user_id') 指定了按照 user_id 字段进行分组,然后 annotate() 方法结合 Sum() 和 Count() 函数对每个分组进行聚合计算,分别计算了总交易金额和交易次数,并将计算结果作为新的字段添加到每个分组中。
这样,我们就可以得到每个用户的总交易金额和交易次数,并且这些信息会被添加到对应的对象中,以便后续使用。
返回单个值的方法
get()
get() 方法用于从数据库中获取满足指定条件的单个对象。它期望找到且仅找到一个符合条件的对象,如果找到多个或者没有找到对象,它将抛出异常。
基本用法如下:
obj = MyModel.objects.get(field_name=value)其中,field_name 是模型中的字段名,value 是要匹配的值。get() 方法将返回一个满足条件的对象,如果没有找到满足条件的对象,或者找到多个满足条件的对象,它将抛出 MyModel.DoesNotExist 或 MyModel.MultipleObjectsReturned 异常。
get() 方法常用于获取单个对象,如通过主键获取对象,或者通过唯一字段获取对象。例如,如果你有一个模型 Person,其中有一个名为 id 的主键字段,你可以通过 get() 方法获取指定 id 的对象:
person = Person.objects.get(id=1)或者,如果你有一个模型 Product,其中有一个唯一的字段 sku,你可以通过 get() 方法获取指定 sku 的产品对象:
product = Product.objects.get(sku='ABC123')需要注意的是,如果使用 get() 方法时找不到对象或找到多个对象,它将引发异常。因此,在使用 get() 方法时,要确保条件能够精确地匹配到一个对象。
get() 背后的SQL:
get() 方法用于从数据库中获取满足指定条件的单个对象。当你调用 get() 方法时,Django 会生成相应的 SQL 查询来执行这个操作。
下面是一个示例模型和使用 get() 方法的示例:
class MyModel(models.Model):
name = models.CharField(max_length=100)
category = models.CharField(max_length=50)
price = models.DecimalField(max_digits=10, decimal_places=2)
## 使用 get() 方法获取单个对象
object = MyModel.objects.get(name='Product A')在这个示例中,get() 方法用于获取模型 MyModel 中 name 字段值为 'Product A' 的单个对象。当你执行这个查询时,Django 将生成以下类似的 SQL 查询:
SELECT * FROM app_mymodel WHERE name = 'Product A';这条 SQL 查询会选取指定模型 MyModel 对应的数据库表中 name 字段值为 'Product A' 的单个对象,并将其返回给 Django 的查询集对象。
需要注意的是,get() 方法期望找到且仅找到一个满足条件的对象。如果找到多个或者没有找到对象,它将抛出异常。因此,生成的 SQL 查询是基于唯一性条件进行过滤的。
count()
count() 方法用于计算查询集(QuerySet)中对象的数量。它返回一个表示查询集中对象数量的整数。
基本用法如下:
count = MyModel.objects.count()这将返回模型 MyModel 中对象的数量。你也可以在查询集上应用过滤条件,然后计算满足条件的对象的数量:
count = MyModel.objects.filter(field_name=value).count()其中,field_name 是模型中的字段名,value 是要匹配的值。这将返回满足指定条件的对象的数量。
count() 方法通常用于确定查询集中的对象数量,以便在视图中进行分页或生成汇总信息时使用。它是一个高效的方法,因为它只返回对象的数量,而不会实际获取对象本身。
count() 背后的SQL:
count() 方法用于计算查询集(QuerySet)中对象的数量。当你调用 count() 方法时,Django 会生成相应的 SQL 查询来执行计数操作。
下面是一个示例模型和使用 count() 方法的示例:
class MyModel(models.Model):
name = models.CharField(max_length=100)
category = models.CharField(max_length=50)
price = models.DecimalField(max_digits=10, decimal_places=2)
## 使用 count() 方法计算对象数量
count = MyModel.objects.count()在这个示例中,count() 方法用于计算模型 MyModel 中对象的数量。当你执行这个查询时,Django 将生成以下类似的 SQL 查询:
SELECT COUNT(*) FROM app_mymodel;这条 SQL 查询会对指定模型 MyModel 对应的数据库表中的所有数据进行计数,并返回结果给 Django,即查询集对象的 count 方法。
值得注意的是,count() 方法执行的是数据库层面的计数操作,而不是在 Python 中对查询集对象进行遍历计数。因此,它是一个高效的方法,尤其适用于大型数据集。
first()
first() 方法用于从查询集(QuerySet)中获取第一个对象。它返回满足查询条件的第一个对象,如果查询集为空,则返回 None。
基本用法如下:
first_object = MyModel.objects.first()这将返回模型 MyModel 中的第一个对象。如果查询集为空,则 first_object 将为 None。
first() 方法通常用于获取查询集中的第一个对象,而不需要关心查询集中是否有其他对象。它可以用于快速获取单个对象的信息,例如在模板中显示第一个对象的详情。
first() 背后的SQL:
first() 方法用于从查询集(QuerySet)中获取第一个对象。当你调用 first() 方法时,Django 会生成相应的 SQL 查询来执行获取操作。
下面是一个示例模型和使用 first() 方法的示例:
class MyModel(models.Model):
name = models.CharField(max_length=100)
category = models.CharField(max_length=50)
price = models.DecimalField(max_digits=10, decimal_places=2)
## 使用 first() 方法获取第一个对象
first_object = MyModel.objects.first()在这个示例中,first() 方法用于获取模型 MyModel 中的第一个对象。当你执行这个查询时,Django 将生成以下类似的 SQL 查询:
SELECT * FROM app_mymodel LIMIT 1;这条 SQL 查询会选取指定模型 MyModel 对应的数据库表中的第一个对象,并将其返回给 Django 的查询集对象。
first() 方法执行的是数据库层面的操作,它会返回查询集中的第一个对象,而不会获取整个查询集的数据。因此,它是一个高效的方法,尤其适用于大型数据集。
last()
last() 方法用于从查询集(QuerySet)中获取最后一个对象。它返回满足查询条件的最后一个对象,如果查询集为空,则返回 None。
基本用法如下:
last_object = MyModel.objects.last()这将返回模型 MyModel 中的最后一个对象。如果查询集为空,则 last_object 将为 None。
last() 方法通常用于获取查询集中的最后一个对象,而不需要关心查询集中是否有其他对象。它可以用于快速获取单个对象的信息,例如在模板中显示最后一个对象的详情。
last() 背后的SQL:
last() 方法用于从查询集(QuerySet)中获取最后一个对象。当你调用 last() 方法时,Django 会生成相应的 SQL 查询来执行获取操作。
下面是一个示例模型和使用 last() 方法的示例:
class MyModel(models.Model):
name = models.CharField(max_length=100)
category = models.CharField(max_length=50)
price = models.DecimalField(max_digits=10, decimal_places=2)
## 使用 last() 方法获取最后一个对象
last_object = MyModel.objects.last()在这个示例中,last() 方法用于获取模型 MyModel 中的最后一个对象。当你执行这个查询时,Django 将生成以下类似的 SQL 查询:
SELECT * FROM app_mymodel ORDER BY id DESC LIMIT 1;这条 SQL 查询会选取指定模型 MyModel 对应的数据库表中的最后一个对象,并将其返回给 Django 的查询集对象。
需要注意的是,last() 方法需要对查询集进行排序操作,以便获取最后一个对象。默认情况下,它会按照模型的主键字段(通常是 id)进行降序排序。
exists()
在Django中,exists() 方法用于检查查询集(QuerySet)是否包含至少一个对象。它返回一个布尔值,如果查询集中至少包含一个对象,则返回 True,否则返回 False。
基本用法如下:
if MyModel.objects.filter(field_name=value).exists():
# 查询集中至少有一个对象满足条件
# 在这里执行相应的逻辑
else:
# 查询集中没有对象满足条件
# 在这里执行相应的逻辑这个例子中,exists() 方法检查是否存在满足指定条件的对象。如果至少有一个对象满足条件,则执行 if 语句块中的逻辑,否则执行 else 语句块中的逻辑。
exists() 方法通常用于检查是否存在满足特定条件的对象,以便在后续代码中根据结果执行不同的逻辑。它是一个效率很高的方法,因为它只会检查是否存在对象,而不会实际获取对象的数据。
exists() 背后的SQL:
exists() 方法用于检查查询集(QuerySet)中是否存在至少一个对象。当你调用 exists() 方法时,Django 会生成相应的 SQL 查询来执行这个检查操作。
下面是一个示例模型和使用 exists() 方法的示例:
class MyModel(models.Model):
name = models.CharField(max_length=100)
category = models.CharField(max_length=50)
price = models.DecimalField(max_digits=10, decimal_places=2)
## 使用 exists() 方法检查是否存在对象
if MyModel.objects.filter(category='A').exists():
# 执行逻辑...在这个示例中,exists() 方法用于检查模型 MyModel 中是否存在 category 字段值为 'A' 的对象。当你执行这个查询时,Django 将生成以下类似的 SQL 查询:
SELECT EXISTS (SELECT 1 FROM app_mymodel WHERE category = 'A' LIMIT 1);这条 SQL 查询会在数据库中检查是否存在满足条件的对象,并返回 True 或 False。
需要注意的是,exists() 方法是一个高效的方法,因为它只会检查是否存在对象,而不会实际获取对象的数据。这使得它成为在判断查询集是否为空时的常用方法。
与或非
## AND,同时满足两个条件
result = mgr.filter(StuID__gt=6, StuID__lt=8)
print(result) # <QuerySet [<Employee: 7 Xi Ren 19>]>
## OR,满足其中一个条件即可
result = mgr.filter(StuID__in=[1, 2, 3])
print(result) # <QuerySet [<Employee: 1 Shi Zhongyu 22>, <Employee: 2 Shi Potian 22>, <Employee: 3 Xie Yanke 53>]>
## NOT
由filter改用exclude是最简单的方式,也可使用Q对象中的~运算符取反Q 对象
在Django中,Q 对象是用于构建复杂查询条件的对象。它允许你使用逻辑运算符(AND、OR、NOT)来组合多个查询条件,以实现更灵活和复杂的查询。
基本上,Q 对象是通过在模型的查询中使用逻辑运算符来构建条件查询的工具。它允许你将多个查询条件组合在一起,以构建更复杂的查询语句。通常,Q 对象与 filter() 方法一起使用。
下面是一个简单的示例,演示了如何使用 Q 对象:
from django.db.models import Q
## 构建查询条件
query = Q(name__icontains='john') | Q(age__gte=18)
## 使用 Q 对象进行过滤
results = MyModel.objects.filter(query)在这个示例中,首先导入了 Q 对象,然后创建了两个查询条件:一个是检查 name 字段是否包含 “john”(不区分大小写),另一个是检查 age 字段是否大于或等于 18。然后,使用逻辑运算符 |(OR)将这两个条件组合在一起,形成一个复合条件。最后,将这个复合条件传递给 filter() 方法,以获取满足条件的对象。
Q 对象可以使用以下逻辑运算符进行组合:
&(AND):逻辑与运算符,用于组合多个条件,要求所有条件都满足。|(OR):逻辑或运算符,用于组合多个条件,要求至少一个条件满足。~(NOT):逻辑非运算符,用于对条件取反。
通过使用 Q 对象,你可以更加灵活地构建复杂的查询语句,以满足各种不同的查询需求。
下面是每个逻辑运算符的使用示例:
逻辑与运算符(&)示例:
from django.db.models import Q
## 构建查询条件:同时满足两个条件
query = Q(name__icontains='john') & Q(age__gte=18)
## 使用 Q 对象进行过滤
results = MyModel.objects.filter(query)在这个示例中,query 变量包含了两个条件:检查 name 字段是否包含 “john”(不区分大小写)并且 age 字段是否大于或等于 18。
逻辑或运算符(|)示例:
from django.db.models import Q
## 构建查询条件:满足其中一个条件即可
query = Q(name__icontains='john') | Q(name__icontains='jane')
## 使用 Q 对象进行过滤
results = MyModel.objects.filter(query)在这个示例中,query 变量包含了两个条件:检查 name 字段是否包含 “john”(不区分大小写)或者是否包含 “jane”(不区分大小写)。
逻辑非运算符(~)示例:
from django.db.models import Q
## 构建查询条件:不满足指定条件
query = ~Q(name__icontains='john')
## 使用 Q 对象进行过滤
results = MyModel.objects.filter(query)在这个示例中,query 变量包含了一个条件:检查 name 字段不包含 “john”(不区分大小写)的对象。
使用聚合函数
除使用count()方法外,还可使用aggregate()、annotate()函数来使用SQL中的其他聚合函数
aggregate()
aggregate() 方法用于执行聚合函数,例如求和、计数、平均值等。它允许你对查询集中的对象进行聚合操作,并返回聚合结果(返回字典,默认key命名为字段名__聚合函数名)。
下面是一些常用的聚合函数以及它们的使用示例:
from django.db.models import Sum, Count, Avg, Max, Min
## 求和,在这个示例中,`total_sales` 变量将包含 `sales_amount` 字段的总和。
total_sales = MyModel.objects.aggregate(total_sales=Sum('sales_amount'))
## 计数,在这个示例中,`total_customers` 变量将包含 `customer_id` 字段的数量。
total_customers = MyModel.objects.aggregate(total_customers=Count('customer_id'))
## 平均值,在这个示例中,`average_rating` 变量将包含 `rating` 字段的平均值。
average_rating = MyModel.objects.aggregate(average_rating=Avg('rating'))
## 最大值,在这个示例中,`highest_price` 变量将包含 `price` 字段的最大值。
highest_price = MyModel.objects.aggregate(highest_price=Max('price'))
## 最小值,在这个示例中,`lowest_price` 变量将包含 `price` 字段的最小值。
lowest_price = MyModel.objects.aggregate(lowest_price=Min('price'))示例(不额外为返回值命名):
result = mgr.aggregate(Sum('Age'), Count('StuID'), Avg('Age'), Max('Age'), Min('Age'))
print(result) # {'Age__sum': 685, 'StuID__count': 25, 'Age__avg': 27.4, 'Age__max': 100, 'Age__min': 17}
'''
SELECT
SUM( `students`.`Age` ) AS `Age__sum`,
COUNT( `students`.`StuID` ) AS `StuID__count`,
AVG( `students`.`Age` ) AS `Age__avg`,
MAX( `students`.`Age` ) AS `Age__max`,
MIN( `students`.`Age` ) AS `Age__min`
FROM
`students`;
'''annotate()(分组)
annotate() 方法用于对每个对象添加聚合函数的计算结果作为新的字段,并返回包含这些新字段的查询集(QuerySet)。这允许你在每个对象上访问聚合函数的计算结果,而不是仅仅获取一个总体的聚合结果。
返回查询集,元素是对象。用聚合函数,聚合函数会分组,没有指定分组则使用pk分组,行行分组。(对主键分组一般无意义,因此通常需要用values来指定分组字段)
下面是一些常用的聚合函数以及它们如何使用 annotate() 方法:
from django.db.models import Sum, Count, Avg, Max, Min
## 求和并添加新字段,在这个示例中,`total_sales` 将会是每个对象的 `sales_amount` 字段的总和。
queryset = MyModel.objects.annotate(total_sales=Sum('sales_amount'))
## 计数并添加新字段,在这个示例中,`total_customers` 将会是每个对象的 `customer_id` 字段的数量。
queryset = MyModel.objects.annotate(total_customers=Count('customer_id'))
## 平均值并添加新字段,在这个示例中,`average_rating` 将会是每个对象的 `rating` 字段的平均值。
queryset = MyModel.objects.annotate(average_rating=Avg('rating'))
## 最大值并添加新字段,在这个示例中,`highest_price` 将会是每个对象的 `price` 字段的最大值。
queryset = MyModel.objects.annotate(highest_price=Max('price'))
## 最小值并添加新字段,在这个示例中,`lowest_price` 将会是每个对象的 `price` 字段的最小值。
queryset = MyModel.objects.annotate(lowest_price=Min('price'))示例(不额外为返回值命名):
## 通过Gender分组,取各分组的Age总和
result = mgr.values('Gender').annotate(Sum('Age'))
print(result) # <QuerySet [{'Gender': 'M', 'Age__sum': 495}, {'Gender': 'F', 'Age__sum': 190}]>
'''
SELECT
`students`.`Gender`,
SUM( `students`.`Age` ) AS `Age__sum`
FROM
`students`
GROUP BY
`students`.`Gender`
'''
## 后面的values表示仅取Gender列(实际上无意义,仅作为values的使用演示)
result = mgr.values('Gender').annotate(Sum('Age')).values('Gender')
print(result) # <QuerySet [{'Gender': 'M'}, {'Gender': 'F'}]>
'''
SELECT
`students`.`Gender`
FROM
`students`
GROUP BY
`students`.`Gender`
ORDER BY
'''除了使用 print() 函数打印结果外,result 变量作为一个查询集(QuerySet)还可以进行许多其他常见操作,包括:
result = mgr.values('Gender').annotate(Sum('Age'))
print(result)
'''
<QuerySet [{'Gender': 'M', 'Age__sum': 495}, {'Gender': 'F', 'Age__sum': 190}]>
'''
## 迭代访问每个结果,使用 for 循环迭代访问查询集中的每个结果,并对结果进行处理。
for item in result:
print(item)
print(item['Gender'], item['Age__sum'])
print('-' * 30)
'''
{'Gender': 'M', 'Age__sum': 495}
M 495
------------------------------
{'Gender': 'F', 'Age__sum': 190}
F 190
------------------------------
'''两者区别
在 Django ORM 中,aggregate() 和 annotate() 方法都用于聚合数据,但它们之间有着不同的作用和行为:
aggregate()方法:- 用于对整个查询集进行聚合操作,返回一个包含聚合结果的字典。
- 返回的结果是一个单一的值或者值的集合,而不是与原始对象集相关联的新字段。
- 聚合操作会将所有对象的相关字段值汇总为一个值(如总和、平均值、最大值等)。
- 适合于需要获取整个数据集的总体聚合结果的情况。
from django.db.models import Sum
total_sales = MyModel.objects.aggregate(total_sales=Sum('sales_amount'))annotate()方法:- 用于对每个对象添加聚合函数的计算结果作为新的字段,并返回包含这些新字段的查询集。
- 返回的结果是一个包含了原始对象以及聚合结果字段的查询集。
- 聚合操作会对每个对象进行计算,并将计算结果作为一个新字段添加到每个对象中。
- 适合于需要在每个对象级别上访问聚合函数的计算结果的情况。
from django.db.models import Count
queryset = MyModel.objects.annotate(total_customers=Count('customer_id'))所以,aggregate() 方法用于整体汇总数据,而 annotate() 方法用于在每个对象级别上添加聚合计算结果。
一对多查询
在 Django 中,一对多关系是指一个模型与另一个模型之间的关系,其中一个模型中的一个对象可以关联另一个模型中的多个对象。通常在这种关系中,父模型(“一”)使用 ForeignKey 字段与子模型(“多”)关联。通过一对多关系,你可以方便地查询和操作相关联的对象。
创建一对多关系
首先,我们来创建一个简单的一对多关系。假设我们有一个模型 Author 表示作者,另一个模型 Book 表示书籍,并且一个作者可以有多本书。
from django.db import models
class Author(models.Model):
name = models.CharField(max_length=100)
class Book(models.Model):
title = models.CharField(max_length=100)
author = models.ForeignKey(Author, on_delete=models.CASCADE, related_name='books')在这个例子中,Book 模型中有一个 ForeignKey 字段 author,它与 Author 模型关联,表示每本书都有一个作者。同时,related_name='books' 设置了一个反向关系名称,这样可以从 Author 对象访问关联的 Book 对象。
示例
from django.db import models
class Classes(models.Model):
class Meta:
db_table = 'classes'
ClassID = models.AutoField(primary_key=True)
Class = models.CharField(null=False, max_length=100)
NumOfStu = models.SmallIntegerField(null=True)
def __repr__(self):
return "<Classes: {} {}>".format(
self.ClassID, self.Class
)
__str__ = __repr__
class Students(models.Model):
class Meta:
db_table = 'students'
StuID = models.AutoField(primary_key=True)
Name = models.CharField(null=False, max_length=50)
Age = models.IntegerField(null=False)
Gender = models.SmallIntegerField(null=False)
ClassID = models.ForeignKey(Classes, on_delete=models.CASCADE, db_column='ClassID')
TeacherID = models.IntegerField(null=True)
def __repr__(self):
return "<Students: {} {} {} {}>".format(
self.StuID,
self.Name,
self.Age,
self.ClassID_id # 如果只写ClassID,返回的将是Classes的ClassID对象
)
__str__ = __repr__
from django.db import models
class Classes(models.Model):
class Meta:
db_table = 'classes'
ClassID = models.AutoField(primary_key=True)
Class = models.CharField(null=False, max_length=100)
NumOfStu = models.SmallIntegerField(null=True)
def __repr__(self):
return "<Classes: {} {}>".format(
self.ClassID, self.Class
)
__str__ = __repr__
class Students(models.Model):
class Meta:
db_table = 'students'
StuID = models.AutoField(primary_key=True)
Name = models.CharField(null=False, max_length=50)
Age = models.IntegerField(null=False)
Gender = models.SmallIntegerField(null=False)
ClassID = models.ForeignKey(Classes, on_delete=models.CASCADE, db_column='ClassID')
TeacherID = models.IntegerField(null=True)
def __repr__(self):
return "<Students: {} {} {} {}>".format(
self.StuID,
self.Name,
self.Age,
self.ClassID_id # 如果只写ClassID,返回的将是Classes的ClassID对象
)
__str__ = __repr__ClassID = models.ForeignKey(Classes, on_delete=models.CASCADE, db_column='ClassID')
- 该外键默认引用Classes的主键;
db_column用于指定表中字段的实际名称,如不加该选项,则默认为ClassID_id(在实际字段名后加__id);
查询一对多关系
- 从父模型查询子模型:
通过
related_name(在这个例子中为books),你可以从Author查询其关联的Book对象。
author = Author.objects.get(name='J.K. Rowling')
books = author.books.all() # 获取该作者的所有书籍- 从子模型查询父模型:
你可以通过
ForeignKey字段来查询关联的父模型。
book = Book.objects.get(title='Harry Potter and the Philosopher\'s Stone')
author = book.author # 获取该书籍的作者- 过滤子模型:
可以使用
filter()方法来根据某个条件过滤关联的子模型。
authors_with_most_books = Author.objects.annotate(book_count=Count('books')).filter(book_count__gt=5)示例
查询ClassID为1的学生信息
要查询 ClassID 为 1 的学生信息,你可以使用 Django ORM 的 filter() 方法来筛选符合条件的学生对象。filter() 方法用于根据指定条件筛选查询集。在这个例子中,你可以通过 Students 模型中的 ClassID 字段过滤学生信息。
这里是查询 ClassID 为 1 的学生信息的示例代码:
## 获取所有 `ClassID` 为 1 的学生信息
students_in_class_1 = Students.objects.filter(ClassID=1)
## 输出这些学生的信息
for student in students_in_class_1:
print(student) # 或者使用其他方式查看学生信息在这个例子中,我们使用 filter(ClassID=1) 来获取 ClassID 为 1 的所有学生。然后,我们可以迭代结果并输出每个学生的信息。
其他查询方式
此外,你还可以使用其他查询方法来检索相关数据:
-
通过 ForeignKey 反向关系查询: 如果有反向关系(例如
related_name),可以从Classes模型中访问相关学生:# 获取 `ClassID` 为 1 的班级 class_1 = Classes.objects.get(ClassID=1) # 通过反向关系访问相关的学生 students_in_class_1 = class_1.students_set.all() # 使用默认 `related_name` -
过滤多个条件: 你还可以在过滤中使用多个条件来进一步筛选结果:
# 获取 `ClassID` 为 1 且年龄大于 18 的学生 students_in_class_1 = Students.objects.filter(ClassID=1, Age__gt=18)
这些方法展示了如何在 Django ORM 中查询和过滤一对多关系中的数据,并根据需要进一步筛选结果。
反向关系查询
在 Django 中,ForeignKey 字段用于创建一对多关系。它表示模型中的一个对象可以关联另一个模型中的多个对象。ForeignKey 字段本身位于 “多” 的那一端,而 “一” 的那一端是关联的模型。
“一” 端的模型可以通过反向关系访问与其关联的 “多” 端对象。在 Django 中,这种反向关系的名称可以通过 related_name 指定。如果没有指定 related_name,Django 将使用默认名称(模型名小写 + _set)。
以下是如何使用反向关系查询的详细解释和示例:
定义模型和关系
假设我们有两个模型:Classes 和 Students。其中,Students 与 Classes 有一对多关系,一个班级(Classes)可以有多个学生(Students)。
from django.db import models
## 定义班级模型
class Classes(models.Model):
ClassID = models.AutoField(primary_key=True)
ClassName = models.CharField(max_length=100)
## 定义学生模型
class Students(models.Model):
StuID = models.AutoField(primary_key=True)
Name = models.CharField(max_length=50)
ClassID = models.ForeignKey(Classes, on_delete=models.CASCADE, related_name='students') # 使用 related_name在这个例子中,Students 模型中的 ClassID 字段是一个 ForeignKey,关联到 Classes 模型。我们设置 related_name='students' 来指定反向关系的名称,这样从 Classes 模型可以方便地访问其关联的学生。
使用反向关系查询
有了 related_name,我们可以从 Classes 模型访问与其关联的学生列表:
## 获取班级 ID 为 1 的班级
class_1 = Classes.objects.get(ClassID=1)
## 使用反向关系访问这个班级的所有学生
students_in_class_1 = class_1.students.all() # 使用 related_name 'students'
## 打印学生信息
for student in students_in_class_1:
print(student.Name)在这个例子中,通过 class_1.students.all(),我们访问了这个班级关联的所有学生。因为使用了 related_name='students',所以反向关系的名称为 students。
如果没有指定 related_name,Django 会使用默认名称 students_set。因此,在这种情况下,访问关联的学生将是 class_1.students_set.all()。
过滤反向关系中的对象
你还可以使用 filter() 方法在反向关系上进行过滤,以获得特定的结果:
## 获取班级 ID 为 1 中年龄大于 18 的学生
students_above_18 = class_1.students.filter(Age__gt=18)
for student in students_above_18:
print(student.Name)通过这种方式,你可以方便地使用反向关系来查询和操作一对多关系中的相关对象。
级联删除
当父模型中的对象被删除时,Django 允许你指定关联的子模型的行为。on_delete 参数控制这种行为:
CASCADE: 如果父模型对象被删除,相关的子模型对象也会被删除。SET_NULL: 如果父模型对象被删除,相关的子模型对象的ForeignKey字段将被设置为None。PROTECT: 如果父模型对象被删除,Django 将阻止删除操作。- 其他选项如
SET_DEFAULT和DO_NOTHING。
例如,使用 on_delete=models.CASCADE 意味着当 Author 对象被删除时,关联的 Book 对象也会被删除。
通过以上这些方法,你可以深入理解 Django 中的一对多关系及其查询方式,并根据需求来使用它们。
distinct() 与子查询
在 Django ORM 中,distinct() 和子查询是用于处理复杂查询的强大工具。下面将分别详细解释这两个概念。
distinct()
distinct() 方法用于从查询集中消除重复的结果。它确保返回的结果集中的每个记录都是唯一的。
基本使用
在最简单的情况下,distinct() 可以确保查询结果中没有重复项。例如,假设有一个模型 Transaction,其中包含了重复的客户 ID:
from django.db import models
class Transaction(models.Model):
customer_id = models.IntegerField()
amount = models.DecimalField(max_digits=10, decimal_places=2)为了获取唯一的客户 ID,可以使用 distinct():
unique_customers = Transaction.objects.values('customer_id').distinct()结合 order_by()
当 distinct() 与 order_by() 结合使用时,结果的唯一性取决于 order_by() 中的字段。如果 order_by() 包含不同的字段组合,可能会影响结果的唯一性。
unique_customers = Transaction.objects.values('customer_id', 'amount').distinct()这个例子会确保 (customer_id, amount) 组合是唯一的,而不是仅 customer_id。
子查询
子查询是一种在查询中嵌套另一查询的技术,用于解决复杂的查询需求。在 Django 中,子查询通常通过 Subquery 来实现。
子查询示例
假设有一个模型 Order 和另一个模型 OrderItem。我们想要找到每个订单的最新订单项。可以使用子查询来实现。
from django.db import models
from django.db.models import Subquery, OuterRef
class Order(models.Model):
order_date = models.DateTimeField()
class OrderItem(models.Model):
order = models.ForeignKey(Order, on_delete=models.CASCADE)
item_name = models.CharField(max_length=100)
created_at = models.DateTimeField()为了获取每个订单的最新订单项,我们可以使用子查询来查找 OrderItem 的最大日期,然后结合 OuterRef 来关联外部查询。
latest_order_item_subquery = OrderItem.objects.filter(
order=OuterRef('pk')
).order_by('-created_at').values('item_name')[:1]
orders_with_latest_item = Order.objects.annotate(
latest_item=Subquery(latest_order_item_subquery)
)在这个例子中,OuterRef 指代外部查询的 pk 字段,Subquery 则表示子查询,order_by('-created_at')[:1] 表示选择最新的订单项。
结合使用 distinct() 和子查询
在某些情况下,可能需要结合 distinct() 和子查询。例如,要找到每个客户的最后一笔交易:
latest_transaction_subquery = Transaction.objects.filter(
customer_id=OuterRef('customer_id')
).order_by('-date').values('amount')[:1]
unique_customers_with_latest_transaction = Transaction.objects.values(
'customer_id'
).annotate(
latest_transaction=Subquery(latest_transaction_subquery)
).distinct()这个示例首先使用 distinct() 确保结果中没有重复的客户,然后使用子查询查找每个客户的最新交易。
通过以上详细解释和示例,你可以理解 Django 中 distinct() 的应用以及子查询的用法,并且知道如何结合使用这两者来解决复杂的查询需求。
raw()
Django 的 raw() 方法允许你执行原始 SQL 查询并将结果映射到 Django 模型对象。它是 Django ORM 提供的与数据库直接交互的方式之一,适用于在复杂查询无法通过 Django ORM 实现时直接使用 SQL 查询。
使用 raw() 执行 SQL 查询
raw() 方法可以在 QuerySet 上调用,并接受一个 SQL 查询字符串。你可以将 SQL 查询中的参数替换为 Django 中的占位符,如 %s。这在避免 SQL 注入时非常有用。
以下是一些示例,展示了 raw() 的使用:
基本用法
from django.db import models
class Employee(models.Model):
name = models.CharField(max_length=100)
department = models.CharField(max_length=100)
## 使用原始 SQL 查询
employees_in_sales = Employee.objects.raw("SELECT * FROM myapp_employee WHERE department = 'Sales'")在这个示例中,raw() 接受 SQL 查询字符串,并返回一个查询集。尽管它返回的是查询集,但由于是使用原始 SQL 查询,不能像通常的 Django ORM 查询集那样使用。
使用占位符避免 SQL 注入
为了避免 SQL 注入,使用 raw() 时最好通过参数化查询或占位符传递参数,而不是直接将值拼接到 SQL 字符串中。
department_name = 'Sales'
employees_in_sales = Employee.objects.raw("SELECT * FROM myapp_employee WHERE department = %s", [department_name])复杂查询
如果有非常复杂的 SQL 查询,可能包含子查询、联合、分组等,raw() 是一个强有力的工具。下面是一个例子,展示了使用原始 SQL 查询实现更复杂的操作。
## 获取每个部门员工的平均工资,并显示部门名称和平均工资
average_salary_per_dept = Employee.objects.raw(
"""
SELECT department, AVG(salary) as avg_salary
FROM myapp_employee
GROUP BY department
"""
)注意事项
- 结果映射:
raw()查询的结果需要与模型结构匹配。即使查询返回额外的字段,它们也会被忽略。缺少必需字段可能导致错误。 - 无法链式调用:使用
raw()的查询不能与其他 Django ORM 方法链式调用,如filter()或order_by()。 - 事务处理:确保在使用
raw()时考虑事务处理,特别是在执行可能影响数据的 SQL 查询时。
何时使用 raw()
在以下情况下,raw() 是一个有用的工具:
- 当查询过于复杂,无法通过 Django ORM 表达。
- 当查询需要高度定制,或者需要利用数据库的特定功能。
- 当在现有数据库上使用 Django,而这些数据库可能包含非标准结构。
通过这些信息,你应该对 Django 中 raw() 方法的用法和适用场景有更好的理解。记住,在执行原始 SQL 查询时,要小心处理潜在的 SQL 注入问题,并确保查询的结果与模型结构相匹配。