linRichielinRichie
前端
Python
Linux
ChatGPT
  • B 站
  • 500px
前端
Python
Linux
ChatGPT
  • B 站
  • 500px
    • Python学习指南
  • 快速开始

    • Python: 输入与输出 (I/O)
    • Python: 异常处理 try-except
    • Python: List深入概述
    • Python: 面向对象编程(OOP)
  • Python 语法库函数

    • 语法库目录
    • 库

      • library目录
      • argparse:解析命令行参数
      • difflib:比较序列并生成差异信息
      • dnspython: DNS处理库
      • IPy:IP地址处理库
      • logging:记录和管理日志信息
      • os:访问和操作操作系统
      • psutil:系统性能信息库
      • re:正则表达式库
      • smtplib:邮件发送库
    • 函数

      • function目录
      • any() 函数
      • input 函数
      • lambda 和 map 函数
      • reversed()函数
      • zip()函数
    • 语句

      • statement目录
      • import 语句
      • Try/Exception 异常
    • 概念

      • concept目录
      • 深拷贝与浅拷贝
      • 列表、字典与元组
      • 文件读写
      • IO: 输入与输出
      • 逻辑判断与条件语句
      • OOP 面向对象:class
      • OOP: 面向对象编程
  • SQLAlchemy

    • 获取insert或者update的id
  • Pandas

    • Pandas目录
    • Pandas:基础操作
    • Pandas:数据处理与转换
    • Pandas: 数据写入 excel 表格
  • Python前端框架

    • Flask

      • 1. Flask简介
      • 2. Flask程序基本结构
      • 3. Flask请求-响应循环
      • 4. flask案例: 框架网页查询IP
      • Python: Flask中的GitHub OAuth
    • Django

      • chapter-01:Django框架认识

        • 1.1 Django的产生背景
        • 1.2 MTV设计模式
        • 1.3 Django 主要功能模块
      • chapter-02:开发环境配置

        • 2.1 Python的安装与配置
        • 2.2 虚拟环境安装与配置
        • 2.3 Django安装与配置
        • 2.4 MySQL安装配置
      • chapter-03:项目框架搭建

        • 3.1 Django管理工具-创建项目骨架
        • 3.2 修改项目的默认配置
        • 3.3 初始化项目环境
      • chapter-04:ORM应用与原理剖析

        • 4.1 构建POST应用需要的数据集
        • 4.2 Model相关的概念和使用方法
        • 4.3 Model的查询操作API
        • 4.4 ORM实现原理
      • chapter-05:Django管理后台

        • 5.1 将Model注册到管理后台
        • 5.2 管理后台实现原理
      • chapter-06:视图

        • 6.1 视图基础
        • 6.2 视图的高级特性和快捷方法
        • 6.3 基于类的通用视图
      • chapter-07:模板系统

        • 7.1 模板系统基础
        • 7.2 模板语言
      • chapter-08:表单系统

        • 8.2 使用表单系统实现表单: ModelForm
      • chapter-09:用户认证系统

        • 9.1 用户与身份认证
        • 9.2 权限管理
        • 9.3 用户认证系统应用
      • chapter-10:Django路由系统

        • 10.1 路由系统基础
      • chapter-11:Django中间件

        • 11.1 中间件基础
  • Python例子

    • Python: Linux的Shell命令
    • Python: PEP8自动格式化代码
    • Python: pip操作
    • Python: 业务服务监控
    • Python: 从文件逐行读取数据
    • 将链表转换为字符串
    • Python: 检查URL是否能正常访问
    • Python: 爬取网易云音乐
    • Python: 读取目录下的所有内容
    • 案例研究:文本统计
  • Python爬虫

    • 数据解析工具:Xpath
  • 算法题

    • 02:两数相加
    • 09:回文数
    • 13:罗马数值转换为整数
    • 14:最长公共前缀

