Django基础入门⑩:Django查询数据库操作详讲

Django查询数据库操作基础操作查询数据比较运算符逻辑符号去重查询分组集合:排序查询:分页操作模糊查询多表查询

执行原生 SQL

️️个人简介:以山河作礼。 ️️:Python领域新星创作者,CSDN实力新星认证,阿里云社区专家博主,CSDN内容合伙人 :Web全栈开发专栏:《Web全栈开发》免费专栏,欢迎阅读! :文章末尾扫描二维码可以加入粉丝交流群,不定期免费送书。

Django查询数据库操作

基础操作

添加数据

存储数据需要通过save方法进行存储

# 添加数据1

Book(title='张三',price=222,author=Author.objects.get(id=1)).save()

# 添加数据2

# Book.objects.create(title='张三',price=222,author=Author.objects.get(id=1)).save()

# 打印数据

data = Book.objects.all() # 获取全部数据

for book in data:

print(book.title, book.price, book.author.name)

修改数据

# 修改数据

from datetime import date

book = Book.objects.get(title='张三')

# # book.publish_date = date(2022, 1, 1)

book.price = 555

book.save() # 更新数据,保存到数据库中

删除数据

# 删除数据

book = Book.objects.get(title='张三')

book.delete() # 删除数据

查询数据

查询所有书籍:

books = Book.objects.all()

查询 title 字段为 Python 的书籍(精确匹配):

books = Book.objects.filter(title='Python')

其中 filter() 方法对模型进行过滤。这个方法接受关键字参数作为查询条件,可以进行链式操作。

假设我们有一个名为 Person 的模型,其中有一个属性 age 表示人的年龄,我们可以使用如下代码来查询年龄大于等于 18 岁的人:

results = Person.objects.filter(age__gte=18)

在这个例子中,我们使用 filter() 方法对模型进行过滤,传递给它一个查询条件 age__gte=18,其中 gte表示大于等于查询操作符,18 是年龄筛选条件。这个查询可以返回所有年龄大于等于 18 岁的人。

需要注意的是,如果要对查询结果进行多次条件操作,可以使用链式操作。

例如,如果我们需要查询所有年龄大于等于 18 岁且性别为男的人,可以使用以下代码:

results = Person.objects.filter(age__gte=18, gender='male')

在这个例子中,我们在第一次查询过滤后使用了第二次的查询条件 gender='male',即查询所有性别为男,年龄大于等于 18 岁的人。

需要注意的是,在 filter 查询操作中,需要使用双下划线(__)进行多条件查询,例如 age__gte表示查询大于等于指定年龄,gender__icontains 表示查询包含指定性别(不区分大小写)的记录等等。

查询 rating 不等于 1 的书籍:

results = Book.objects.exclude(rating = 1)

其中的 exclude() 方法对模型进行过滤并排除某些不需要的数据,即排除指定查询条件的结果。这个方法与 filter() 方法一样,接受关键字参数作为查询条件,可以进行链式操作。

假设我们有一个名为 Person 的模型,其中有一个属性 age 表示人的年龄,我们可以使用如下代码来排除所有年龄小于 18 岁的人:

results = Person.objects.exclude(age__lt=18)

在这个例子中,我们使用 exclude() 方法对模型进行过滤,传递给它一个排除条件 age__lt=18,其中 lt 表示小于查询操作符,18 是年龄筛选条件。这个查询可以排除所有年龄小于 18 岁的人。

需要注意的是,如果要对查询结果进行多次排除条件操作,可以使用链式操作。例如,如果我们需要排除所有年龄小于 18 岁且没有指定性别的人,可以使用以下代码:

results = Person.objects.exclude(age__lt=18).exclude(gender=None)

在这个例子中,我们在第一次排除过滤后使用了第二次的排除条件 exclude(gender=None),即排除所有性别未知的年龄小于 18岁的人。

需要注意的是,在 exclude 查询操作中,需要使用双下划线(__)进行多条件查询,例如 age__lt表示查询小于指定年龄,gender__icontains 表示查询包含指定性别(不区分大小写)的记录等等。

exclude() 和 filter()方法区别

