[Python] Django ORM与执行原生SQL实现操作数据库(下)

简介: [Python] Django ORM与执行原生SQL实现操作数据库(下)

🍒 聚合函数

在查询数据时需要使用聚合函数,则需要使用aggregate()过滤器调用聚合函数来进行数据的查询。

聚合函数包括:Avg平均,Count数量,Max最大,Min最小,Sum求和,被定义在django.db.models中。

语法:

aggregate(聚合函数('字段名'))

注意aggregate的返回值是一个字典类型,格式如下:

{'属性名__聚合类小写':值}

# 导入聚合函数
from django.db.models import Avg, Count, Max, Min, Sum
# 查询图书的总阅读量。
books = BookInfo.objects.aggregate(Sum('read_count'))

🍒 排序

使用order_by()对结果进行排序.

降序排序只需要在指定字段前加一个负号即可

# 根据评论数对所有图书的查询结果进行排序
# 默认升序
books = BookInfo.objects.all().order_by('comment_count')
# 降序
books = BookInfo.objects.all().order_by('-comment_count')

# 根据阅读数和id对所有图书的查询结果进行排序
# 阅读数升序,相同时按照id号升序排序
books = BookInfo.objects.all().order_by('read_count', 'id')
# 阅读数升序,相同时按照id号降序排序
books = BookInfo.objects.all().order_by('read_count', '-id')

🍒 关联查询

🍓 由一到多的访问

已知主表(被从表建立外键引用)数据,关联查询从表(引用主表)数据。

语法:

当在两个模型中使用外键建立关联之后,Django会在被从表关联的主表模型中自动生成添加可以用于进行反向操作(因为外键在从表,主表连接从表查询数据为反向操作)的属性 从表模型类名小写,如果在从表定义外键时有指定反向操作时使用的名称,则使用指定的名称进行反向操作。

主表模型(或实例对象).从表模型类名小写_set
# 查询书籍为1的所有人物信息
# 1. 查询id为1的书籍
book = BookInfo.objects.get(id=1)
# 2. 根据书籍关联任务信息
people = book.peopleinfo_set.all()

🍓 由多到一的访问

已知从表数据,关联查询主表数据。

语法:

从表模型(或实例对象).从表模型中的外键属性
from book.models import PeopleInfo
# 查询人物为1的书籍信息
# 1. 查询id为1的人物信息
person = PeopleInfo.objects.get(id=1)
# 2. 根据人物信息获取人物对应的书籍信息
book = person.book

🍓 由一到一的访问

一到一的访问为上述一到多和多到一的特例,在进行主表关联从表和从表关联主表的查询时,依旧可以使用上述的方法进行数据的关联查询。有定义外键的模型使用外键进行关联,没有定义外键的模型(被关联的模型)使用Django在被关联的模型中自动生成添加用于进行反向操作的属性 关联模型类名小写 进行关联查询。

🍓 由多到多的访问

与上述一到多和多到一的查询方法类似,有定义外键的模型使用外键进行关联,没有定义外键的模型(被关联的模型)使用Django在被关联的模型中自动生成添加用于进行反向操作的属性 关联模型类名小写 进行关联查询。

🍓 select_related()

可以将关联的模型数据提取出来,可以减少数据库查询的次数。

用于一到多的关联查询

主表模型.objects.select_related("关联从表模型名小写")

🍓 prefetch_related()

可以将关联的模型数据提取出来,可以减少数据库查询的次数。

用于多到一和多到多的关联查询

从表模型.objects.prefetch_related("关联主表模型名小写")

🍓 关联查询的筛选

Django会在被关联的模型中自动生成添加可以用于进行反向操作的属性 关联模型类名小写

# 查询图书,要求图书人物为“郭靖”
# 需要查询的为书籍信息,已知条件为人物信息
# 需要查询的为主表信息,已知条件为从表(外键定义所在的模型对应的表)信息
# 语法:
# filter(关联从表模型类名小写__字段名__运算符=值)
book = BookInfo.objects.filter(peopleinfo__name__exact='郭靖')
# 简写
book = BookInfo.objects.filter(peopleinfo__name='郭靖')