4.3 Model的查询操作API

  • 4.3 Model的查询操作API
    • 4.3.1 创建实例
      • save方法创建Model实例
      • create方法创建Model实例
    • 4.3.2 返回单实例的查询方法
    • 4.3.3 返回QuerySet的查询方法
      • values()方法获取字典结果
      • values_list()方法获取元组结果
      • 对QuerySet进行切片
    • 4.3.4 返回RawQuerySet的查询方法
      • 简单的SQL查询
      • 不要拼接SQL语句
      • RawQuerySet支持索引和切片
      • 总结
    • 4.3.5 返回其他类型的查询方法
      • 返回QuerySet的对象数量:count()
      • 判断QuerySet是否包含对象:exists()
      • update()方法更新Model实例
      • delete()方法删除Model实例
    • 4.3.6 关联关系的查询
      • Model的反向查询
      • 跨关联关系查询
      • 跨关联关系多值查询
    • 4.3.7 F对象和Q对象查询
    • 4.3.8 聚合查询和分组查询
      • 1. 聚合查詢

  • 应用中创建的每一个Model类,Django都会自动添加一个名称为objects的Manager对象
  • 它是Model与数据库实现交互的入口,也被称作查询管理器

4.3.1 创建实例

save方法创建Model实例

使用save方法可以创建并保存一个Model实例到数据库中。例如,如果我们有一个名为Book的Model,我们可以使用以下代码创建并保存一个新的Book实例:

book = Book(title='新书', author='作者')
book.save()

这将创建一个新的Book实例,并将其保存到数据库中。

create方法创建Model实例

create方法是另一种创建Model实例的方式。它可以直接创建并保存一个实例,而不需要先创建实例然后再调用save方法。例如:

Book.objects.create(title='新书', author='作者')

这将直接创建并保存一个新的Book实例到数据库中。

需要注意的是,create方法返回的是一个已经保存到数据库的实例对象,而不是一个未保存的实例对象。

4.3.2 返回单实例的查询方法

Django在查询的时候支持使用pk代替主键名称

  • 查询实例的接口通常返回三种类型

    • 单实例
    • QuerySet
    • RawQuerySet
  • 查询单实例

    • get()
      • 会抛出两类异常
      • DoesNotExist: 给定的查询条件找不到对应的数据记录
      • MultipleObjectsReturned: 给定的查询条件匹配了多条数据记录
    • get_or_create(): 实例不存在会创建新的实例对象
  • get()查询

    Topic.objects.get(title='first topic')
    # 优化
    try:
        topic = Topic.objects.get(title='first topic')
    except Topic.DoesNotExist:
        pass
    except Topic.MultipleObjectsReturned:
        pass
    
    # pk
    primary_obj = Topic.objects.get(pk=1)
    
  • get_or_create()查询

    • 此方法返回一个:tuple对象(object,created)。第一个元素是实例对象,第二个元素是布尔值 Topic.objects.get_or_create(id=1,title='first topic')
    • 如果查询条件能够匹配多条记录,也会抛出MultipleObjectsReturned异常

4.3.3 返回QuerySet的查询方法

  • 返回多条数据记录时,需要使用QuerySet对象
  • 可以简单理解为Model集合,可以包含一个,多个或者零个Model实例
  • 返回QuerySet对象的常用接口
    • all:获取所有的数据记录
    • filter:按照查询条件过滤数据记录;在数据库中实现WHERE的功能 filter
      • 对于contains、startswith、endswith关键字,也都有对应的忽略大小写的查询版本,只需要在关键字之前加上字母i就可以了,例如icontains
    • exclude:反向过滤:与filter方法实现的功能是很类似的,只是用一个布尔值标记自己是正向过滤(filter)还是反向过滤(exclude)
      • exclude方法相当于在filter方法的前面加上一个NOT,即过滤出来的结果是不满足给定条件的数据记录
    • reverse:获取逆序数据记录
    • order_by:获取自定义排序规则

Topic
小技巧:使用print(Topic.objects.all().query)可以打印QuerySet的query属性查看生成的SQL语句

# all
Topic.objects.all()
topic_qs = Topic.objects.all()  # 将语句赋值给一个变量时不会去访问数据库的,(惰性求值)
print(Topic.objects.all().query)  # 打印QuerySet的query属性查看生成的SQL语句

# reverse
Topic.objects.reverse()

# order_by
Topic.objects.order_by('-title', 'created_time')

# filter
Comment.objects.filter(up_get=30)  # up字段与gte关键字使用的连接符是双下画线,up字段与gte关键字使用的连接符是双下画线

## filter_exect是比较特殊的
# 当查询字段没有指定查询关键字,默认就是exact,下面的两条语句是等效的
Topic.objects.filter(title='title')
Topic.objects.filter(title_exect='title')