exclude() 和 filter() 方法都是在 Django 中对模型进行查询的常用方法,它们的区别在于查询的方向不同。filter() 方法是对模型查询符合条件的记录,即从数据集中选取数据满足查询条件。而exclude()方法则是从模型中排除不符合查询条件的记录,即从数据集中去除数据不满足查询条件。在应用时,filter() 方法通常用于基础查询场合,比如查询某个模型中的所有记录、查询符合某个条件的记录等。exclude()方法通常用于复杂的查询场合,比如查询一个模型中的记录中满足某些条件而排除满足其他条件的记录等。

例如,当我们要查询年龄大于等于 18 岁的人时,我们可以使用 filter() 方法:

results = Person.objects.filter(age__gte=18)

当我们要查询年龄不是 18 岁的人时,则需要使用 exclude() 方法:

results = Person.objects.exclude(age=18)

在这个例子中,我们使用 filter() 方法查询年龄大于等于 18 岁的人,而使用 exclude() 方法排除年龄为 18 岁的人。

比较运算符

比较运算符用于筛选符合条件的数据

比较运算符含义使用方法=等于Person.objects.filter(age=18)!=不等于Person.objects.exclude(age=18)>大于Person.objects.filter(age__gt=18)>=大于等于Person.objects.filter(age__gte=18)<小于Person.objects.filter(age__lt=18)<=小于等于Person.objects.filter(age__lte=18)in在给定的范围内Person.objects.filter(age__in=[18, 19, 20])range在给定的范围内Person.objects.filter(age__range=(18, 20))contains包含指定值Person.objects.filter(name__contains='Jack')icontains包含指定值(不区分大小写)Person.objects.filter(name__icontains='Jack')startswith以指定值开头Person.objects.filter(name__startswith='Jack')istartswith以指定值开头(不区分大小写)Person.objects.filter(name__istartswith='Jack')endswith以指定值结尾Person.objects.filter(name__endswith='Smith')iendswith以指定值结尾(不区分大小写)Person.objects.filter(name__iendswith='Smith')

逻辑符号

在 Django 中进行查询时,通常需要通过逻辑符将查询条件组合在一起。以下是 Django 查询中需要用到的逻辑符,以及它们的含义和使用方式,以 Markdown 表格的形式列出:

