RESTful的学习之 序列化 和视图

---恢复内容开始---

博客参考

APIView源码解析 之 request的重新定义,和认证组件流程

refrem权限组件 和认证组件 解析器,分页,路由

RESTful 规范

restfromwork如何创建和更新数据

 

 

 

 

 

 

 

实际得到的数据

 

 更新操作

 

 

 

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)


 

 

 

 

 

 

 

 

快速实例

Quickstart

序列化

创建一个序列化类

 

注意序列化后的数据返回都要用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 temp

class 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)
View Code

 

单条数据的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('')
View Code

 

 

实际应用中的使用  重要

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当时单表  简单的查询等操作的时候 继承

posted on 2018-04-09 01:17  王大拿  阅读(419)  评论(0)    收藏  举报

导航