# 查询图书,要求图书中人物的描述包含"八"
book = BookInfo.objects.filter(peopleinfo__description__contains='

从表模型中有一个外键属性

# 需要查询的为人物信息,已知条件为书籍信息
# 需要查询的为从表信息,已知条件为主表信息
# 语法:
# filter(外键名__字段名__运算符=值)
# 查询书名为“天龙八部”的所有人物
people = PeopleInfo.objects.filter(book__name__exact='天龙八部')
# 简写
people = PeopleInfo.objects.filter(book__name='天龙八部')
# 查询图书阅读量大于30的所有人物
people = PeopleInfo.objects.filter(book__read_count__gt=30)

🍉 新增数据

🍒 方式一:实例化模型类,save()保存新增数据

🌰 1.导入模型类:

from subapp01.models import BookInfo  # 导入模型

🌰 2.实例化模型对象

写法1:实例化模型类型对象,并且在实例化的时候为属性赋值,会返回对应的新数据对象

book = BookInfo(
    name='python',
    pub_date='2000-01-01'
    # 其他属性有默认值,这边就不进行赋值了
)

写法2:实例化模型类对象后,单独为实例化后的模型对象的属性赋值

book = BookInfo()
book.name = 'Java'
book.pub_date = '2001-01-01'
# 其他属性有默认值,这边就不进行赋值了

🌰 3.调用save()方法,将新增的数据对象同步到数据库中

book.save()

🍒 方式二:直接create()新增数据入库

对数据进行增删改查都可以通过调用模型类的模型管理类对象objects来实现

🌰 1.导入模型类:

from subapp01.models import BookInfo  # 导入模型

🌰 2.调用objects的create()方法直接新增数据对象入库

objects的create()方法会把新增的数据对象返回给我们

BookInfo.objects.create(
    name='css3',
    pub_date='2010-01-01'
    # 其他属性有默认值,这边就不进行赋值了
)

🍉 更新数据

对数据进行增删改查都可以通过调用模型类的模型管理类对象objects来实现

🍒 方法一:获取模型对象修改对象属性,save()保存修改数据

🌰 1.导入模型类:

from subapp01.models import BookInfo  # 导入模型

🌰 2.获取模型对象

# 查询(获取)出需要进行修改的数据对象  获取id=1的数据对象
book = BookInfo.objects.get(id=1)

🌰 3.修改获取到的实例对象的属性

book.read_count = 2000

🌰 4.调用save()方法将修改后的数据同步数据库

book.save()

🍒 方法二:直接update()修改数据

🌰 1.导入模型类:

from subapp01.models import BookInfo  # 导入模型

🌰 2.获取模型对象时链式调用update()方法修改对象属性同时将修改后的数据同步到数据库

# 先过滤(filter)出需要进行修改的数据对象,然后修改对象属性
# 此方法会返回受影响的行数
BookInfo.objects.filter(id=1).update(
    read_count = 100,
    comment_count = 2000
)

get()方法得到的是一个模型对象,模型对象上不具有update()方法,所以不能使用get()方法得到模型对象后直接链式调用update()方法更新数据;而filter()方法返回的是一个QuerySet对象,QuerySet对象上具有update()方法,可以直接链式调用update()方法更新数据。

🍒 objects调用update()批量更新所有数据

🌰 1.导入模型类:

from subapp01.models import BookInfo  # 导入模型

🌰 2.使用objects直接调用update()批量更新所有数据对象(行)数据

该方法会返回数据库中受影响的行数

# 将所有数据对象(行)的read_count修改为500
books = BookInfo.objects.update(read_count=500)

🍒 批量更新多行数据

🌰 1.导入模型类:

from subapp01.models import BookInfo  # 导入模型

🌰 2.通过filter()方法过滤出多行数据,然后链式调用update()方法对过滤出来的多行数据对象进行批量更新

该方法会返回受影响的行数

books = BookInfo.objects.filter(read_count=1000).update(read_count=700)

🍉 删除数据

🍒 方法一:获取指定数据对象调用delete()删除该数据对象

🌰 1.导入模型类:

from subapp01.models import BookInfo  # 导入模型

🌰 2.获取指定的模型数据对象

book = BookInfo.objects.get(id=5)

🌰 3.获取到的模型数据对象调用delete()方法将自己删除

该方法会返回一个元组类型的数据,元组的第一个元素为本次操作删除的所有表的总行数之和,元组的第二个元素为字典类型,字典中为每个表在本次操作分别删除了几行数据

book.delete()

🍒 方法二:过滤出指定数据对象调用delete()删除过滤出来的数据对象

🌰 1.导入模型类:

from subapp01.models import BookInfo  # 导入模型

🌰 2.过滤出指定数据对象直接链式调用delete()方法删除过滤出来的数据对象

该方法会返回一个元组类型的数据,元组的第一个元素为本次操作删除的所有表的总行数之和,元组的第二个元素为字典类型,字典中为每个表在本次操作分别删除了几行数据

book = BookInfo.objects.filter(id=6).delete()

🍒 删除多行数据

🌰 1.导入模型类:

from subapp01.models import BookInfo  # 导入模型

🌰 2.过滤出指定数据对象直接链式调用delete()方法删除过滤出来的数据对象

该方法会返回一个元组类型的数据,元组的第一个元素为本次操作删除的所有表的总行数之和,元组的第二个元素为字典类型,字典中为每个表在本次操作分别删除了几行数据

books = BookInfo.objects.filter(read_count=700).delete()

🍒 删除全部数据

🌰 1.导入模型类:

from subapp01.models import BookInfo  # 导入模型

🌰 2.使用objects调用all()方法查询出全部数据,然后调用delete()方法删除模型对应的表的全部数据对象

该方法会返回一个元组类型的数据,元组的第一个元素为本次操作删除的所有表的总行数之和,元组的第二个元素为字典类型,字典中为每个表在本次操作分别删除了几行数据

books = BookInfo.objects.all().delete()

🍉 执行原生SQL

当ORM提供的API无法实现数据的查询时,需要使用原生的SQL语句进行数据的查询,此时我们可以通过执行原生SQL实现数据的查询。

🍒 raw()

在Django中执行原生的SQL语句,我们需要使用raw()方法,raw()方法被定义在django.db.models.Manager模块中。raw()方法返回的结果为RawQuerySet对象,对于RawQuerySet对象可以使用列表下标的方式获取其中的数据对象。

语法:

sql = ''  # sql语句中使用 %s 作为占位符(防止SQL注入)
# 调用row()方法执行原生sql查询数据
模型类.objects.raw(sql, [填充sql中占位符的数据])  # 第二个参数可以使用列表

🌰

from subapp01.models import BookInfo
sql = """
select id, name 
from bookinfo
where id = %s or read_count<=%s;
"""
books = BookInfo.objects.raw(sql, [1, 20])

>>> from subapp01.models import BookInfo
>>> sql = """
... select id, name
... from bookinfo
... where id = %s or read_count<=%s;
... """
>>> books = BookInfo.objects.raw(sql, [1, 20])
>>> books
<RawQuerySet:
select id, name
from bookinfo
where id = 1 or read_count<=20;
>
# 返回的结果集可以使用列表下标的方式获取数据对象
>>> books[0]
<BookInfo: 射雕英雄传>
>>> books[1]
<BookInfo: 笑傲江湖>
>>>

🍒 游标

在Django中,django.db.connection中封装了数据库的连接对象,我们可以通过数据库的连接对象获取游标,然后通过获取的游标来执行原生SQL语句实现对数据库的增删改查。

🍓 用法

获取数据库的连接对象:

from django.db import connection

通过数据库的连接对象获取游标:

cursor = connection.cursor()

使用游标执行原生sql语句:

cursor.execute(sql, [填充占位符的数据])

关闭游标:

cursor.close()

🍓 插入数据

from django.db import connection
cursor = connection.cursor()
sql = """
insert into bookinfo(name, pub_date)
values (%s, %s);
"""
res = cursor.execute(sql, ('Java', '2020-11-11'))
cursor.close()
>>> from django.db import connection
>>> cursor = connection.cursor()
>>> sql = """
... insert into bookinfo(name, pub_date, read_count, comment_count, is_delete)
... values (%s, %s, %s, %s, %s);
... """
>>> res = cursor.execute(sql, ('Java', '2020-11-11', 100, 50, 0))
>>> res
<django.db.backends.sqlite3.base.SQLiteCursorWrapper object at 0x000002721C9BB0A0>
>>> cursor.close()

🍓 查询数据

游标对象提供了fetchall()方法用于获取执行sql语句后的全部数据,返回的结果为所有数据对象各字段数据组成的元组形成的列表;以及fetchone()方法用于获取其中的一个结果,第一次执行获取第一条数据记录,此后每执行一次都会返回下一条数据记录,直到所有的数据记录都获取过为止,每次返回的结果为数据对象各字段数据组成的元组。

from django.db import connection
sql = "select * from bookinfo;"
cursor = connection.cursor()
res = cursor.execute(sql)
data = res.fetchall()
cursor.close()
>>> from django.db import connection
>>> sql = "select * from bookinfo;"
>>> cursor = connection.cursor()
>>> res = cursor.execute(sql)
>>> res.fetchall()
[(8, '射雕英雄传', datetime.date(1980, 5, 1), 12, 34, False), (9, '天龙八部', datetime.date(1986, 7, 24), 36, 40, False), (10, '笑傲江湖', datetime.date(1995, 12, 24), 20, 80, False), (11, '雪山飞狐', datetime.d
ate(1987, 11, 11), 58, 24, False), (12, 'Java', datetime.date(2020, 11, 11), 100, 50, False)]
>>> cursor.close()
>>> from django.db import connection
>>> sql = "select * from bookinfo;"
>>> cursor = connection.cursor()
>>> res = cursor.execute(sql)
>>> res.fetchone()
(8, '射雕英雄传', datetime.date(1980, 5, 1), 12, 34, False)
>>> res.fetchone()
(9, '天龙八部', datetime.date(1986, 7, 24), 36, 40, False)
>>> res.fetchone()
(10, '笑傲江湖', datetime.date(1995, 12, 24), 20, 80, False)
>>> res.fetchone()
(11, '雪山飞狐', datetime.date(1987, 11, 11), 58, 24, False)
>>> res.fetchone()
(12, 'Java', datetime.date(2020, 11, 11), 100, 50, False)
>>> res.fetchone()  # 此时以及获取完返回的全部数据记录,所以没有结果输出
>>> cursor.close()

🍓 更新数据

from django.db import connection
sql = "update bookinfo set name='python' where id=%s;"
cursor = connection.cursor()
res = cursor.execute(sql, [12])
res.rowcount  # 获取本次操作在数据库中影响的行数
cursor.close()
>>> from django.db import connection
>>> sql = "update bookinfo set name='python' where id=%s;"
>>> cursor = connection.cursor()
>>> res = cursor.execute(sql, [12])
>>> res.rowcount                                    
1
>>> cursor.close()

🍓 删除数据

from django.db import connection
sql = "delete from bookinfo where id=%s;"
cursor = connection.cursor()
res = cursor.execute(sql, [12])
res.rowcount  # 获取本次操作在数据库中影响的行数
cursor.close()
>>> from django.db import connection
>>> sql = "delete from bookinfo where id=%s;"
>>> cursor = connection.cursor()
>>> res = cursor.execute(sql, [12])
>>> res.rowcount  # 获取本次操作在数据库中影响的行数
1
>>> cursor.close()

🍉 事务处理

在Django中,提供了django.db.transaction模块用于事务的处理。

🍒 装饰器方式

from django.db import transaction
@transaction.atomic  # 装饰器
def fun(request):
    save_id = transaction.savepoint()  # 开启事务
    try:
        # 操作1
        # 操作2
        # ......
        transaction.savepoint_commit(save_id)  # 没有出现错误,提交从保存点到当前位置的所有操作
    except:
        transaction.savepoint_rollback(save_id)  # 如果出现错误就回滚到保存点

🍒 with 语句方式

from django.db import transaction
def fun(request):
    with transaction.atomic():
        save_id = transaction.savepoint()  # 开启事务
        try:
            # 操作1
            # 操作2
            # ......
            transaction.savepoint_commit(save_id)  # 没有出现错误,提交从保存点到当前位置的所有操作
        except:
            transaction.savepoint_rollback(save_id)  # 如果出现错误就回滚到保存点


相关文章
|
6天前
|
SQL 安全 数据库
如何在Django中正确使用参数化查询或ORM来避免SQL注入漏洞?
如何在Django中正确使用参数化查询或ORM来避免SQL注入漏洞?
100 77
|
6天前
|
SQL 安全 数据库连接
除了使用Django的ORM,还能通过什么方式在Django中避免SQL注入漏洞?
除了使用Django的ORM,还能通过什么方式在Django中避免SQL注入漏洞?
33 17
|
3天前
|
关系型数据库 API 数据库
Python流行orm框架对比
Python中有多个流行的ORM框架,如SQLAlchemy、Django ORM、Peewee、Tortoise ORM、Pony ORM、SQLModel和GINO。每个框架各有特点,适用于不同的项目需求。SQLAlchemy功能强大且灵活,适合复杂项目;Django ORM与Django框架无缝集成,易用性强;Peewee轻量级且简单,适合小型项目;Tortoise ORM专为异步框架设计;Pony ORM查询语法直观;SQLModel结合Pydantic,适合FastAPI;GINO则适合异步环境开发。初学者推荐使用Django ORM或Peewee,因其易学易用。
|
1月前
|
SQL 定位技术 数据库
深入探索Django ORM:高效数据库操作的秘诀####
本文旨在为读者揭开Django ORM(对象关系映射)的神秘面纱,通过一系列生动的比喻和详实的案例,深入浅出地讲解其核心概念、工作原理及高级特性。我们将一起探讨如何利用Django ORM简化数据库交互,提升开发效率,同时确保数据的一致性和安全性。不同于传统的技术文档,本文将以故事化的形式,带领读者在轻松愉快的氛围中掌握Django ORM的精髓。 ####
|
3月前
|
SQL 关系型数据库 数据库
优化Web开发流程:Python ORM的优势与实现细节
【10月更文挑战第4天】在Web开发中,数据库操作至关重要,但直接编写SQL语句既繁琐又易错。对象关系映射(ORM)技术应运而生,让开发者以面向对象的方式操作数据库,显著提升了开发效率和代码可维护性。本文探讨Python ORM的优势及其实现细节,并通过Django ORM的示例展示其应用。ORM提供高级抽象层,简化数据库操作,提高代码可读性,并支持多种数据库后端,防止SQL注入。Django内置强大的ORM系统,通过定义模型、生成数据库表、插入和查询数据等步骤,展示了如何利用ORM简化复杂的数据库操作。
86 6
|
3月前
|
关系型数据库 MySQL Java
Django学习二:配置mysql,创建model实例,自动创建数据库表,对mysql数据库表已经创建好的进行直接操作和实验。
这篇文章是关于如何使用Django框架配置MySQL数据库,创建模型实例,并自动或手动创建数据库表,以及对这些表进行操作的详细教程。
124 0
Django学习二:配置mysql,创建model实例,自动创建数据库表,对mysql数据库表已经创建好的进行直接操作和实验。
|
3月前
|
存储 关系型数据库 数据库
轻量级数据库的利器:Python 及其内置 SQLite 简介
轻量级数据库的利器:Python 及其内置 SQLite 简介
85 3
|
3月前
|
数据库连接 Linux 数据库
GBase 8s数据库连接 – Python
GBase 8s数据库连接 – Python
|
3月前
|
SQL Go 数据库
【速存】深入理解Django ORM:编写高效的数据库查询
【速存】深入理解Django ORM:编写高效的数据库查询
109 0
|
4月前
|
数据库 Python
django中数据库外键可以自定义名称吗
django中数据库外键可以自定义名称吗