Datawhale Django后端开发入门 TASK03 QuerySet和Instance、APIVIew

一、QuerySet

QuerySet 是 Django 中的一个查询集合,它是由 Model.objects 方法返回的,并且可以用于生成数据库中所有满足一定条件的对象的列表。

QuerySet 在 Django 中表示从数据库中获取的对象集合,它是一个可迭代的、类似列表的对象集合。主要特点包括:- 从 Model.objects 获得,表示数据库中所有该 Model 的对象集合。

- 可以添加过滤条件来限制查询结果,如 .filter()、`.exclude()`、`.order_by()` 等。

- 惰性执行,创建 QuerySet 不会立即执行查询,只有在需要求值时才查询数据库。- 可遍历,可以用在 for 循环中进行迭代。- 可切片,使用索引切片来获取一个子集。

- 支持链式调用过滤方法,每个过滤调用返回一个新的 QuerySet。

- 可以获取单个元素,如 .get() 返回单个匹配的对象。

- 支持转换为其他对象列表,如 .values()

- 可以转为字符串执行原始 SQL 查询。所以 QuerySet 是从数据库中获取模型对象数据的一个强大而灵活的接口。正确使用可以最大限度减少数据库查询,提高效率。每个 QuerySet 包含多个 Model 实例(Instance),表示满足查询条件的所有对象实例。

1.filter()方法接收的参数有:- 字段名,比如:.filter(name='John')
- 查询表达式,比如: .filter(age__gt=18)
- Q对象,用于复杂查询,比如:

from django.db.models import Qqueryset.filter(Q(age__gt=18) & Q(name='John'))

关键字参数,比如:.filter(name='John', age__gt=18)filter()会基于给定的参数生成一个新的过滤后的QuerySet。例如:

Article.objects.filter(published=True) # 已发布文章Article.objects.filter(title__contains='Django') # 标题包含Django的文章Article.objects.filter(Q(title__contains='Django') | Q(title__contains='Python'))
# 标题包含Django或Python的文章

重要的是,filter()并不会立即执行查询,只是返回一个新的QuerySet,真正的数据库查询会在需要求值的时候发生。我们可以多次调用filter()来链式过滤,每个filter()调用会基于前一个QuerySet然后返回一个新的QuerySet。

2.get()

get()方法与filter()有些类似,主要区别在于:

