1、前后端分离
1.1 传统的开发模式
传统的开发模式流程:根据url访问视图函数,在视图函数中进行逻辑判断、调用数据库、渲染HTML,最后再向浏览器返回HTML页面。之前功能的开发使用的就是传统开发模式。但有时候我们需要将这些内容在移动端(手机APP)或者其他设备上显示,显然使用传统的开发方式工作量太大,为了解决这个问题,可以使用前后分离开发模式。
1.2 前后端分离模式
前后端分离模型指的是后端只负责返回数据,不再负责渲染页面。前端负责渲染数据。
2、ajax
2.1 ajax介绍
AJAX(Asynchronous Javascript And XML)翻译成中文就是“异步JavaScript和XML”。即使用JavaScript语言与服务器进行异步交互,传输的数据为XML(当然,传输的数据不只是XML)还有JSON数据。
AJAX还有一个最大的特点就是,当服务器响应时,不用刷新整个浏览器页面,而是可以局部刷新。这一特点给用户的感受是在不知不觉中完成请求和响应过程。
2.2 ajax格式
使用原生的JavaScript 使用AJAX 比较麻烦,我们采用 jQuery 实现 AJAX 请求。
2.2.1 语法
$.ajax({
url: url, //请求的url
type: "get", //请求类型
data: {"goodstype_id": 1}, //发送请求时携带的数据
success: function (data) { //请求成功时调用的函数,data是请求成功后返回的数据
console.log(data)
},
error: function (error) { //请求失败时调用的函数,error是请求失败后的错误信息
console.log(error)
}
})
2.2.2 参数介绍
参数 | 描述 |
---|---|
url | 发送请求的地址 |
type | 请求方式(“POST” 或 “GET”), 默认为 “GET”。注意:其它 HTTP 请求方法如 PUT 和 DELETE 也可以使用,但仅部分浏览器支持。 |
data | 发送到服务器的数据。格式 {key:value,key:value} |
error | 请求失败时调用此函数。 |
success | 请求成功后的回调函数。参数:由服务器返回数据 |
dataType | 预期服务器返回的数据类型。如果不指定,浏览器会智能判断。 可用值: “xml”: 返回 XML 文档,可用 jQuery 处理。 “html”: 返回纯文本 HTML 信息; “script”: 返回纯文本 JavaScript 代码。 “json”: 返回 JSON 数据 。 |
3、Vue—前端渲染
使用ajax异步请求之后可以把请求到的数据渲染到页面,但这个步骤太过繁琐,为了解决这个问题,从而使用vue框架。Vue 是一套用于构建用户界面的JavaScript框架,前端使用Vue的目的就是把AJAX里面的数据绑定到前端。
3.1 下载引用
下载之后直接引用即可
第一个脚本文件是vue脚本文件,第二个文件vue-resource是使用vue发送异步请求时要导入的文件。
3.2 创建视图部分
def get_two_goods(request):
"""获取最新的两条数据"""
goods_type = request.GET.get("goods_type")
goods_list = Goods.objects.filter(goodstype=goods_type).order_by("-id")[:2]
result = [{"id": goods.id, "name": goods.name, "price": goods.price, "picture": goods.picture.name} for goods in goods_list]
# safe: 默认只支持返回字典类型数据,设置为false可以返回其他类型数据
# json_dumps_params:设置是否转换成ASCII编码,不转则为中文
return JsonResponse(result, safe=False, json_dumps_params={"ensure_ascii": False})
3.3 创建脚本并渲染
3.3.1 符号冲突
由于vue的变量也是用{{}}符号,和Django会有冲突。解决方法有两种
方法一:设置Django的verbatim标签,Django对标签内的变量不再识别
{% verbatim %}
<div id="content">{{ message }}</div>
{% endverbatim %}
方法二:修改vue变量的符号
<div id="content">[[ message ]]</div>
new Vue({
"el": "#content",
"delimiters": ["[[","]]"],
"data":{
"message": "123"
}
})
3.3.2 渲染数据
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<script src="/static/buyer/js/vue.min.js"></script>
<script src="/static/buyer/js/vue-resource.min.js"></script>
<body>
<div id="content">
[[ message ]]
</div>
</body>
<script>
new Vue({
"el": "#content", //id下的dom与vue绑定
"delimiters": ["[[","]]"], //修改vue获取变量的方式
"data":{ //数据
"message": "123"
},
method:{}
})
</script>
</html>
3.4 常用的基本语法
3.4.1 插值操作
插值操作就是将Vue对象中的数据显示到页面上。
<div id="content">
[[message]]
</div>
</body>
<script>
new Vue({
"el": "#content",
"delimiters": ["[[","]]"],
"data":{
"message": "123"
}
})
</script>
3.4.2 v-bind
属性绑定,如果属性值需要字符串和变量拼接后得到时,字符串要加上引号,如下面a标签的href属性,若拼接不正确会导致页面渲染失败。
<div id="content">
<p v-bind:style="color">123</p>
<a v-bind:href="'/buyer/goods_detail?goods_id='+[[ goods_id ]]">
</div>
</body>
<script>
new Vue({
"el": "#content",
"delimiters": ["[[","]]"],
"data":{
"color": "color:red",
"goods_id": 1
}
})
</script>
3.4.3 v-if
指令用于条件性地渲染一块内容。这块内容只会在指令的表达式返回 true值的时候被渲染。
<div id="content">
<p v-if="age > 18">成年了</p>
</div>
</body>
<script>
new Vue({
"el": "#content",
"delimiters": ["[[","]]"],
"data":{
"age": 20
}
})
</script>
3.4.4 v-for
循环展示数据
<div id="content">
<p v-for="i in list">[[ i ]]</p>
</div>
</body>
<script>
new Vue({
"el": "#content",
"delimiters": ["[[","]]"],
"data":{
"list": ['1','2','3']
}
})
</script>
3.4.5 v-on
可以用 v-on 指令监听 DOM 事件,并在触发时运行一些 JavaScript 代码。
<div id="content">
<input type="button" value="点击" v-on:click="my_test()">
</div>
</body>
<script>
new Vue({
"el": "#content",
"delimiters": ["[[","]]"],
"data":{
},
methods:{
my_test: function () {
console.log(1)
alert("点击事件")
}
}
})
</script>
示例:
<div id="content">
[[message]]
<p v-bind:style="color">123</p>
<p v-if="age > 18">成年了</p>
<p v-for="i in list">[[ i ]]</p>
<input type="button" value="点击" v-on:click="my_test">
</div>
</body>
<script>
new Vue({
"el": "#content",
"delimiters": ["[[","]]"],
"data":{
"message": "123",
"color": "color:red",
"age": 20,
"list": ['1','2','3']
},
methods:{
my_test: function () {
console.log(1)
alert("点击事件")
}
}
})
</script>
3.5 异步请求
有时候我们需要发送异步请求来获取并渲染数据,vue-resource提供了这个功能,它不仅可以便捷的给dom绑定数据,还可以发送多个异步请求。
3.5.1 导包
参考3.1
3.5.2 基本格式
// this.$http 固定写法
// this.$http.请求方法 比如get、post
// this.$http.请求方法(url) url:异步请求的路由
this.$http.get("/api/get_goods_list_info/?goods_type=1").then(
function (data) { // 请求成功执行的函数,data是服务器返回的数据
console.log(data)
this.goods_list = data.data.goods_page_list
this.page_range = data.data.page_range
},function (error) { // 请求失败执行的函数
console.log(error)
}
)
如果需要传递数据,可以使用 this.$http.get(url,{params : jsonData}) 格式,第二个参数jsonData 就是传到后端的数据。post 发送数据到后端,需要第三个参数 {emulateJSON:true}。
this.$http.get("/api/get_goods_list_info/",{"params":{"goods_type":1}}).then(
function (data) { // 请求成功执行的函数,data是服务器返回的数据
console.log(data)
this.goods_list = data.data.goods_page_list
this.page_range = data.data.page_range
},function (error) { // 请求失败执行的函数
console.log(error)
}
)
案例:vue获取商品列表页数据
视图函数
def get_goods_list_info(request):
goods_type_id = request.GET.get("goods_type")
goods_type = GoodsType.objects.get(id=goods_type_id)
goods_list = goods_type.goods_set.all().order_by("id")
page_num = int(request.GET.get("page", 1)) # 获取页数
page_size = 4 # 每一页展示的数据
paginator = Paginator(goods_list, page_size) # 创建分页对象
page = paginator.page(page_num) # 根据页数返回当页的数据
page_range = page.paginator.page_range # 返回range对象,分页的数量
if page_num <= 2:
my_page_range = page_range[:5]
else:
my_page_range = page_range[page_num - 3: page_num + 2]
goods_page_list = [
{"id": goods.id, "picture": goods.picture.name, "name": goods.name, "price": goods.price, "unite": goods.unite}
for goods in page]
result = {
"goods_page_list": goods_page_list,
"page_range": [i for i in my_page_range]
}
return JsonResponse(result, safe=False, json_dumps_params={"ensure_ascii": False})
前端
<div class="col-xs-12 col-sm-10">
<div class="panel panel-success">
<!-- 商品列表 -->
<div class="panel-body">
<div class="col-xs-12 col-sm-3" v-for="goods in goods_list">
<a v-bind:href="'/buyer/goods_detail?goods_id='+[[ goods.id ]]" class="thumbnail">
<img v-bind:src="'/static/'+[[ goods.picture ]]" class="img-responsive">
</a>
<p class="text-center">[[ goods.name ]]</p>
<p class="text-center myps"><span>¥[[ goods.price ]]</span><span>[[ goods.price ]]/[[ goods.unite ]]</span><span><img src="/static/buyer/images/carts.png"/></span></p>
</div>
</div>
</div>
<!-- 分页 -->
<div class="col-xs-12 col-sm-8 col-sm-offset-4">
<ul class="pagination col-xs-12 col-sm-8 col-sm-offset-2">
<li class="page-item" v-for="page in page_range">
<a class="page-link" v-on:click="get_page([[page]])">[[page]]</a>
</li>
</ul>
</div>
</div>
<script src="/static/buyer/js/vue.min.js"></script>
<script src="/static/buyer/js/vue-resource.min.js"></script>
<script>
var now_url = window.location.href // 获取当前页面url
var goods_type = now_url.split("?")[1].split("=")[1] // 获取路由上的goods_type信息
new Vue({
el: "#content",
delimiters: ["[[","]]"],
data: {
goods_list: [],
two_goods:[],
page_range:[]
},
methods: {
get_goods_list_info: function () {
this.$http.get("/api/get_goods_list_info/?goods_type="+goods_type).then(
function (data) {
console.log(data)
this.goods_list = data.data.goods_page_list
this.page_range = data.data.page_range
},function (error) {
console.log(error)
}
)
},
get_page:function (e) {
var page = e[0][0]
console.log(page)
url = "/api/get_goods_list_info/?goods_type="+goods_type+"&page="+page
console.log(url)
this.$http.get(url).then(
function (data) {
console.log(data)
this.goods_list = data.data.goods_page_list
this.page_range = data.data.page_range
},function (error) {
console.log(error)
}
)
}
},
mounted: function () { //相当于js onload表示窗体加载完毕
this.get_goods_list_info()
},
})
</script>
结果:
4、REST
REST(Representational State Transfer的简称,中文翻译为“表现层状态转移”)与技术无关,是一种软件架构风格、设计风格,而不是标准,只是提供了一组设计原则和约束条件。基于这个风格设计的软件可以更简洁,更有层次,更易于实现缓存等机制。满足这些约束条件和原则的应用程序或设计就是 RESTful。
REST需要遵循如下10条规则:
1、协议
API与用户的通信协议,总是使用HTTPS协议。
2、域名
在域名上进行区分,例如
子域名方式:
www.baidu.com 所有人都知道这是访问网站
api.baidu.com 看到这个网站就就知道返回json、xml数据。
Url上区分:
www.baidu.com/
www.baidu.com/api/ (添加一个api 目录,让人一看到就知道是一个接口)
3、版本
因为项目存在着版本迭代更新,因此建议在url上添加版本
www.baidu.com/api/v1/
http://www.example.com/app/1.0/foo
v1、1.0: 就是代表第一版。
4、路径
网络上任何东西都是资源,均使用名词表示(可复数)
通俗来说,URL不应该使用动作来描述。例如,下面是一些不符合统一接口 要求的URI: GET:/getUser/1、 POST:/createUser、PUT:/updateUser/1 、DELETE:/deleteUser/1,建议使用 /user/1
5、HTTP动词
对于资源的具体操作类型,由HTTP动词表示。
常用的HTTP动词有下面四个(括号里是对应的SQL命令)。
GET(SELECT):从服务器取出资源(一项或多项)。
POST(CREATE):在服务器新建一个资源。
PUT(UPDATE):在服务器更新资源(客户端提供改变后的完整资源)。
DELETE(DELETE):从服务器删除资源。
还有三个不常用的HTTP动词。
PATCH(UPDATE):在服务器更新(更新)资源(客户端提供改变的属性)。
HEAD:获取资源的元数据。
OPTIONS:获取信息,关于资源的哪些属性是客户端可以访问改变的。
下面是一些例子。
GET /zoos:列出所有动物园
POST /zoos:新建一个动物园(上传文件)
GET /zoos/ID:获取某个指定动物园的信息
PUT /zoos/ID:更新某个指定动物园的信息(提供该动物园的全部信息)
PATCH /zoos/ID:更新某个指定动物园的信息(提供该动物园的部分信息)
DELETE /zoos/ID:删除某个动物园
GET /zoos/ID/animals:列出某个指定动物园的所有动物
DELETE /zoos/ID/animals/ID:删除某个指定动物园的指定动物
6、过滤信息
如果记录数量很多,服务器不可能都将它们返回给用户。API应该提供参数,过滤返回结果。
下面是一些常见的参数。
?limit=10:指定返回记录的数量
?offset=10:指定返回记录的开始位置。
?page=2&per_page=100:指定第几页,以及每页的记录数。
?sortby=name&order=asc:指定返回结果按照哪个属性排序,以及排序顺序。
?animal_type_id=1:指定筛选条件
参数的设计允许存在冗余,即允许API路径和URL参数偶尔有重复。比如:
GET /zoos/ID/animals
GET /animals?zoo_id=ID
二者含义是相同的
7、状态码
服务器向用户返回的状态码和提示信息,常见的有以下一些(方括号中是该状态码对应的HTTP动词)。
200 OK - [GET]:服务器成功返回用户请求的数据
201 CREATED - [POST/PUT/PATCH]:用户新建或修改数据成功。
202 Accepted - [*]:表示一个请求已经进入后台排队(异步任务)
204 NO CONTENT - [DELETE]:用户删除数据成功。
400 INVALID REQUEST - [POST/PUT/PATCH]:用户发出的请求有错误,服务器没有进行新建或修改数据的操作
401 Unauthorized - [*]:表示用户没有权限(令牌、用户名、密码错误)。
403 Forbidden - [*] 表示用户得到授权(与401错误相对),但是访问是被禁止的。
404 NOT FOUND - [*]:用户发出的请求针对的是不存在的记录,服务器没有进行操作,该操作是幂等的。
406 Not Acceptable - [GET]:用户请求的格式不可得(比如用户请求JSON格式,但是只有XML格式)。
410 Gone -[GET]:用户请求的资源被永久删除,且不会再得到的。
422 Unprocesable entity - [POST/PUT/PATCH] 当创建一个对象时,发生一个验证错误。
500 INTERNAL SERVER ERROR - [*]:服务器发生错误,用户将无法判断发出的请求是否成功。
8、错误处理
如果状态码是4xx,服务器就应该向用户返回出错信息。一般来说,返回的信息中将error作为键名,出错信息作为键值即可。{error: “Invalid API key”}
9、返回结果
针对不同操作,服务器向用户返回的结果应该符合以下规范。
GET /collection:返回资源对象的列表(数组)
GET /collection/resource:返回单个资源对象
POST /collection:返回新生成的资源对象
PUT /collection/resource:返回完整的资源对象
PATCH /collection/resource:返回完整的资源对象
DELETE /collection/resource:返回一个空文档
10、超媒体
RESTful API最好做到Hypermedia(即返回结果中提供链接,连向其他API方法),使得用户不查文档,也知道下一步应该做什么。
比如,Github的API就是这种设计,访问api.github.com会得到一个所有可用API的网址列表。
{
“current_user_url”: “https://api.github.com/user”,
“authorizations_url”: “https://api.github.com/authorizations”,
// …
}
从上面可以看到,如果想获取当前用户的信息,应该去访问api.github.com/user,然后就得到了下面结果。
{
“message”: “Requires authentication”,
“documentation_url”: “https://developer.github.com/v3”
}
上面代码表示,服务器给出了提示信息,以及文档的网址。
11、其他
服务器返回的数据格式,应该尽量使用JSON,避免使用XML。
5、FBV和CBV
Django中关于视图有两种写法:基于函数的视图(FBV)和基于类的视图(CBV),function base view 函数式class base view 类式
5.1 FBV
函数至少有一个参数,一般名字是request,表示当前请求对象。
5.1.1 csrf
后端工程师做接口开发测试的时候用前端发送请求太过麻烦,为了解决这个问题可以使用postman工具直接发送各种请求,Django除了get请求其他请求都会进行csrf拦截,为了方便开发,测试的时候都会把csrf关闭。
from django.views.decorators.csrf import csrf_exempt
@csrf_exempt # 被装饰的函数不会被cstf拦截
def fvb(request):
pass
5.1.2 示例
视图
@csrf_exempt
def fvb(request):
if request.method == "GET":
return HttpResponse("GET...")
elif request.method == "POST":
return HttpResponse("POST...")
elif request.method == "PUT":
return HttpResponse("PUT...")
elif request.method == "DELETE":
return HttpResponse("DELETE...")
路由
path("api/fvb/", fvb)
5.2 CBV
视图类必须直接或者间接继承django.views.View类,定义方法表示接受对应的请求。方法中request表示请求对象,*args和**kwargs用来接收。
5.2.1 csrf
from django.views.decorators.csrf import csrf_exempt
@method_decorator(csrf_exempt, "dispatch") # 所有请求方法的入口在dispatch这里,装饰这个方法即不会被cstf拦截
class CBV(View):
def get(self, request, *args, **kwargs):
pass
5.2.2 示例
视图
@method_decorator(csrf_exempt, "dispatch")
class CBV(View):
def get(self, request, *args, **kwargs):
return HttpResponse("GET...")
def post(self, request, *args, **kwargs):
return HttpResponse("POST...")
def put(self, request, *args, **kwargs):
return HttpResponse("PUT...")
def delete(self, request, *args, **kwargs):
return HttpResponse("DELETE...")
路由
path("api/cbv/", CBV.as_view())
6、Django REST Framework 框架
自己写REST接口太复杂,因为规范很多,Django拥有丰富的插件,有一个关于REST的插件,使用起来比较方便。文档:https://www.django-rest-framework.org/
6.1 安装DRF框架
6.1.1 安装
pip install djangorestframework
6.1.2 app安装
6.2 使用
DRF中的序列化类可以进行数据格式的转换,例如将Python中的字典转换成JSON数据格式。json序列化:实体类(或字典)与json格式字符串转换。
6.2.1 步骤
1、创建Serializer类
在应用当中创建serializers文件用来编写序列化类
需要继承 ModelSerializer或HyperlinkedModelSerializer,后者会生成url路由。
from rest_framework import serializers
from store.models import Goods
class GoodsSerializers(serializers.ModelSerializer):
class Meta:
model = Goods
fields = "__all__"
# 也可以自定义字段
# fields = ["name", "price", "goodstype"]
# meta属性解释:
# model:对应的model类
# fields:model类要序列化的属性 “__all__” 表示所有属性
# depth:关联对象序列化的深度,默认只序列化关联对象的id,设置为1后序列化关联对象所有filelds属性
2、创建视图
from rest_framework import viewsets
from .serializers import GoodsSerializers
class GoodsViewSet(viewsets.ModelViewSet):
queryset = Goods.objects.all()
serializer_class = GoodsSerializers
def get_serializer_context(self):
"""不对字段进行处理,返回原来的数据"""
return {"view": self}
3、指出路由
# 创建DRF相关的路由
from django.urls import path, re_path
from rest_framework.routers import DefaultRouter
from api.views import GoodsViewSet
urlpatterns = [
path("test/", test)
]
router = DefaultRouter()
router.register("ser_goods", GoodsViewSet)
urlpatterns += router.urls
4、简单的分页
接口直接返回所有数据是不合适的,并且数据量太大的时候容易发生问题,所以可以使用分页来解决这个问题。DRF提供了快速的分页策略,只需要在settings当中进行配置就可以了。
6.2.2 测试
get:获取所有
get:分页获取
get:获取单个
post:新增
put:修改
delete:删除
6.3 视图类
6.3.1 继承APIView类
APIView 对Django中的django.views.View类进行了进一步封装,功能更加强大。但是不能使用DRF的分页
# serializers.py
class GoodsSerializers(serializers.ModelSerializer):
class Meta:
model = Goods
fields = "__all__"
# views.py
from rest_framework.views import APIView
from .serializers import GoodsSerializers
class SeleteGoodsByType(APIView):
"""根据商品类型查询所有商品"""
def get(self, request, *args, **kwargs):
"""接收GET请求"""
# 接收参数
goodstype_id = request.GET.get("goodstype_id")
# 查询数据
goods_qs = Goods.objects.filter(goodstype_id=goodstype_id).order_by("-id")
# 序列化
serializer = GoodsSerializers(goods_qs, many=True)
# safe: 默认只支持返回字典类型数据,设置为false可以返回其他类型数据
# json_dumps_params:设置是否转换成ASCII编码,不转则为中文
return JsonResponse(serializer.data, safe=False, json_dumps_params={"ensure_ascii": False})
# urls.py
from api.views import SeleteGoodsByType
urlpatterns = [
path("goods/", SeleteGoodsByType.as_view()),
]
6.3.2 使用mixins类(常用)
使用REST框架的mixins类,可以提高代码的重用率,使用OPP中的继承。
class CreateModelMixin(object): 增加
class ListModelMixin(object): 展示数据列表
class RetrieveModelMixin(object): 展示单条数据
class UpdateModelMixin(object): 更新单条数据
class DestroyModelMixin(object): 删除单条数据
# serializers.py
class GoodsSerializers(serializers.ModelSerializer):
class Meta:
model = Goods
fields = "__all__"
# views.py
from rest_framework import generics
from rest_framework import mixins
class SeleteGoodsByType2(generics.GenericAPIView,
mixins.ListModelMixin,
mixins.DestroyModelMixin,
mixins.RetrieveModelMixin,
mixins.UpdateModelMixin,
mixins.CreateModelMixin
):
"""获取指定商品类型下的商品"""
# 序列化类
serializer_class = GoodsSerializers
def get_queryset(self):
"""根据类型id查询并返回结果集"""
goodstype_id = self.request.GET.get("goodstype_id")
if goodstype_id:
goods_qs = Goods.objects.filter(goodstype_id=goodstype_id).order_by("-id")
else:
goods_qs = Goods.objects.all()
return goods_qs
def get(self, request, *args, **kwargs):
"""get请求,获取参数查询数据"""
pk = kwargs.get("pk") # 获取get请求的参数
if pk:
# 返回单个数据
return self.retrieve(request, *args, **kwargs)
else:
# 返回查询集合
return self.list(request, *args, **kwargs)
def post(self, request, *args, **kwargs):
"""post请求,数据的添加"""
print("request.method:", request.method) # 获取请求的方法
print("request.POST", request.POST) # 获取post发送过来的数据
print("args, kwargs:", args, kwargs) # 路由上的参数
return self.create(request, *args, **kwargs)
def put(self, request, *args, **kwargs):
"""put请求,数据的修改"""
return self.update(request, *args, **kwargs)
def delete(self, request, *args, **kwargs):
"""delete请求,数据的删除"""
return self.destroy(request, *args, **kwargs)
def get_serializer_context(self):
"""不进行字符串拼接,返回原来的数据"""
return {"view": self}
form api.views import SeleteGoodsByType2
urlpatterns = [
path("goods2/", SeleteGoodsByType2.as_view()),
re_path(r"goods2/(?P<pk>\d+)$", SeleteGoodsByType2.as_view()),
]
测试
GET:
1、根据商品类型和id查询单个数据
2、根据商品类型查询对应类型的数据(可以分页)
POST:
添加数据
PUT:
更新id为18的商品的数量
DELETE:
删除id为20的商品的数量
6.3.3 通用视图类
mixins类视图的简化
使用mixin类,我们重写了视图,使用的代码比以前略少,但我们可以更进一步。REST框架提供了一组已经混合的通用视图,我们可以使用它来进一步减少我们的views.py模块。
# serializers.py
class GoodsSerializers(serializers.ModelSerializer):
class Meta:
model = Goods
fields = "__all__"
通过查看ListAPIView可以看出,该视图即是查询集合的视图。
# ListAPIView视图源码
class ListAPIView(mixins.ListModelMixin,
GenericAPIView):
"""
Concrete view for listing a queryset.
"""
def get(self, request, *args, **kwargs):
return self.list(request, *args, **kwargs)
继承该类后开发者只需自定义查询集合语句返回query_set对象即可获取数据集合,无需再写获取集合数据的函数。
# views.py
from rest_framework import generics
class SeleteGoodsByType3(generics.ListAPIView):
"""获取指定商品类型下的商品"""
# 序列化类
serializer_class = GoodsSerializers
def get_queryset(self):
"""根据类型id查询并返回结果集"""
goodstype_id = self.request.GET.get("goodstype_id")
if goodstype_id:
goods_qs = Goods.objects.filter(goodstype_id=goodstype_id).order_by("-id")
else:
goods_qs = Goods.objects.all()
return goods_qs
def get_serializer_context(self):
"""不进行字符串拼接,返回原来的数据"""
return {"view": self}
form api.views import SeleteGoodsByType2
urlpatterns = [
path(r"goods3/", SeleteGoodsByType3.as_view()),
]
测试
获取商品类型为1的所有商品
REST还提供了很多混合的通用视图,可查看源码继承使用,例如CreateAPIView(数据添加视图)、RetrieveAPIView(查询单个数据视图)等等。虽然这种方法减少了代码量,但在某些特殊需求下不够灵活,只适用于一般业务逻辑的操作。