# exclude:相当于在filter方法的前面加上一个NOT
Comment.objects.exclude(up_lt=29)  # 过滤up不小于29的Comment对象;lt(小于)

Topic
filter和exclude方法返回的是一个QuerySet,所以,在它们获得的结果后面还可以继续调用filter exclude等方法,来形成链式查询

# 优化后的链式查询
# 需要content中包含good、down不大于20且up大于20的Comment对象,可以使用链式查询
Comment.objects.filter(content__icontains='good', down__lte=20, up__gt=20)

# 优化后的排序查询
# 下面的查询会按照created_time正序排列
Topic.objects.order_by('created_time')
# 可以通过print() 方法查看对应的SQL语句

values()方法获取字典结果

  • values方法返回的是一个QuerySet,与all, filter的区别在于

    • all, filter:通过迭代获取Model的实例对象
    • values:返回字典,字典中的键对应Model的字段名
  • 给values方法传递参数,限制SELECT的查询范围,没有指定,会查询Model的所有字段

    # 只查询Comment表的id和up字段
    Comment.objects.values('id','up')
    

values_list()方法获取元组结果

  • 获取到的结果是元组,不是字典

  • 会按照传递的字段名限制SELECT的查询范围

  • 当只传递一个参数,可以配合flat参数。

    • flat=True: 迭代返回结果得到的将是字段值
    • 默认False: 结果是只有单个元素的元组
    Comment.objects.values_list('id','up')
    Comment.objects.values_list('id',flat=True)
    

对QuerySet进行切片

  • 在SQL上表现为LIMIT和OFFSET

    Comment.objects.all()[:2]
    

注意

  • QuerySet不支持从末尾切片,即索引值不能为负数
  • 一个QuerySet执行切片操作会返回另一个QuerySet,不会触发数据库的查询
  • 但使用step切片语法,则会触发数据库查询,并返回Model实例列表
    • 如Comment.objects.all()[1:3:2]
  • 切片后返回的QuerySet不能在执行过滤或者排序操作

Topic: Manager而言,all filter exculde等常用的方法会返回QuerySet外,一些不常用的方法也会返回QuerySet,如:distinct,only,defer等方法

4.3.4 返回RawQuerySet的查询方法

复杂的查询:ORM的表达能力会存在局限性,需要使用SQL语句实现查询

  • Manager提供了:raw()方法允许使用SQL语句实现对Model的查询
  • raw()返回的是一个RawQuerySet对象,可以迭代得到Model实例对象
  • RawQuerySet不能在此基础上执行filter exclude等方法

简单的SQL查询

for comment in Comment.objects.raw('SELECT * FROM post_comment'):
    print('%d:%s' % (comment.id,comment.content))

注意:Django不会对传递给raw方法的SQL语句进行检查。Django期望它会从数据库中返回一组行数据,但是并不做强制要求。如果查询的结果不是行数据,则会产生一个错误

不要拼接SQL语句

  • 不要手动填充SQL字符串,因为此方式会存在SQL注入攻击的风险

  • raw()方法提供了params参数解决SQL注入的风险

  • 例子

    # 对于参数化查询的SQL来说,只需要在语句中加上%s或者%(key)s之类的占位符
    for topic in Topic.objects.raw('SELECT id FROM post_topic WHERE title = %s', {'first_topic'}):
        print('%d:%s' % (topic.id,topic.content))
    
  • raw() 接受的是一个:列表或者字典的参数,将SQL语句中的占位符进行替换,完成查询

  • 上面的代码中获取的是id这个字段,但是仍可以获取title字段

RawQuerySet支持索引和切片

Comment.objects.raw('SELECT ** FROM post_comment')[0]
Comment.objects.raw('SELECT ** FROM post_comment')[1:2]

总结

raw SQL的查询方法是比较少用到的,一般还是使用常规的ORM模型进行查询

4.3.5 返回其他类型的查询方法

返回QuerySet的对象数量:count()

  • 通过 all filter exclude方法得到QuerySet对象后,可以通过len()方法对象的数量
    • len(Comment.objects.filter(id_gt=1))
    • 通过len()方法的效率比较低效
  • QuerySet提供的是count()方法
    • Comment.objects.filter(id_get=1).count()
    • count()方法会在SQL上执行SELECT COUNT(*)操作并返回数字类型