(1. get()用来获取单个对象,而filter()获取一个对象集合(queryset)。

(2. get()只能返回一个满足条件的对象实例,如果不存在会引发模型类的DoesNotExist异常。

(3. get()无法链接调用,只能获取单个对象。例如:

# 获取id=1的文章
article = Article.objects.get(id=1) # 查询标题包含'Python'的文章,如果不存在会报错
article = Article.objects.get(title__contains='Python')# filter()返回满足条件的所有文章
articles = Article.objects.filter(title__contains='Python')

所以get()主要用于根据过滤条件获取单个对象,这对于获取某个具体模型实例很有用,但需要注意如果不存在会引发异常。而filter()用于获取多个满足条件的对象,这对获取一个QuerySet集合并对其进一步处理很有用。需要根据具体场景选择使用get()还是filter()。

3.all()

Django中的`Model.objects.all()`方法返回该模型的全部对象集合。它相当于没有任何过滤条件的`filter()`查询:

Article.objects.all()
# 等同于
Article.objects.filter()

all()返回的是一个包含该模型所有对象的QuerySet。我们可以在`all()`的基础上进一步链式过滤:

Article.objects.all().filter(published=True) 

all()对于获取某个模型的全部数据很有用。当然,如果数据量很大,我们可能需要限制返回的数据条数,以提高性能。另外,all()每次都会查询数据库。如果需要多次使用全部数据集,可以考虑使用缓存或预取相关对象:prefetch_related()。所以在合适的时候使用`all()`可以方便地获取模型的全部实例,但需要注意数据量大小和重复查询的问题。

4.delete()

delete()方法将删除QuerySet中的所有对象,并返回删除的对象数量。

  @action(detail=False, methods=['get','post'])def delete_example(self, request):name = request.data.get('name')# 删除名称为 'name' 的商品categories_to_delete = GoodsCategory.objects.filter(name=name)# 使用delete()方法删除对象deleted_count= categories_to_delete.delete()print(f"Deleted {deleted_count} categories.")      

主要流程是:1. 通过filter()筛选出需要删除的对象集合2. 在这个QuerySet上调用delete()方法实现删除3. delete()方法会删除QuerySet中的所有对象,并返回删除的对象数量所以delete()为批量删除QuerySet中的对象提供了很好的便利。注意delete()会立即执行删除操作,不像filter()那样是延迟执行。另外,delete()默认不会触发模型的delete()方法,如果需要调用可以设置:

categories_to_delete.delete(keep_parents=False)

这样会为每个对象调用delete()方法。delete()方法非常适合批量删除不需要的对象,可以用来定期清理数据库。但需要注意确保筛选条件正确,避免误删除。

5.update()

update() 方法将对QuerySet进行筛选,获取需要更新的对象集合,然后执行数据库更新操作。基本语法如下:

queryset.update(字段1=值1, 字段2=值2...)

这将设置指定的字段到相应的值。例如:

Article.objects.filter(published=True).update(status='p') 
# 将已发布文章的状态都设为'p'Article.objects.filter(id__in=[1,2,3]).update(views=F('views') + 1)
# 对id为1,2,3的文章浏览量增1

update()默认只会更新指定的字段。需要注意,update()同样会立即执行更新,并返回更新的行数。update()提供了一个非常高效的批量更新对象方法,可以避免大量的单个对象更新开销。但同样需要确保更新条件的准确性。

6.create()

create() 方法是 save() 方法的快捷方式,用于创建并保存一个新的对象。

@action(detail=False, methods=['get','post'])def create_example(self, request):name = request.data.get('name')# 使用create()方法创建新的商品分类对象created_category = GoodsCategory.objects.create(name)print("Created category:", created_category) 

主要的用法是:

GoodsCategory.objects.create(name='名称', field1='值1',...)

这将实例化GoodsCategory,为其设置指定的字段值,然后直接保存到数据库中。与下面的用法等价:

category = GoodsCategory(name='名称', field1='值1',...) 
category.save()

所以create()方法对于快速创建对象非常方便,尤其是在数据初始化或者测试中可以减少代码量。需要注意:- create()参数必须提供对象必填字段的值
- create()会自动保存对象,无需再调用save()
- 创建成功后会返回新创建的对象实例综上,create()是save()方法的一个非常好用的封装,可以简化对象创建的代码。

7.count()

Django QuerySet 中的 count() 方法可以用来返回满足指定查询条件的对象的总数。count() 方法会执行查询,获取匹配查询(filters)的对象数目。使用方式:

Article.objects.filter(published=True).count() # 返回已发布文章数Article.objects.filter(title__contains='Django').count() # 返回标题包含'Django'的文章数

Article.objects.filter(published=True).count() # 返回已发布文章数 Article.objects.filter(title__contains='Django').count() # 返回标题包含'Django'的文章数

count()会执行查询数据库的COUNT操作,性能上比提取所有对象再计算列表长度要更高效,尤其是数据量很大的时候。我们可以像链式调用filter()一样,链式调用count()来获取不同条件下的数量:

Article.objects.filter(published=True).filter(views__gt=10).count() 
# 返回已发布且浏览量大于10的文章数。

需要注意,使用count()之后再继续链式过滤就不会生效了。所以count()为我们提供了一个简单直观的方式来获取查询集大小。可以用来判断是否有匹配的对象,或者计算比较不同查询的结果数目。

8.order_by()

Django QuerySet 中order_by()方法用于对返回的对象进行排序。order_by()的常见用法:

# 按发布日期升序排序
Article.objects.order_by('publish_date') # 按浏览量降序排序 
Article.objects.order_by('-views')# 先按发布日期降序,再按标题升序
Article.objects.order_by('-publish_date', 'title')

默认order_by()是按升序排列的,如果需要降序,可以在参数字段名前加一个负号-。可以传递多个字段名来先后进行排序,如上面的例子。order_by()通常用在需要排序的查询中,比如获取最新文章:

latest_articles = Article.objects.order_by('-publish_date')[:10] 

需要注意order_by()通常应该放在链式查询的最后,因为它会改变查询结果的顺序。order_by()非常实用,可以帮助我们按任意字段排序查询集,灵活地获取需要的数据。

9.values()

在Django中,values()方法可以用来获取QuerySet中的对象的指定字段的值,返回一个ValueQueryset,里面是以字典形式包含指定字段值的对象。那么

Map.objects.all().values().first()

的作用就是:

(1. Map.objects.all() 返回Map模型的全部对象的QuerySet

(2. 在这个QuerySet上调用`.values()`,不指定任何字段,那么会返回包含每个对象的所有字段及值的字典。

(3. 最后调用.first()返回第一个对象的字段字典。例如,如果Map模型有字段id, name, address,那么它类似于:

{'id': 1,'name': 'John','address': '123 Main St' 
}

如果我们只需要名称和地址:

Map.objects.all().values('name', 'address').first()

那么得到的是:

{'name': 'John','address': '123 Main St'
}

这可以避免提取整个对象然后访问字段,提高效率。所以values()方法非常适合只需要获取对象某些字段的值而不需要模型对象实例的时候使用。

 二、Instance

创建一个对象:Obj = Model(attr1=val1, attr2=val2),Obj.save()
更新一个对象:Obj = Model.objects.get(id=xxx),Obj.attr1 = val1,Obj.save()
删除一个对象:Obj = Model.objects.get(id=xxx),Obj.delete()

Django模型实例(Instance)表示的是数据库中一个模型对象的一行记录。它可以完成如下操作:

1. 创建对象实例可以通过Model类直接创建:

obj = Model(attr1=val1, attr2=val2)
obj.save()

或者使用objects管理器的create()方法:

obj = Model.objects.create(attr1=val1, ...)

2. 更新对象实例先获取实例,修改字段后保存:

obj = Model.objects.get(id=1) 
obj.attr1 = new_value
obj.save()

3. 删除对象实例获取实例后调用delete():

obj = Model.objects.get(id=1)
obj.delete()

所以Django模型实例表示单个对象,主要用于对象的CRUD操作。它和QuerySet的区别在于是一个对象 VS 一组对象。

QuerySet 适用于需要查找多个对象或进行聚合操作的场景,而 Instance 适用于单独对象的创建、修改和删除操作。

三、APIView(在 view.py 中)

 

 

APIview 是 Django REST Framework 提供的一个视图类。它和 Django 中的 view 类有些相似,但是又有一些不同之处。APIview 可以处理基于 HTTP 协议的请求,并返回基于内容协商的响应,它旨在提供一个易于使用且灵活的方式来构建 API 视图。

- APIView继承自Django的View类,提供了许多处理HTTP请求的方法,比如get、post等。

- APIView实现了内容协商,可以根据请求头中的Accept信息自动返回JSON或其他格式。

- APIView具有请求解析器,可以解析请求的数据,并将请求数据绑定到请求对象上。- APIView可以轻松地构建基于类的视图逻辑,通过继承和组合来重用通用逻辑。

- APIView支持基于函数的视图行为,可以使用@action装饰器来实现。

- APIView比Django的View类更偏向于构建Web API。它提供了对请求和响应的更多控制能力。

- APIView需要与序列化器Serializer配合使用,来序列化复杂数据。

总结起来,APIView是一个专门用来构建Web API的类,它建立在Django的通用View组件之上,提供了对requests和responses的控制,内容协商等功能,以及对Serializer的集成,可以更便捷地构建灵活的API。 

这里是一个使用APIView的代码示例:(可与项目给出的示例做对比补充)

from rest_framework.views import APIView
from rest_framework.response import Responseclass HelloView(APIView):def get(self, request):content = {'message': 'Hello, World!'}return Response(content)def post(self, request):name = request.data.get('name')content = {'message': 'Hello, {}!'.format(name)}return Response(content)

这个示例中定义了一个简单的HelloView,继承自APIView。

- get() 方法处理GET请求,返回一个字典作为响应数据。

- post() 方法处理POST请求,从请求数据中获取name参数,返回个性化的问候信息。

- APIView会自动根据请求方法调用对应的get或post方法。

- 返回Response对象,APIView会处理内容协商,转换数据格式。这样,就可以快速构建一个支持GET/POST的API端点了。我们还可以利用APIView提供的其他功能,比如解析器、身份验证等来构建更强大的API。APIView作为Django REST framework的基础,提供了简洁而不失灵活性的API视图构建方式。

# 面向对象编程
from django.shortcuts import render
from rest_framework.decorators import api_view
from .models import *
from rest_framework.response import Response
from rest_framework.views import APIView
#### APIViewclass GetGoods(APIView):def get(self, request):data = Goods.objects.all()serializer = GoodsSerializer(instance=data, many=True)print(serializer.data)return Response(serializer.data)def post(self, request):# 从请求数据中提取字段request_data = {"category": request.data.get("Goodscategory"),"number": request.data.get("number"),"name": request.data.get("name"),"barcode": request.data.get("barcode"),"spec": request.data.get("spec"),"shelf_life_days": request.data.get("shelf_life_days"),"purchase_price": request.data.get("purchase_price"),"retail_price": request.data.get("retail_price"),"remark": request.data.get("remark"),}# 使用 create() 方法创建新的商品对象new_goods = Goods.objects.create(**request_data)# 对创建的对象进行序列化,并作为响应返回serializer = GoodsSerializer(instance=new_goods)return Response(serializer.data)# 面向对象编程class FilterGoodsCategoryAPI(APIView):# request 表示当前的请求对象# self 表示当前实例对象def get(self, request, format=None):print(request.method)return Response('ok')def post(self, request, format=None):print(request.method)return Response('ok')def put(self, request, format=None):print(request.method)return Response('ok')

实现了一个简单的商品信息获取接口。主要逻辑是:

1. 从Goods模型中获取所有商品对象数据

2. 用GoodsSerializer序列化器对商品数据进行序列化

3. 将序列化后的数据通过Response返回这样就实现了一个获取所有商品信息的API端点。

需要注意的是:

- APIView会自动根据请求方法调用对应的方法处理器,如这里的get()来处理GET请求。

- 数据需要序列化后才能作为JSON响应返回,这里使用rest_framework的Serializer完成。

- Response包含了内容协商、状态码等响应处理功能。

使用APIView的优点是:

- 继承APIView就直接拥有了请求调度、响应处理等功能- 可以通过面向对象的方式组织视图逻辑,扩展灵活- 结合Serializer可以快速实现序列化与响应总之,这是一个典型的使用APIView构建API的示例,利用其提供的封装与便利性来简化视图开发。

def post(self, request):# 从请求数据中提取字段request_data = {"category": request.data.get("Goodscategory"),"number": request.data.get("number"),"name": request.data.get("name"),"barcode": request.data.get("barcode"),"spec": request.data.get("spec"),"shelf_life_days": request.data.get("shelf_life_days"),"purchase_price": request.data.get("purchase_price"),"retail_price": request.data.get("retail_price"),"remark": request.data.get("remark"),}

这个post方法实现了从请求数据中提取需要的参数来创建商品的功能。主要逻辑:

1. 从request.data中获取前端传来的各个字段的数据

2. 将获取到的数据保存到一个字典request_data中

3. request_data中的key就是模型的字段名,value是从请求中获取到的值

4. 这样就可以直接用这个request_data字典来创建商品实例:

goods = Goods.objects.create(**request_data)

这种字典参数的解包语法可以直接把字典的值赋给模型的对应字段。

5. 最后可能需要返回创建好的商品数据给前端:

serializer = GoodsSerializer(goods)
return Response(serializer.data)

这样,我们就可以通过接受用户输入的数据来创建新的商品,并返回结果。这种从请求中提取参数,然后直接解包传递的方式可以简化创建对象的代码。配合DRF的Serializer来序列化和响应数据,可以快速构建出创建对象的API。

# 面向对象编程class FilterGoodsCategoryAPI(APIView):# request 表示当前的请求对象# self 表示当前实例对象def get(self, request, format=None):print(request.method)return Response('ok')def post(self, request, format=None):print(request.method)return Response('ok')def put(self, request, format=None):print(request.method)return Response('ok')

实现了一个商品分类过滤的API视图,演示了APIView中如何根据不同的HTTP方法来处理不同的业务逻辑。

主要逻辑:

- 定义了FilterGoodsCategoryAPI类,继承APIView

- 分别实现了get(), post(), put()方法来处理不同的HTTP请求方法

- 在每个方法内部,打印了request.method,用于验证接收到的请求方法

- 然后直接返回字符串'ok'作为响应这样,当一个GET请求发送到这个视图的时候,会调用get()方法,并在终端打印输出“GET”。对于POST或PUT请求,也会分别调用对应的方法,并打印“POST”或“PUT”。APIView会自动根据请求方法将不同的请求分发到对应方法进行处理。我们可以在每个方法内实现真正的业务逻辑,比如获取分类、添加商品等。这种面向对象的类视图可以帮助我们更好地组织代码,利用APIView的请求分发来处理不同的请求与业务逻辑。

所以这是一个使用APIView的典型示例,演示了其根据请求方法调度到对应方法的功能。

未完待续

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/93932.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

复数

文章目录 复数虚数 i i i几何解释 复数复数的性质复数的加法1. 加实部2. 加虚部3. 组合实部和虚部复数加法的几何解释总结 复数减法1. 减实部2. 减虚部3. 组合实部和虚部复数减法的几何解释总结 复数乘法1. 展开乘法2. 使用 i 2 − 1 i^2 -1 i2−13. 合并实部和虚部复数乘法…

conda常用命令及国内镜像源

conda命令使用介绍 启动conda source ~/.bashrc帮助目录 conda create -h检查conda版本 conda --version升级当前版本的conda conda update conda环境管理 列出所有的环境 conda info -e conda env list安装一个不同版本的python新环境 conda create --name py27 pytho…

内网穿透-外远程连接中的RabbitMQ服务

文章目录 前言1.安装erlang 语言2.安装rabbitMQ3. 内网穿透3.1 安装cpolar内网穿透(支持一键自动安装脚本)3.2 创建HTTP隧道 4. 公网远程连接5.固定公网TCP地址5.1 保留一个固定的公网TCP端口地址5.2 配置固定公网TCP端口地址 前言 RabbitMQ是一个在 AMQP(高级消息队列协议)基…

实现动画的连续展示 JAVA

目录 1、前言:2、图片的展示以及自动关闭:3、动画的连续展示: 1、前言: 要实现动画的流畅展示需要在能展示图片的基础上对图片进行关闭,再切换下一张图片,这要关闭窗口,与延时函数以及while函数…

Grafana Prometheus 通过JMX监控kafka 【2023最新方式】

第三方kafka exporter方案 目前网上关于使用Prometheus 监控kafka的大部分资料都是使用一个第三方的 kafka exporter,他的原理大概就是启动一个kafka客户端,获取kafka服务器的信息,然后提供一些metric接口供Prometheus使用,随意它…

【C语言】回调函数,qsort排序函数的使用和自己实现,超详解

文章目录 前言一、回调函数是什么二、回调函数的使用1.使用标准库中的qsort函数2.利用qsort函数对结构体数组进行排序 三、实现qsort函数总结 先记录一下访问量突破2000啦,谢谢大家支持!!! 这里是上期指针进阶链接,方便…

【GitLab私有仓库】如何在Linux上用Gitlab搭建自己的私有库并配置cpolar内网穿透?

文章目录 前言1. 下载Gitlab2. 安装Gitlab3. 启动Gitlab4. 安装cpolar5. 创建隧道配置访问地址6. 固定GitLab访问地址6.1 保留二级子域名6.2 配置二级子域名 7. 测试访问二级子域名 前言 GitLab 是一个用于仓库管理系统的开源项目,使用Git作为代码管理工具&#xf…

数据结构单链表

单链表 1 链表的概念及结构 概念:链表是一种物理存储结构上非连续、非顺序的存储结构,数据元素的逻辑顺序是通过链表中的指针链 接次序实现的 。 在我们开始讲链表之前,我们是写了顺序表,顺序表就是类似一个数组的东西&#xff0…

UG NX二次开发(C#)-CAM-获取刀具类型

文章目录 1、前言2、UG NX中的刀具类型3、获取刀具类型3.1 刀具类型帮助文档1、前言 在UG NX的加工模块,加工刀具是一个必要的因素,其包括了多种类型的类型,有铣刀、钻刀、车刀、磨刀、成型刀等等,而且每种刀具所包含的信息也各不相同。想获取刀具的信息,那就要知道刀具的…

uniapp安卓ios打包上线注意事项

1、安卓包注意事项 隐私政策弹框提示 登录页面隐私政策默认不勾选隐私政策同意前不能获取用户权限APP启动时,在用户授权同意隐私政策前,APP及SDK不可以提前收集和使用IME1、OAID、IMS1、MAC、应用列表等信息 ios包注意事项 需要有注销账号的功能 3、安…

Octree八叉树python

原理 简单示例: 假设我们有以下一组三维点云数据: points [[0.1, 0.1, 0.1],[0.4, 0.1, 0.1],[0.1, 0.4, 0.1],[0.4, 0.4, 0.1],[0.1, 0.1, 0.4],[0.4, 0.1, 0.4],[0.1, 0.4, 0.4],[0.4, 0.4, 0.4], ] 我们可以使用八叉树将这些点云数据存储在三维空…

学习笔记|基于Delay实现的LED闪烁|模块化编程|SOS求救灯光|STC32G单片机视频开发教程(冲哥)|第八集:实现LED闪烁(下)

文章目录 2 函数的使用1.函数定义(需要带类型)2.函数声明(需要带类型)3.函数调用 3 新建文件,使用模块化编程新建xxx.c和xxx.h文件xxx.h格式:调用头文件验证代码调用:完整的文件结构如下&#x…

spring入门基本介绍及注入方式---详细介绍

一,spring的简介 Spring是一个开源框架,它由Rod Johnson创建。它是为了解决企业应用开发的复杂性而创建的。 提供了许多功能强大且易于使用的特性,使得开发者能够更加轻松地构建可维护且可扩展的应用程序,简单来说: Spring使用基…

内网穿透实战应用——【通过cpolar分享本地电脑上有趣的照片:发布piwigo网页】

通过cpolar分享本地电脑上有趣的照片:发布piwigo网页 文章目录 通过cpolar分享本地电脑上有趣的照片:发布piwigo网页前言1. 设定一条内网穿透数据隧道2. 与piwigo网站绑定3. 在创建隧道界面填写关键信息4. 隧道创建完成 总结 前言 首先在本地电脑上部署…

最新ChatGPT网站AI系统源码+详细图文搭建教程/支持GPT4.0/AI绘画/H5端/Prompt知识库/

一、前言 SparkAi系统是基于国外很火的ChatGPT进行开发的Ai智能问答系统。本期针对源码系统整体测试下来非常完美,可以说SparkAi是目前国内一款的ChatGPT对接OpenAI软件系统。 那么如何搭建部署AI创作ChatGPT?小编这里写一个详细图文教程吧&#xff01…

Linux学习之iptables的nat表

iptables -t nat 命令 规则链 规则是格式命令。 PREROUTING一般用于内网,用于目的地址转换。 POSTROUTING一般用于外网,用于源地址转换。 iptables -t nat -A PREROUTING -i eth0 -d 114.115.116.117 -p tcp --dport 80 -j DNAT --to-destination 10.0.0…

Docker基础入门:常规软件安装与镜像加载原理

Docker基础入门:常规软件安装与镜像加载原理 一、Docker常规软件安装1.1、部署nginx1.2、部署tomcat1.3、部署elasticsearch1.4、如何部署kibana-->连接elasticsearch1.5、部署可视化工具 二、 镜像加载原理2.1、镜像是什么2.2、Docker镜像加速原理2.3、分层理解…

HTTP--Request详解

请求消息数据格式 请求行 请求方式 请求url 请求协议/版本 GET /login.html HTTP/1.1 请求头 客户端浏览器告诉服务器一些信息 请求头名称: 请求头值 常见的请求头: User-Agent:浏览器告诉服务器,我访问你使用的浏览器版本信息 可…

软件需求-架构师之路(五)

软件需求 软件需求: 指用户 对系统在功能、行为、性能、设计约束等方面的期望。 分为 需求开发 和 需求管理 两大过程。 需求开发: 需求获取需求分析需求定义(需求规格说明书)需求验证:拉客户一起评审&#xff0c…

Qt扫盲-Qt Paint System 概述

Qt Paint System 概述 一、概述二、绘图设备和后端1. Widget2. Image3. Pixmap4. OpenGL绘制设备5. Picture6. 自定义绘制后端 三、绘图与填充1. Drawing2. 填充 Filling 四、坐标系统1. 渲染Window-Viewport转换 五、读写图像文件1. QMovie 六、绘图相关设备 一、概述 Qt的pa…