测试开发进阶(二十九)

测试开发进阶(二十九)

欢迎关注我的公众号「测试游记」

再次优化

查看rest_framework.generics.CreateAPIView

rest_framework.generics.ListAPIView

可以看到很多重复的代码

image-20191023202028076

rest_framework.generics.ListCreateAPIView

1
2
3
4
5
6
7
8
9
10
11
class ListCreateAPIView(mixins.ListModelMixin,
mixins.CreateModelMixin,
GenericAPIView):
"""
Concrete view for listing a queryset or creating a model instance.
"""
def get(self, request, *args, **kwargs):
return self.list(request, *args, **kwargs)

def post(self, request, *args, **kwargs):
return self.create(request, *args, **kwargs)

所以直接继承

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
from projects.models import Projects
from projects.serializer import ProjectModelSerializer
from django_filters.rest_framework import DjangoFilterBackend
from rest_framework import generics


class ProjectsList(generics.ListCreateAPIView):
ordering_fields = ['name', 'leader']
queryset = Projects.objects.all()
serializer_class = ProjectModelSerializer
filter_backends = [DjangoFilterBackend]
filterset_fields = ['name', 'leader', 'tester']


class ProjectDetail(generics.RetrieveUpdateDestroyAPIView):
queryset = Projects.objects.all()
serializer_class = ProjectModelSerializer

现有问题

  • 两个类视图,不能合并
  • 有相同的get方法
  • 两个类视图所对应的url地址不一致

再次优化

优化思路:使用动作来触发,而不是请求方法

1
from rest_framework import viewsets

viewsets不再支持get/post/put/delete等请求方法,而只支持action动作

但是ViewSet类中没有提供get_object(),get_serializer等方法

继承viewsets.GenericViewSet

将两个类合成一个类

修改url

1
2
3
4
5
6
7
8
9
10
11
12
13
14
from django.urls import path
from projects import views

urlpatterns = [
path('project/', views.ProjectsViewSet.as_view({
'get': 'list',
'post': 'create'
}), name='projects_list'),
path('project/<int:pk>/', views.ProjectsViewSet.as_view({
'get': 'retrieve',
'put': 'update',
'delete': 'destroy'
}), name='project_detail')
]

viewsets.ModelViewSet

1
2
3
4
5
6
7
8
9
10
11
12
from projects.models import Projects
from rest_framework import viewsets
from projects.serializer import ProjectModelSerializer
from django_filters.rest_framework import DjangoFilterBackend


class ProjectsViewSet(viewsets.ModelViewSet):
ordering_fields = ['name', 'leader']
queryset = Projects.objects.all()
serializer_class = ProjectModelSerializer
filter_backends = [DjangoFilterBackend]
filterset_fields = ['name', 'leader', 'tester']

视图

Django中

  • View

DRF中

  • APIView
  • GenericAPIView
  • mixins扩展类
  • CreateAPIView(合并拓展类)

视图集

action和请求方法的映射

  • ViewSet
  • GenericViewSet
  • CreateViewSet

路由

1
from rest_framework import routers
  1. 创建SimpleRouter路由对象
  2. 注册路由

第一个参数prefix为路由前缀,一般添加为应用名称即可
第二个参数viewset为视图集「不要加as_view」

  1. 将自动生成的路由添加到列表中
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
from django.urls import path, include
from projects import views
from rest_framework import routers

# 1.创建SimpleRouter路由对象
router = routers.SimpleRouter()
# 2.注册路由
# 第一个参数prefix为路由前缀,一般添加为应用名称即可
# 第二个参数viewset为视图集「不要加as_view」
router.register(r'projects',views.ProjectsViewSet)

urlpatterns = [
# 将自动生成的路由添加到列表中
path('',include(router.urls))
]

action

自定义action

1
from rest_framework.decorators import action
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
def action(methods=None, detail=None, url_path=None, url_name=None, **kwargs):
"""
Mark a ViewSet method as a routable action.

Set the `detail` boolean to determine if this action should apply to
instance/detail requests or collection/list requests.
"""
methods = ['get'] if (methods is None) else methods
methods = [method.lower() for method in methods]

assert detail is not None, (
"@action() missing required argument: 'detail'"
)

# name and suffix are mutually exclusive
if 'name' in kwargs and 'suffix' in kwargs:
raise TypeError("`name` and `suffix` are mutually exclusive arguments.")

def decorator(func):
func.mapping = MethodMapper(func, methods)

func.detail = detail
func.url_path = url_path if url_path else func.__name__
func.url_name = url_name if url_name else func.__name__.replace('_', '-')
func.kwargs = kwargs

# Set descriptive arguments for viewsets
if 'name' not in kwargs and 'suffix' not in kwargs:
func.kwargs['name'] = pretty_name(func.__name__)
func.kwargs['description'] = func.__doc__ or None

return func
return decorator

可以使用action装饰器来声明自定义的动作

默认情况下,实例方法名就是动作名

methods参数用于指定该动作支持的请求方法,默认为get

detail用于指定该动作要处理的是否为详情资源对象「url是否需要传递pk值」

url.py中添加

1
2
3
path('project/names/', views.ProjectsViewSet.as_view({
'get', 'names'
}))

新增序列化器

1
2
3
4
class ProjectNameSerializer(serializers.ModelSerializer):
class Meta:
model = Projects
fields = ('id', 'name')

view中添加

1
2
3
4
5
@action(methods=['get'], detail=False)
def names(self, request, *args, **kwargs):
queryset = self.get_queryset()
serializer = ProjectNameSerializer(instance=queryset, many=True)
return Response(serializer.data)

同理添加Interface

需要通过projects/1/interfaces/来拿到id=1interfaces信息

添加序列化器

1
2
3
4
5
6
7
8
9
10
11
12
class InterfacesNameSerializer(serializers.ModelSerializer):
class Meta:
model = Interfaces
fields = ('id', 'name', 'tester')


class InterfacesByProjectIdSerializer(serializers.ModelSerializer):
interfaces_set = InterfacesNameSerializer(read_only=True, many=True)

class Meta:
model = Projects
fields = ('id', 'interfaces_sets')

添加自定义action

1
2
3
4
5
@action(detail=True)
def interfaces(self, reques, *args, **kwargs):
instance = self.get_queryset()
serializer = InterfacesByProjectIdSerializer(instance=instance)
return Response(serializer.data)

查看路由

路由

添加url_pathurl_name

1
@action(methods=['get'], detail=False, url_path='nm', url_name='url_name')

修改名称

url_path url的路径名
url_name url的别名「应用名称-url_name」

测试

1
$ http :8000/projects/names/		

image-20191023220718885

 wechat
欢迎您扫一扫上面的微信公众号,订阅我的博客!
您的支持将鼓励我继续创作!