RESTful的学习之 序列化 和视图
---恢复内容开始---
APIView源码解析 之 request的重新定义,和认证组件流程
refrem权限组件 和认证组件 解析器,分页,路由
RESTful 规范



实际得到的数据

更新操作

restful协议---
url("users/",查)
url("users/add",添加)
url("users/(\d+)/edit",编辑)
url("users/(\d+)/delete",删除)
1 面向资源开发
url("users/",users)
url("books/",users)
class UserView(View):
def get():
pass
def post():
pass
def put():
pass
def delete():
pass
class BookView(View):
def get():
pass
def post():
pass
def put():
pass
def delete():
pass
2 注意后边都有一个 / 号
get books/ : 获取多个书籍
get books/2/ : 获取某一本书籍
post books :添加一本书籍1,书籍来自请求体
PUT books/1/ :对1真本书进行编辑 对哪一本书编辑就要写那个书的序号 一定要写上
PATCH books/1/ :对1真本书进行编辑
DELETE books/1/ :对1进行删除操作
3
get books/2/ : 获取某一本书籍


以books为例: (1)创建表,数据迁移 需要导入的模块: from rest_framework import viewsets from django.contrib.auth.models import User, Group from rest_framework import serializers from .models import * (2)创建表序列化类BookSerializer,这个类似于创建ModelForm class BookSerializer(serializers.HyperlinkedModelSerializer): class Meta: model=Book #这里类似于modelform的 fields="__all__" #这里也类似, (3)创建视图类:继承ModelsViewSet class BookViewSet(viewsets.ModelViewSet): queryset = Book.objects.all()# queryset这是固定写法,ModelViewSet中会使用这个参数,获取一个queryset对象 serializer_class = BookSerializer#初始化的乐行 (4) 设计url:
from rest_framework import routers
router = routers.DefaultRouter() router.register(r'books', views.BookViewSet) urlpatterns = [ url(r'^', include(router.urls)), url(r'^api-auth/', include('rest_framework.urls', namespace='rest_framework')) ]

class BookView(View): def get(self,request,*args,**kwargs): book_list=Book.objects.all() #方式1 # temp=[] # for book in book_list: # temp.append({'title':book.title,'price':book.price,'pk':book.pk}) #方式三: #优点,可以将queryset转换成json类型的数据, #但是不能反向将json数据转换成queryset对象 from django.core import serializers temp=serializers.serialize('json',book_list) return HttpResponse(temp)
快速实例
序列化
创建一个序列化类
注意序列化后的数据返回都要用restfrem_work模块中的 Respons 响应函数来返回响应
简单使用
开发我们的Web API的第一件事是为我们的Web API提供一种将代码片段实例序列化和反序列化为诸如json之类的表示形式的方式。我们可以通过声明与Django forms非常相似的序列化器(serializers)来实现。
models部分:
from django.db import models # Create your models here. class Book(models.Model): title=models.CharField(max_length=32) price=models.IntegerField() pub_date=models.DateField() publish=models.ForeignKey("Publish") authors=models.ManyToManyField("Author") def __str__(self): return self.title class Publish(models.Model): name=models.CharField(max_length=32) email=models.EmailField() def __str__(self): return self.name class Author(models.Model): name=models.CharField(max_length=32) age=models.IntegerField() def __str__(self): return self.name
views部分:
from rest_framework.views import APIView from rest_framework.response import Response from .models import * from django.shortcuts import HttpResponse from django.core import serializers #方法三之创建序列化的类 class BookSerializers(serializers.Serializer): title=serializers.CharField(max_length=32) price=serializers.IntegerField() pub_date=serializers.DateField() publish=serializers.CharField(source="publish.name") #authors=serializers.CharField(source="authors.all") authors=serializers.SerializerMethodField() def get_authors(self,obj): temp=[] for author in obj.authors.all(): temp.append(author.name) return tempclass BookViewSet(APIView): #这里传入的是APIView def get(self,request,*args,**kwargs): book_list=Book.objects.all() # 序列化方式1: # from django.forms.models import model_to_dict # import json # data=[] # for obj in book_list: # data.append(model_to_dict(obj)) # print(data) # return HttpResponse("ok") # 序列化方式2: # data=serializers.serialize("json",book_list) # return HttpResponse(data) # 序列化方式3: bs=BookSerializers(book_list,many=True)
return Response(bs.data) #序列化好的结果放在了bs.data这个参数里边了
方法四使用ModelSerializer