逻辑符含义使用方法Q()逻辑或( OR )`Person.objects.filter(Q(age__lt=18)&逻辑与( AND )Person.objects.filter(age__lt=18, gender='male')\~Q()逻辑非( NOT )Person.objects.exclude(~Q(age__lt=18))

逻辑或( OR ):可以使用 Q() 对象联合两个或多个查询条件,实现逻辑或的查询,其中 | 是逻辑或运算符。例如,查询年龄小于 18 岁或性别为男的人可以使用:Person.objects.filter(Q(age__lt=18) | Q(gender='male'))。逻辑与( AND ):可以使用 & 运算符组合多个查询条件,实现逻辑与的查询。例如,查询年龄小于 18 岁且性别为男的人可以使用:Person.objects.filter(age__lt=18, gender='male')。逻辑非( NOT ):可以使用 ~Q() 对象表示查询结果的逆,实现逻辑非的查询,其中 ~ 是逻辑非运算符。例如,查询年龄不是小于 18 岁的人可以使用:Person.objects.exclude(~Q(age__lt=18))。

需要注意的是,当多个逻辑运算符混合使用时,需要使用小括号对查询条件进行分组,以保证查询的顺序正确,例如:

from django.db.models import Q

Person.objects.filter(Q(age__lt=18) | (Q(gender='male') & Q(city='北京')))

在这个例子中,使用小括号将 gender 和 city 的查询条件进行分组,确保查询逻辑正确。

查询 publish_date 在某个时间段以内的书籍:

import datetime

start_date = datetime.date(2021, 1, 1)

end_date = datetime.date(2021, 12, 31)

books = Book.objects.filter(publish_date__range=(start_date, end_date))

values(‘my_field’,‘list_datra’) values获取数据查询集的字段 ,返回字段类型 values_list(‘my_field’,'list_datra) 获取数据查询集的字段 ,返回列表类型

去重查询

from myapp.models import MyModel

# 去重查询,返回去重后的所有 MyModel 对象

distinct_objects = MyModel.objects.all().distinct()

# 去重查询,返回去重后的某个字段的值的集合

distinct_values = MyModel.objects.values('my_field').distinct()

分组集合:

annotate() 方法进行分组集合查询。

from django.db.models import Count

from myapp.models import MyModel

# 获取按照 category 分组后的 count 值,返回分组后的 QuerySet 对象

count_by_category = MyModel.objects.values('category').annotate(count=Count('category'))

可以使用的聚合函数还包括 Sum、Avg、Min 和 Max 等等,你可以根据自己的需要使用不同的聚合函数来计算分组聚合后的结果。

需要注意的是, annotate() 方法和 distinct() 方法一样,都是应用于 QuerySet 对象或相关的 Manager 方法中,并且会创建一个新的查询集。我们还可以使用 filter() 方法和 exclude() 方法对分组对象进行过滤和排除。 例如,你可以按照如下方式获取 category 字段中计数大于 10 的对象:

count_gt_10 = MyModel.objects.values('category').annotate(count=Count('category')).filter(count__gt=10)

在这个示例中,我们在 annotate() 方法之后使用 filter() 方法对分组后的对象进行了过滤,只保留了计数大于 10的对象。

排序查询:

order_by() 方法进行排序查询。

from myapp.models import MyModel

# 对 name 字段进行升序排序

ascending_objects = MyModel.objects.all().order_by('name')

对 name 字段进行升序排序,然后对 age 字段进行降序排序:

# 对 name 字段进行升序排序,对 ages 字段进行降序排序

ascending_then_descending_objects = MyModel.objects.all().order_by('name', '-age')

在这个示例中,我们在 order_by() 方法的第一个参数中指定了 name 字段,并在第二个参数中指定了 -age,表示要对age 字段进行降序排序。

需要注意的是, order_by()方法只能应用于可以比较相等的字段,因此不能用于像二进制字段或大文本字段这样的大对象字段上。此外,排序查询也可以与其他查询方法 filter()、exclude() 和 annotate() 等组合使用。

分页操作

Paginator 类实现分页查询。Paginator 是一个支持分页操作的类,可以将一系列对象按照固定的数量分隔成多个页面。

使用 Paginator 类的步骤如下:

首先,从 Django 的 core.paginator 模块导入 Paginator 类:

from django.core.paginator import Paginator

接下来,获取一个 QuerySet 或者一个列表数据,通常是从数据库中查询到的:

data_list = MyModel.objects.all()

使用 Paginator 类对查询到的数据进行分页设置。例如,将查询到的数据划分成每页5个:

paginator = Paginator(data_list, 5)

最后,从需要显示数据的页面中获取页码并使用 Paginator 对象返回对应的 QuerySet,从而实现分页查询:

page_number = request.GET.get('page') # 获取当前请求的页码

data_page = paginator.get_page(page_number) # 获取对应页码的数据

在这个例子中,我们从请求中获取到需要显示的页码 page_number,然后使用 get_page() 方法返回对应页码的数据。get_page() 方法还可以自动处理非数字或者小于 1 的页码,会返回合法的页码且保持在范围内。如果请求的页码超出范围,get_page() 会返回第一页或最后一页的数据。

需要注意的是, Paginator 类可能会导致大量的查询操作,因此我们在使用它时应该考虑如何优化查询。

模糊查询

使用 QuerySet 的 filter() 方法和 icontains 查询操作符来进行模糊查询。icontains 可以用于查找包含指定字符串的记录,不区分大小写。

例如,如果我们有一个名为 Person 的模型,其中有一个字段 name 表示人名,我们可以使用如下代码来模糊查询名字中包含 “john” 的人:

results = Person.objects.filter(name__icontains='john')

__icontains 表示不区分大小写的包含查询操作符,'john' 是要查找的字符串内容。需要注意的是,进行模糊查询操作时,如果数据集过大,可能会影响性能。在实际开发中,应该根据实际情况使用合理的查询条件和数据库索引来优化查询效率。在 Django 中,可以使用正则表达式进行模糊查询,具体使用方法是使用 regex 查询操作符和 re模块中的正则表达式语法进行匹配。

假设我们有一个名为 Person 的模型,其中有一个字段 name 表示人名,我们可以使用如下代码来使用正则表达式查询名字中包含字母 “j” 或 “o” 的人:

import re

pattern = r'[jo]'

results = Person.objects.filter(name__regex=pattern)

需要注意的是,使用正则表达式进行模糊查询需要注意以下几点:

正则表达式不是 SQL 的一部分,查询速度可能会比简单的 SQL 查询慢,因此尽量减少正则表达式的使用,只在必要时使用。正则表达式的语法较为复杂,需要了解正则表达式的语法和用法才能使用。正则表达式匹配的查询条件应该根据实际情况进行优化,尽量减少不必要的模式匹配过程,以提高查询效率。

多表查询

在 Django 中,可以使用相关对象和 filter() 方法进行多表查询。相关对象是指模型类中通过 ForeignKey、OneToOneField 或 ManyToManyField 字段所定义的关联关系。

假设我们有两个模型类 Author 和 Book,其中 Book 模型类通过 ForeignKey 字段关联到了 Author 模型类,表示一本书只能属于一位作者。现在我们想要查询所有书名以 “Django” 开头的作者及其所写的所有书籍,代码示例如下:

from myapp.models import Author, Book

authors = Author.objects.filter(book__title__startswith='Django')

在这里我们首先导入 Author 和 Book 模型类,然后使用 filter() 方法对 Author 模型类进行过滤,筛选条件为 book__title__startswith='Django',其中 book 是 Author模型中的 ForeignKey 关联字段,title 是 Book 模型类中的属性名,表示书籍的名称,startswith是字符串查询操作符,表示筛选书名以 “Django” 开头的记录。

通过上述查询,我们可以得到所有书籍名以 “Django” 开头的作者,接着通过 Author 模型类中的 book_set 属性获取每个作者所写的所有书籍,示例代码如下:

for author in authors:

books = author.book_set.all()

for book in books:

print(author.name, book.title)

在这里,我们使用 for 循环遍历每个查询到的作者实例对象,然后使用 author.book_set.all()获取该作者所写的所有书籍。由于 Book 模型类中 ForeignKey 关联字段默认生成的 related_name 属性为book_set,因此我们可以使用 author.book_set 获取该作者所写的所有书籍。接着再使用 for循环遍历每本书,输出书籍名称和作者名称。

需要注意的是,如果是多个模型之间的复杂查询,最好使用 Django 内置的 ORM 或第三方的 QueryBuilder来进行多表查询,而不是直接编写原生 SQL 语句,这样可以提高开发效率并避免 SQL 注入安全问题。

执行原生 SQL

Django 提供了游标 cursor 对数据库进行增删改操作,在 Django 中执行非查询语句必须使用游标进行操作。游标 cursor 定义在 django.db.connection 包中,使用前用下面的方式进行导入:

from django.db import connection

用创建 cursor 类的构造函数创建 cursor 对象,再使用 cursor 对象执行 SQL 语句。为确保能够在出现异常时释放 cursor 游标,通常使用 with 语句进行创建操,如下所示:

from django.db import connection

with connection.cursor() as cur:

cur.execute('执行SQL语句')

使用示例如下:

from django.db import connection

with connection.cursor() as cur:

#调用游标对象的execute方法,更新author的名字

cur.execute('update index_author set name="Jack" where id=3;')

with connection.cursor() as cur:

# 删除id为3的一条author记录

cur.execute('delete from index_author where id=3;')

with connections.cursor() as cursor:

cursor.execute('SELECT * FROM my_table WHERE some_field > %s', [some_value])

result = cursor.fetchall()

开发建议使用 Django ORM 中的高级查询,例如 filter、exclude、annotate、values 等方法,这些方法可以构建复杂的查询条件,并且支持链式调用,便于代码维护和可读性。ORM 查询的好处是它是语言级别的,且与数据库类型无关,使你的代码更加可移植。ORM 还提供了自动构造 SQL查询语句的功能,省去了手动构造 SQL 语句的繁琐和容易出错的过程。当然,在某些特定情况下,ORM 查询不能满足需求,例如需要执行复杂的聚合函数,或者需要执行跨数据库的 SQL 查询。这时候可以考虑使用Django 的 connections 模块执行原生 SQL 查询,但需要注意避免 SQL 注入攻击。

查看原文