视图集简单来说就是一群视图逻辑操作的功能合集,并可采用路由映射的方式进行功能选择,编写的内置逻辑方法不再是使用请求命名,而是使用功能来进行命名
视图集类不再实现 get、post等方法,而是实现动作 action如 list、create等,视图集只在使用 as_view方法的时候,才会将 action动作与具体请求方式对应上
比如编写一个最基本的视图集
那么他的路由映射不是之前的直接 as_view,还要对应采取对应请求进行映射
继承自 APIView,作用也与 APIView基本类似,提供了身份认证、权限校验、流量管理等
在 ViewSet中,没有提供任何动作 action方法,需要我们自己实现 action方法
说白了,ViewSet只是让我们可以在路由配置的地方可以进行请求映射,其他也没啥卵用,属于最基础的视图集基类
继承自 GenericAPIView,作用也与 GenericAPIView类似,提供了 get_object、get_queryset等方法便于列表视图与详情信息视图的开发
在 ModelViewSet从类继承 GenericAPIView,并包括用于各种动作实现方式中,通过在各种混入类的行为混合,包含了.list、.retrieve、.create、.update、.partial_update、和.destroy等方法,继承 ListModelMixin、RetrieveModelMixin、CreateModelMixin、UpdateModelMixin、DestroyModelMixin
需要至少提供 queryset和 serializer_class属性
该 ReadOnlyModelViewSet班也继承 GenericAPIView,但只是提供了读取数据的操作,含有的 action 有 list和 retrieve
需要至少提供 queryset和 serializer_class属性
创建基础视图集类,提供create,list和retrieve操作,继承GenericViewSet和混入所需的操作
出了在视图集内容所提供的内置方法之外,还可以手动编写新的功能视图函数来完成更为复杂的业务逻辑,比如实现获取一些特定条件下的数据,那么可以在视图集中直接定义该方法,以一定过滤条件获取数据并序列化返回即可
视图集很多情况下,我们都需要在获取与创建时提供不同的序列化器,除了最粗暴的直接复制视图集定义新的路由映射,还可以通过重写getserializerclass方法实现功能
对于视图集ViewSet,我们除了可以自己手动指明请求方式与动作action之间的对应关系外,还可以使用drf所提供的路由功能Routers来帮助我们快速实现路由信息,更加的方便快捷
Routers主要分如下两种
最基本的路由映射方式,只会将视图集具备的混入类功能进行路由的生成
比如我们编写一个视图集,具备有全部功能,采用ModelViewSet
创建好的路由如下所示,虽然你只看到两条路由 但其实每一条路由后面都映射了可以保留的请求方式及action的映射
对比与SimpleRouter更加高级,包含有drf根页面的路由,不只是视图集所包含的视图部分
我们看到中间还多了一些路由,是带有format正则匹配的,这些是用来获取纯粹json格式数据
比如直接通过浏览器访问接口地址,默认会返回DRF所提供的页面,但是此时数据被解析成了text/html格式,如果希望得到纯粹的json格式,那么可以利用上面DefaultRouter路由生成的新路由,直接访问如下连接,看到的就是直接的json格式返回,而不是html标签格式
如果是自定义编写的视图,是不会直接在SimpleRouter进行register时进行初始化的,所以需要我们再通过action装饰器进行维护
action
装饰器主要有两个参数
现在创建的路由对象,自定义动作的路由就是这样的
如果detail参数为True,那么此时的路由映射出来是这样的
强大的 drf 还提供了对于分页的支持,可以方便快捷的进行接口的全局分页或者局部分页设置
直接在 settings中配置即可
配置完了如果不向用,那么就直接在视图类下使用如下属性关闭全局分页功能
可以进行直接的页码处理,返回某一页分页数据
可以通过连接可选的参数进行分页单页数量大小的控制,分页数据偏移的选择
除了默认的分页样式之外,还可以通过继承分页类的方式重写分页样式,并将其作用至局部
要创建自定义的分页序列化程序类,可以继承分页基类,重写 paginatequeryset 和 getpaginated_response 方法
如希望可以返回分页数据时,带上分了多少页的信息,可以重写*getpaginatedresponse*
过滤的功能就很简单了,可以通过给定的条件进行数据库字段查询,支持精准查询和模糊查询
如果只需要简单的精准条件过滤,则可以 filterset_fields在视图或视图集上设置一个属性,列出要过滤的字段集
注意:这种方式只能是进行精准过滤,对相应字段进行精准匹配,无法做成模糊查询哦
如果想要完成基本的模糊查询,可以使用另外一种过滤引擎,叫做 SearchFilter,这是一个由 rest_framework自带的过滤引擎,直接就可以实现模糊查询哦
search_fields指明的即是一些文本字符串的字段,比如 CharField或 TextField这样的
通过路由访问时,可以传递search参数来确定要过滤的条件
除此之外,字段还可以通过在字符前面添加各种字符来限制搜索行为
以下案例主要以vue为例
安装插件
修改配置信息
corsheaders首先在Vue项目的main.js中全局配置axios
vue获取数据进行渲染删除
创建数据
修改数据
from django.contrib.auth.models import User
from django.shortcuts import get_object_or_404
from myapps.serializers import UserSerializer
from rest_framework import viewsets
from rest_framework.response import Response
class UserViewSet(viewsets.ViewSet):
"""
A simple ViewSet for listing or retrieving users.
"""
def list(self, request):
queryset = User.objects.all()
serializer = UserSerializer(queryset, many=True)
return Response(serializer.data)
def retrieve(self, request, pk=None):
queryset = User.objects.all()
user = get_object_or_404(queryset, pk=pk)
serializer = UserSerializer(user)
return Response(serializer.data)
path('userlist/', UserViewSet.as_view({'get': 'list'})),
path('userdetail/', UserViewSet.as_view({'get': 'retrieve'})
class ViewSet(ViewSetMixin, views.APIView):
"""
The base ViewSet class does not provide any actions by default.
"""
pass
class ModelViewSet(viewsets.ViewSet):
"""
Example empty viewset demonstrating the standard
actions that will be handled by a router class.
If you're using format suffixes, make sure to also include
the `format=None` keyword argument for each action.
"""
def list(self, request):
pass
def create(self, request):
pass
def retrieve(self, request, pk=None):
pass
def update(self, request, pk=None):
pass
def partial_update(self, request, pk=None):
pass
def destroy(self, request, pk=None):
pass
path('viewset/', ModelViewSet.as_view(
{'get': 'list', 'post': 'create'}
)),
path('viewset/<int:pk>/', ModelViewSet.as_view(
{
'get': 'retrieve',
'put','update',
'patch': 'partial_update',
'destroy': 'destroy'
}
))
class GenericViewSet(ViewSetMixin, generics.GenericAPIView):
"""
The GenericViewSet class does not provide any actions by default,
but does include the base set of generic view behavior, such as
the `get_object` and `get_queryset` methods.
"""
pass
from rest_framework.viewsets import GenericViewSet
from rest_framework import status
class ModelGenericViewSet(GenericViewSet):
queryset = model.objects.all()
serializer_class = Serializer
lookup_url_kwarg = 'pk'
lookup_field = 'pk'
def list(self,request):
data = self.get_queryset()
serializer = self.get_serializer(instance=data, many=True)
return Response(serializer.data)
def retrieve(self, request):
instance = self.get_object()
serializer = self.get_serializer(instance=instance)
return Response(serializer.data)
def create(self, request):
serializer = self.get_serializer(data=request.data)
serializer.is_valid(raise_exception=True)
serializer.save()
return Response(
serializer.data,
status=status.HTTP_201_CREATED
)
def put(self,request):
instance = self.get_object()
serializer = self.get_serializer(instance=instance,data=request.data)
serializer.is_valid(raise_exception=True)
instance = serializer.save()
return instance
def destroy(self, request):
instance = self.get_object()
instance.delete()
return Response(status=status.HTTP_204_NO_CONTENT)
class ModelViewSet(mixins.CreateModelMixin,
mixins.RetrieveModelMixin,
mixins.UpdateModelMixin,
mixins.DestroyModelMixin,
mixins.ListModelMixin,
GenericViewSet):
"""
A viewset that provides default `create()`, `retrieve()`, `update()`,
`partial_update()`, `destroy()` and `list()` actions.
"""
pass
from rest_framework.viewsets import ModelViewSet
class BookModelViewSet(ModelViewSet):
queryset = Book.objects.all()
lookup_field = 'pk'
lookup_url_kwarg = 'pk'
serializer_class = BookSer
path('modelviewset/',BookModelViewSet.as_view(
{'get':'list','post':'create'}
)),
path('modelviewset/<int:pk>/',BookModelViewSet.as_view(
{'get':'retrieve','put':'update','detele':'destroy'}
)),
class ReadOnlyModelViewSet(mixins.RetrieveModelMixin,
mixins.ListModelMixin,
GenericViewSet):
"""
A viewset that provides default `list()` and `retrieve()` actions.
"""
pass
from rest_framework.viewsets import ReadOnlyModelViewSet
class BookReadOnlyModelViewSet(ReadOnlyModelViewSet):
queryset = Book.objects.all()
lookup_field = 'pk'
lookup_url_kwarg = 'pk'
serializer_class = BookSer
path('readonlymodelviewset/',
BookReadOnlyModelViewSet.as_view(
{'get': 'list'}
)),
path('readonlymodelviewset/<int:pk>/', BookReadOnlyModelViewSet.as_view(
{'get': 'retrieve'}
)),
from rest_framework import mixins
class CreateListRetrieveViewSet(mixins.CreateModelMixin,
mixins.ListModelMixin,
mixins.RetrieveModelMixin,
viewsets.GenericViewSet):
"""
A viewset that provides `retrieve`, `create`, and `list` actions.
To use it, override the class and set the `.queryset` and
`.serializer_class` attributes.
"""
pass
class BookModelViewSet(ModelViewSet):
serializer_class = BookSerializer
queryset = Book.objects.all()
# 获取一定条件的书籍,比如ID大于5的
def more_than_5(self, request):
data = self.get_queryset().filter(id__gt=5)
serializer = self.get_serializer(instance=data, many=True)
return Response(serializer.data)
path('bookthan5/',
BookModelViewSet.as_view(
{'get': 'more_than_5'})
),
class ModelView(ModelViewSet):
...
def get_serializer_class(self):
if self.action == 'list':
return ListSer
if self.action == 'create':
return CreateSer
class BookModelViewSet(ModelViewSet):
serializer_class = BookSerializer
queryset = Book.objects.all()
from rest_framework import routers
# 1.实例化路由对象
router = routers.SimpleRouter()
# 2.注册生成路由
router.register('book', BookModelViewSet, basename='book')
# 3.添加路由
urlpatterns += router.urls
[
<URLPattern '^book/$' [name='book-list']>,
# 包含:获取列表get,创建一条post
<URLPattern '^book/(?P<pk>[^/.]+)/$' [name='book-detail']>
# 包含:获取一条get、更新一条put、删除一条delete
]
# 1.实例化路由对象
router = routers.SimpleRouter()
# 2.注册生成路由
router.register('路由命名', 视图集, basename='路由名称前缀')
from rest_framework import routers
# 1.实例化路由对象
router = routers.DefaultRouter()
# 2.注册生成路由
router.register('book', BookModelViewSet, basename='book')
# 3.添加路由
urlpatterns += router.urls
[
<URLPattern '^book/$' [name='book-list']>,
<URLPattern '^book\.(?P<format>[a-z0-9]+)/?$' [name='book-list']>,
<URLPattern '^book/(?P<pk>[^/.]+)/$' [name='book-detail']>,
<URLPattern '^book/(?P<pk>[^/.]+)\.(?P<format>[a-z0-9]+)/?$' [name='book-detail']>,
<URLPattern '^$' [name='api-root']>,
<URLPattern '^\.(?P<format>[a-z0-9]+)/?$' [name='api-root']>
]
<URLPattern '^book\.(?P<format>[a-z0-9]+)/?$'
[name='book-list']>,
<URLPattern '^book/(?P<pk>[^/.]+)\.(?P<format>[a-z0-9]+)/?$'
[name='book-detail']>,
<URLPattern '^\.(?P<format>[a-z0-9]+)/?$'
[name='api-root']>
http://127.0.0.1:8000/book.json
#'^book\.(?P<format>[a-z0-9]+)/?$' [name='book-list'],
path('', include(router.urls)),
from rest_framework.decorators import action
class StuModelViewSet(ModelViewSet):
...
@action(methods=["get"],detail=False)
def id_than_1(self, request): # 获取id超过1的数据们
queryset = self.get_queryset().filter(id__gt=1)
serializer = self.get_serializer(instance=queryset, many=True)
return Response(serializer.data)
'^stu/id_than_1/$'
'^stu/(?P<pk>[^/.]+)/id_than_1/$'
REST_FRAMEWORK = {
'DEFAULT_PAGINATION_CLASS':
# 'rest_framework.pagination.PageNumberPagination',
'rest_framework.pagination.LimitOffsetPagination',
'PAGE_SIZE': 2 # 每页数目
}
class View(...):
pagination_class = None
http://127.0.0.1:8000/user/?limit=2&page=2
# limit:每页的数据大小
# page:当前的页码
http://127.0.0.1:8000/path/?limit=2&offset=2"
# limit:每页的数据大小
# offset:从某一个数据位置开始偏移
from rest_framework.pagination import PageNumberPagination
class StandardResultsSetPagination(PageNumberPagination):
page_size = 2 # 每页数据量
page_size_query_param = 'page_size' # 每页大小连接传递参数
max_page_size = 5 # 每页数据量最多
page_query_param = 'page' # 路由传递获取的页码参数 key 值
class SomeModelViewSet(ModelViewSet):
...
pagination_class = StandardResultsSetPagination
from rest_framework.utils.urls import replace_query_param
class CustomPagination(PageNumberPagination):
def get_paginated_response(self, data):
url = self.request.build_absolute_uri()
num_pages = self.page.paginator.num_pages
return Response({
'links': {
'next': self.get_next_link(),
'previous': self.get_previous_link()
},
'count': self.page.paginator.count,
# 'num_pages': self.page.paginator.num_pages,
'num_pages': [
replace_query_param(
url, # 接口主机地址
self.page_query_param, # get传参时的页码标示
num_page # 带拼接的页码
)
for num_page in range(1, num_pages + 1) # 推导式
],
'results': data
})
pip3 install django-filter
REST_FRAMEWORK = {
'DEFAULT_FILTER_BACKENDS':
['django_filters.rest_framework.DjangoFilterBackend']
}
from django_filters.rest_framework import DjangoFilterBackend
class UserListView(generics.ListAPIView):
...
filter_backends = [DjangoFilterBackend]
class List(generics.ListAPIView):
queryset = model.objects.all()
serializer_class = Serializer
filter_backends = [DjangoFilterBackend]
filterset_fields = ['name',...]
from django_filters import FilterSet, rest_framework
class UserFilter(FilterSet):
name = rest_framework.CharFilter(
field_name="name", # 模型类中的字段
lookup_expr='contains' # 过滤链式条件
)
class Meta:
model = User # 模型
fields = ['name']
filter_class = UserFilter
from rest_framework import filters
class List(generics.ListAPIView):
queryset = model.objects.all()
serializer_class = Serializer
filter_backends = [filters.SearchFilter]
search_fields = ['name',...]
search_fields = ['username', 'foreignkey__field']
...?search=char
pip install django-cors-headers
INSTALLED_APPS = [
...
'corsheaders', # 跨域
...
]
MIDDLEWARE = [
'django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware', # 添加跨域中间件
'corsheaders.middleware.CorsMiddleware',
'django.middleware.common.CommonMiddleware',
# 'django.middleware.csrf.CsrfViewMiddleware', # 关闭csrf验证
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
]
CORS_ORIGIN_ALLOW_ALL = True
import axios from 'axios' // 导入axios
axios.defaults.baseURL = "http://127.0.0.1:8000/" // 设置axios请求的默认域名,即django服务的域名
Vue.prototype.$axios = axios // 全局挂载axios
<template>
<div>
<!-- 三、循环展示所有作者 -->
<table>
<tr v-for="author in authorList" :key="author.id">
<td>{{ author.id }}</td>
<td>{{ author.name }}</td>
<td>{{ author.gender }}</td>
<td>{{ author.age }}</td>
<td>
<!--
内层元素的事件会从内向外依次触发, 叫做冒泡机制
想要阻止冒泡事件,需要 使用 事件修饰符 .stop
-->
<button @click.stop="delAuthor">删除</button>
</td>
</tr>
</table>
</div>
</template>
<script>
export default {
name: "AuthorList",
data() {
return {
authorList: []
}
},
methods: {
// 一、get请求所有作者
getAuthorList() {
this.$axios.get('author/')
.then(resp => {
console.log(resp.data)
this.authorList = resp.data
})
},
delAuthor(id) {
this.$axios.delete('author/' + id + '/')
.then(resp => {
// 删除成功,重新请求数据,刷新页面
this.getAuthorList();
})
.catch(err => {
console.log('删除失败')
})
},
},
mounted() {
// 二、挂载,让请求自动执行
this.getAuthorList();
}
}
</script>
<template>
<div>
<!-- 1. 使用v-model指定从表单获取数据 -->
图书标题: <input type="text" v-model="title">
图书价格: <input type="text" v-model="price">
<!-- 3. 使用事件绑定,将 方法和按钮进行绑定 -->
<button @click="btn">添加</button>
</div>
</template>
<script>
export default {
name: "Book",
data() {
return {
title: '',
price: ''
}
},
methods: {
btn() {
// 2. 定义方法, 使用axios发送post请求,并携带参数
this.$axios.post('book/', {'title': this.title, 'price': this.price})
.then(resp => {
// 添加成功,输出响应数据
console.log(resp.data)
})
.catch(err => {
// 添加失败,输出错误信息
console.log(err.response.data)
})
}
}
}
</script>
<template>
<div>
<!-- 修改之前,需要有原始数据 -->
<!-- 一、修改图书之前,给表单设置默认值, 原本的图书数据 -->
<!-- 二.修改同样也需要获取用户输入的新数据 -->
图书标题: <input type="text" v-model="book.title">
图书价格: <input type="text" v-model="book.price">
<!-- 四、对按钮绑定事件 -->
<button @click="updateBook">修改</button>
</div>
</template>
<script>
export default {
name: "BookUpdate",
data() {
return {
id: 1,
book: {
title: '',
price: ''
}
}
},
methods: {
// 1.1 获取id对应的图书数据
getBook() {
this.$axios.get('book/' + this.id + '/')
.then(resp => {
// 请求成功,将请求到的图书数据赋值给data中的变量,
// 而表单和data中的数据进行双向绑定
// 给data中的数据设置默认值,表单也会有默认值
this.book.title = resp.data.title
this.book.price = resp.data.price
})
},
// 三. 定义方法,使用put提交数据
updateBook() {
this.$axios.put('book/' + this.id + '/', this.book)
.then(resp => {
console.log(resp.data)
})
}
},
mounted() {
// 1.3 挂载
this.getBook();
}
}
</script>