判断QuerySet是否包含对象:exists()

  • 根据当前给定的条件返回是否存在匹配实例对象的布尔结果
  • 方法1:判断count方法的返回值是否为0
  • 方法2:使用exists()方法 推荐
    • 包含返回:True,否则为:False
    • 在SQL中表现为LIMIT 1
    • Comment.objects.filter(id_get=1).exists()

update()方法更新Model实例

  • 更新实例的方法

  • 方法1:save()方法: 即先查询出Model对象,之后更新字段值

    comment = Comment.objects.get(id=1)
    comment.up=90
    comment.save()
    

    注意的是,虽然只是给up字段重新设定了值,但是save执行会更新Comment表的所有列

  • 方法2:update()方法:更新特定的列

    Comment.objects.filter(id=1).update(up=90,down=33)
    

    注意:update方法可以一次性更新多个对象,并返回一个整数,标识受影响的记录条数

delete()方法删除Model实例

# 删除id=1的Comment实例
comment = Comment.objects.get(id=1)
comment.delete()

# 删除id=2的实例
Comment.objects.filter(id=2).delete()

delete方法返回一个二元组:第一个元素标识删除的实例个数,第二个元素是字典类型,记录每一个Model类型删除的实例个数

4.3.6 关联关系的查询

Model的反向查询

  • Django中的每一种关联关系都可以实现反向查询

  • 默认情况下,管理器的名称为“小写模型名_set”,对于Topic而言就是comment_set。之前介绍过可以通过related_name参数覆盖,但是通常不需要这样做

    # 通过comment_set实现反向查询
    topic = Topic.objects.get(id=1)
    topic.comment_set.all()  # 反向查询
    topic.comment_set.filter(content='very good!')
    topic.comment_set.exclude(content_contains='good')
    
  • ManyToManyField和OneToOneField关系类型也可以实现类似的反向查询

注意:OneToOneField类型的反向查询比较特殊。它的管理器代表的是一个单一的对象,而不是对象集合,且名称变成了小写的Model名

  • 对于OneToOneField的反向查询

    # 例如之前定义的CustomUser,它的user字段与Django的User存在OneToOneField的关系,那么,User可以这样实现反向查询
    # 如果没有CustomUser对象与User关联,那么,上面的查询将会抛出RelatedObjectDoesNotExist异常
    user = User.objects.get(username='admin')
    user.customuser
    

跨关联关系查询

  • 跨关联查询:只需要使用双下画线与关联Model的字段名称组合在一起,并给出合适的条件就可以完成查询
    • Comment.objects.filter(topic_title_contains='first'): 查询所有Topic的title字段包含first的Comment对象
    • Comment.objects.filter(topic_user_username='first')
  • 支持反向的关联查询,只需要使用关联Model的小写名称即可
    • Topic.objects.filter(comment_up_gte=30): 查询所有Comment的up值大于等于30的Topic对象

跨关联关系多值查询

topic = Topic.objects.get(id=1)
Comment.objects.create(content='general', up=60, down=40,topic=topic)

# 第一个查询: 对应的是Comment中的content字段值包含very且up字段值小于等于60的所有的Topic对象
Topic.objects.filter(comment_content_contains='very', comment_up_lte=60)

# 第二个查询
Topic.objects.filter(comment_content_contains='very').filter(comment_up_lte=60)

4.3.7 F对象和Q对象查询

4.3.8 聚合查询和分组查询

聚合和分组都是用来生成统计值的过程:聚合是指对QuerySet整体(可以理解为Model对象的集合)生成一个统计值;分组是为QuerySet中每一个对象都生成一个统计值。

1. 聚合查詢

对QuerySet计算统计值,需要使用aggregate方法,提供的参数可以是一个或多个聚合函数。aggregate是QuerySet的一个终止子句,它返回的是字典类型,键是聚合的标识,值是聚合的结果。Django提供了一系列的聚合函数,其中Avg(平均值)、Count(计数)、Max(最大值)、Min(最小值)、Sum(加和)最为常用。要使用这些聚合函数,需要将它们引入当前的环境中

from django.db.models import Avg, Max, Min, Sum, Count

# For Example
Comment.object.filter(topic=1).aggregate(Sum('up'))
最近更新时间:
Prev
4.2 Model相关的概念和使用方法
Next
4.4 ORM实现原理