ModelSerializer
class BookSerializers(serializers.ModelSerializer): class Meta: model=Book fields="__all__" depth=1
提交get请求
class BookView(APIView): #方式四的写法中如何查看全部数据 def get(self,request,*args,**kwargs): #取出所有的book 数据得到一个queryset对象 book_list=Book.objects.all() #实例话一个bs对象, 因为是传入多个 这里要将many 设为True bs=BookSerializersModel(book_list,many=True) print(bs.data) #将序列化的内容返回 return Response(bs.data)
提交post请求 有校验数据正误的功能

处理post请求的代码
class BookView(APIView):
def post(self,request,*args,**kwargs): #这里调用上图中创建的类,并实例化 bs=BookSerializers(data=request.data,many=False)
#验证用户输入的数据是否符合规范 if bs.is_valid(): # print(bs.validated_data)
#如果符合规范,保存用户提交的数据 bs.save()
#并见该数据返回 return Response(bs.data) else:
#否者返回错误信息 return Response(bs.errors)
重写save中的create方法
class BookSerializers(serializers.ModelSerializer): class Meta: model=Book fields="__all__" # exclude = ['authors',] # depth=1 def create(self, validated_data): authors = validated_data.pop('authors') obj = Book.objects.create(**validated_data) obj.authors.add(*authors) return obj
重写create方法和重写update方法
# -*- coding: utf-8 -*- from rest_framework import serializers from school.models import Student, School class StudentSerializer(serializers.ModelSerializer): # 多对多时添加参数 many=True school = serializers.SlugRelatedField(read_only=False, slug_field="name", queryset=School.objects.all()) # address = serializers.CharField(source="school.address", default=None) def create(self, validated_data): # some actions instance = super().create(validated_data) return instance def update(self, instance, validated_data): user = self.context['request'].user # 请求的用户 username = self.context['request'].data.get('username') # 请求的参数username instance.name = validated_data['name'] # 请求的参数name # some actions instance.save() return instance class Meta: model = Student fields = ('id', 'name', 'age', 'school') read_only_fields = ('id', 'age')
在这里值得注意的地方主要有三点:序列化外键字段、重写create方法、重写update方法。
-
在序列化外键字段时,如果要使用被关联对象的某个字段来代表该字段,可以使用SlugRelatedField。在多对多情况下还需要添加参数many=True。当然,也可以另起一个字段来展示被关联对象某个字段的值,如
address = serializers.CharField(source="school.address", default=None)除了上面的方法,还可以使用一个更通用的方法,可以序列化非模型中的字段或者外键的外键等。
使用SerializerMethodField通过定义方法序列化:
status = serializers.SerializerMethodField()
def get_status_verbose(self, obj):
# obj表示当前模型对象
return obj.get_status_display()
-
重写create方法有两个参数,validated_data中是创建实例各字段的值,调用
super().create(validated_data)即创建了实例,返回实例对象instance。我们可以在调用父类的create方法前后作一些处理,比如在调用之前对validated_data中的字段重新赋值。当在创建实例时,如果需要附带自动创建日志实例时,可以将instance作为日志实例的外键创建日志实例。最后将创建的Student实例instance返回出去。 -
重写update方法有三个参数,instance是要修改的模型实例,validated_data是请求的参数。在update方法中,我们一般需要获取相关的数据来修改模型的值,主要可以归为三种。第一,获取用户信息,使用
self.context['request'].user。第二,请求参数中的数据,但是参数的名称与模型字段的名称不一致,无法通过validated_data获取,使用self.context['request'].data.get('username')获取。第三,请求参数中与模型字段命名一致的数据,直接使用validated_data['name']获取。获取完数据后可以对instance的字段进行一些赋值操作,最后保存instance即可。
代码
class BookView(APIView): #方式四的写法中如何查看全部数据 def get(self,request,*args,**kwargs): #取出所有的book 数据得到一个queryset对象 book_list=Book.objects.all() #实例话一个bs对象, 因为是传入多个 这里要将many 设为True bs=BookSerializersModel(book_list,many=True,context={'request':request}) # print(bs.data) #将序列化的内容返回 return Response(bs.data) def post(self,request,*args,**kwargs): print(request.data) obj=BookSerializersModel(data=request.data,context={'request':request}) if obj.is_valid(): obj.save() return Response(obj.data) else: return Response(obj.errors)
单条数据的get和put和delete请求
urlpatterns = [ url(r'^admin/', admin.site.urls), #这里注意一定要好写 $ 号,避免被截胡,无法进到响应的链接 url(r'books/$',BookView.as_view()), url(r'books/(?P<id>\d+)/$',BookDetail.as_view()), ]
class BookDetail(APIView): #查看单条数据 def get(self,request,id,*args,**kwargs): print('执行我了') #这里要注意, 要写.firsts() 否者会提示没有对应的属性 obj = Book.objects.filter(pk=id).first() if obj: # 这里注意,查询一个数据,不用写data=queryset这样的内容,之一 bs=BookSerializersModel(obj) return Response(bs.data) else: return Response() #编辑 def put(self,request,id,*args,**kwargs): #这里要写上两个参数 data=要更新的数据 instance=被更新的数据 obj=BookSerializersModel(data=request.data,instance=Book.objects.filter(pk=id).first()) if obj.is_valid(): obj.save() #如果正确,将更新完的数据返回 return Response(obj.data) else: #如果有错误将错误信息返回 return Response(obj.errors) #删除 def delete(self,request,id,*args,**kwargs): Book.objects.filter(pk=id).first().delete() return Response('')
总代码
class BookDetail(APIView): #查看单条数据 def get(self,request,id,*args,**kwargs): print('执行我了') #这里要注意, 要写.firsts() 否者会提示没有对应的属性 obj = Book.objects.filter(pk=id).first() if obj: # 这里注意,查询一个数据,不用写data=queryset这样的内容,之一 bs=BookSerializersModel(obj,context={'request':request}) return Response(bs.data) else: return Response() #编辑 def put(self,request,id,*args,**kwargs): #这里要写上两个参数 data=要更新的数据 instance=被更新的数据 obj=BookSerializersModel(data=request.data,instance=Book.objects.filter(pk=id).first(),context={'request':request}) if obj.is_valid(): obj.save() #如果正确,将更新完的数据返回 return Response(obj.data) else: #如果有错误将错误信息返回 return Response(obj.errors) #删除 def delete(self,request,id,*args,**kwargs): Book.objects.filter(pk=id).first().delete() return Response('')
实际应用中的使用 重要
from rest_framework.serializers import ModelSerializer class CourseDetailSerializerModel(ModelSerializer): # 自定义 price_police=serializers.SerializerMethodField() teacher_list=serializers.SerializerMethodField() coursedetail__xiangxi=serializers.SerializerMethodField() class Meta: model = Course # 因为这里实例话这个对象的的是一个个的queryset 的obj # 所以对象调用他的属性是不能够直接用双侠方法来找, 值能通过 obj.字段名的方法来找 # 当这个字段不是在他自己表中出现的字段 而是反响关联的字段时,所以需要自定义一个方法来查找他、 #如'coursedetail__xiangxi'这个是他反向关联的数据,所以不能直接查询出对应的数据, #只有使用自定义方法来完成查询操作 fields = ['name', 'img', 'description','price_police','coursedetail__xiangxi','teacher_list'] def get_price_police(self,obj): temp=[] for item in obj.price_police.all(): print({item.price,item.cycle}) temp.append({item.price,item.cycle}) return temp # def get_coursedetail__xiangxi(self, obj): return obj.coursedetail.xiangxi def get_teacher_list(self, obj): temp = [] for item in obj.coursedetail.teacher_set.all(): print({item.name}) temp.append({item.name}) return temp
另一种在序列化方法中自定义一个meta查询方式
class CourseSerializer(ModelSerializer): category = serializers.CharField(source='sub_category.name') #这里写的是本表的字段名加上.跨表查询的属性名 class Meta: model = models.Course fields = ['id', 'name','category',]
但是这里注意 自定义的方法中只是适用于简单的查询 如 OneToOneFiled Forenkeny 可以使用source的方式查询
但是当是 ManyToMany,GenericRelation的方式时就不可以了,因为这是比复杂的
查询的原理类似于 a=obj.sub_category b=a.name 最终将b返回
类似的原理

超链接API:Hyperlinked
class BookSerializers(serializers.ModelSerializer): publish= serializers.HyperlinkedIdentityField( view_name='publish_detail', lookup_field="publish_id", lookup_url_kwarg="pk") class Meta: model=Book fields="__all__" #depth=1

urls部分:
|
1
2
3
4
5
6
|
urlpatterns = [ url(r'^books/$', views.BookViewSet.as_view(),name="book_list"), url(r'^books/(?P<pk>\d+)$', views.BookDetailViewSet.as_view(),name="book_detail"), url(r'^publishers/$', views.PublishViewSet.as_view(),name="publish_list"), url(r'^publishers/(?P<pk>\d+)$', views.PublishDetailViewSet.as_view(),name="publish_detail"),] |
视图
何时继承何种类
1:继承Generics.GenericAPIView 和mixins 中类的试图类中 或者终极优化方案 ModelViewSet 不推荐
这种适用于简单的表单(没有什么外键关联字段)
可以少写方法(get,post,delete等)方法 直接继承mixin中的方法,但是就是表死板,缺乏定制性
但是需要在url中的 as_view({'get':'list',"post",'create',......等})写上这些内容,
2:继承APIView (推荐使用)
此种适用于在复杂的查询中使用,需要自己写 def get() def post() 等方法的类型中 在ulr 中的 .as_view()括号中不用写内容
使用混合(mixins)
实际中的应用 关于何时要加many=True
当是 准备序列化的数据是 QueryDict 类型的 无论是一条还是多条数据 都要加 many=True 因为这个类型的数据是 [obj,] (列表套对象)这种类型的
只有是单独一个对象的时候 才写 many=False
何时使继承此种类
from rest_framework.views import APIView
#创建一个视图类,用于查询等操作 class CourseView(APIView): parser_classes = [JSONParser,] renderer_classes = [JSONRenderer,] def get(self,request,pk=None): if not pk: queryset=Course.objects.all() res=CourseSerializerModel(queryset,many=True) print(res.data) return Response(res.data) else: queryset = Course.objects.filter(pk=pk).only('name','description','coursedetail__xiangxi',) # 这里要注意 当得到的内容不是一个单纯的数据对象时 如queryset = Course.objects.filter(pk=pk) #这个即使的到的数据中只有一个数据 他也是放在了QuerySet中以列表的形式传过去,所以在传入他的时候也要写上many=True print(queryset) res=CourseDetailSerializerModel(queryset,many=True) print(res.data,type(res.data)) #打印出的内容 [OrderedDict([('name', 'python 基础'), ('img', 'dsdafasdfa'), ('description', '阿海珐否的萨芬'), # ('price_police', [{1, Decimal('111.00')}, {2, Decimal('1111.00')}, {Decimal('888.00'), 3}]), # ('coursedetail__xiangxi', '就是20天不能学会啊就是20天不能学会啊就是20天不能学会啊就是'), # ('teacher_list', [{'alex'}, {'yuan'}, {'wusir'}])])] <class 'rest_framework.utils.serializer_helpers.ReturnList'> # 得到的request.data是一个orderdect return Response('dfsaf')
URL中的代码
urlpatterns = [ # url(r"books/$", BookViewSet.as_view()), url(r'^admin/', admin.site.urls), url(r'books/$',BookView.as_view(),name='book_list'),#这里的关键字参数名字一定要是pk 否则在使用mixins中的方法时会报找不到pk的错 url(r'books/(?P<pk>\d+)/$',BookDetail.as_view(),name='publish_list'), ]
继承Generics.GenericAPIView 和mixins 中额类的试图类中 这种适用于简单的表单(没有什么外键关联字段)
可以少写方法(get,post,delete等)方法 直接继承mixin中的方法,但是就是表死板,缺乏定制性
from .service.serializer import * from rest_framework import mixins from rest_framework import generics class BookView(mixins.ListModelMixin,mixins.CreateModelMixin,generics.GenericAPIView): #这两个内容必须要写上,在源码中会使用这些内容 queryset = Book.objects.all() #这个一定要写,因为genericaAPIView中需要有定义他 serializer_class=BookSerializersModel def get(self,request,*args,**kwargs): #这里是调用mixins.ListModelMixin中的self.list()方法, return self.list(request,*args,**kwargs) def post(self,request,*args,**kwargs): return self.create(request,*args,**kwargs) class BookDetail(mixins.UpdateModelMixin,mixins.RetrieveModelMixin,mixins.DestroyModelMixin,generics.GenericAPIView): # 这两个内容必须要写上,在源码中会使用这些内容 queryset = Book.objects.all() serializer_class=BookSerializersModel def put(self,request,*args,**kwargs): return self.update(request,*args,**kwargs) def delete(self,request,*args,**kwargs): return self.destroy( request, *args, **kwargs)
外部执行一个get请求时,走这里的内容
源码解析








优化方案二


优化方案二源码 一个类继承三个类 在用上边新建的类只继承者一个类即可 思路厉害

终极优化方案

url写法
urlpatterns = [ url(r'^admin/', admin.site.urls), url(r"books\.(?P<format>\w+)$",views.BookViewSet.as_view({"get":"list","post":"create"}),name="book_list"), url(r"books/$",views.BookViewSet.as_view({"get":"list","post":"create"}),name="book_list"), url(r"books/(?P<pk>\d+)/$",views.BookViewSet.as_view({"get":"retrieve","delete":"destroy","put":"update"}),name="book_detail"), url(r"books/(?P<pk>\d+)\.(?P<format>\w+)$",views.BookViewSet.as_view({"get":"retrieve","delete":"destroy","put":"update"}),name="book_detail"), ]



URL中的写法

上一节的视图部分:
from rest_framework.views import APIView from rest_framework.response import Response from .models import * from django.shortcuts import HttpResponse from django.core import serializers from rest_framework import serializers class BookSerializers(serializers.ModelSerializer): class Meta: model=Book fields="__all__" #depth=1 class PublshSerializers(serializers.ModelSerializer): class Meta: model=Publish fields="__all__" depth=1 class BookViewSet(APIView): def get(self,request,*args,**kwargs): book_list=Book.objects.all() bs=BookSerializers(book_list,many=True,context={'request': request}) return Response(bs.data) def post(self,request,*args,**kwargs): print(request.data) bs=BookSerializers(data=request.data,many=False) if bs.is_valid(): print(bs.validated_data) bs.save() return Response(bs.data) else: return HttpResponse(bs.errors) class BookDetailViewSet(APIView): def get(self,request,pk): book_obj=Book.objects.filter(pk=pk).first() bs=BookSerializers(book_obj,context={'request': request}) return Response(bs.data) def put(self,request,pk): book_obj=Book.objects.filter(pk=pk).first() bs=BookSerializers(book_obj,data=request.data,context={'request': request}) if bs.is_valid(): bs.save() return Response(bs.data) else: return HttpResponse(bs.errors) class PublishViewSet(APIView): def get(self,request,*args,**kwargs): publish_list=Publish.objects.all() bs=PublshSerializers(publish_list,many=True,context={'request': request}) return Response(bs.data) def post(self,request,*args,**kwargs): bs=PublshSerializers(data=request.data,many=False) if bs.is_valid(): # print(bs.validated_data) bs.save() return Response(bs.data) else: return HttpResponse(bs.errors) class PublishDetailViewSet(APIView): def get(self,request,pk): publish_obj=Publish.objects.filter(pk=pk).first() bs=PublshSerializers(publish_obj,context={'request': request}) return Response(bs.data) def put(self,request,pk): publish_obj=Publish.objects.filter(pk=pk).first() bs=PublshSerializers(publish_obj,data=request.data,context={'request': request}) if bs.is_valid(): bs.save() return Response(bs.data) else: return HttpResponse(bs.errors) 复制代码 通过使用mixin类编写视图: 复制代码 from rest_framework import mixins from rest_framework import generics class BookViewSet(mixins.ListModelMixin, mixins.CreateModelMixin, generics.GenericAPIView): queryset = Book.objects.all() serializer_class = BookSerializers def get(self, request, *args, **kwargs): return self.list(request, *args, **kwargs) def post(self, request, *args, **kwargs): return self.create(request, *args, **kwargs) class BookDetailViewSet(mixins.RetrieveModelMixin, mixins.UpdateModelMixin, mixins.DestroyModelMixin, generics.GenericAPIView): queryset = Book.objects.all() serializer_class = BookSerializers def get(self, request, *args, **kwargs): return self.retrieve(request, *args, **kwargs) def put(self, request, *args, **kwargs): return self.update(request, *args, **kwargs) def delete(self, request, *args, **kwargs): return self.destroy(request, *args, **kwargs) 复制代码 使用通用的基于类的视图 通过使用mixin类,我们使用更少的代码重写了这些视图,但我们还可以再进一步。REST框架提供了一组已经混合好(mixed-in)的通用视图,我们可以使用它来简化我们的views.py模块。 复制代码 from rest_framework import mixins from rest_framework import generics class BookViewSet(generics.ListCreateAPIView): queryset = Book.objects.all() serializer_class = BookSerializers class BookDetailViewSet(generics.RetrieveUpdateDestroyAPIView): queryset = Book.objects.all() serializer_class = BookSerializers class PublishViewSet(generics.ListCreateAPIView): queryset = Publish.objects.all() serializer_class = PublshSerializers class PublishDetailViewSet(generics.RetrieveUpdateDestroyAPIView): queryset = Publish.objects.all() serializer_class = PublshSerializers
////////
渲染器如果继承了 GenericaViewSet的时候会有坑:

问题和报错原因 BoreabaleAPIRender会看你的get_queryset()方法
继承GenricalAPIView的时候 不写queryset= 会报错的原因 就是在于BrowsableAOIRenderer渲染器
这里有一个断言

解决方案 当我们的视图类继承GenericViewSet的时候,
1因为只要json数据 渲染器可以自定义只写JsonRender
2写queruset
创建视图函数的时候继承的类的推荐
1当时单表 简单的查询等操作的时候 继承


浙公网安备 33010602011771号