第三章 restframework——序列化组件

第三章 restframework——序列化组件

一、django自带的序列化组件serializers

二、restframework的序列化组件Serializer

三、restframework的序列化组件ModelSerializer

四、restframework的序列化组件超链接字段hypermedialink

五、restframework的序列化组件之请求数据校验和保存功能

六、补充内容

一、django自带的序列化组件serializers

先来看看django自带的cbv的json写法,然后我们再来看下restframework的写法

【django】序列化

class PublishView(View):
    def get(self,request):
        # 方式一
        publish_list = list(Publish.objects.all().values('name','email'))
        return HttpResponse(json.dumps(publish_list))
        # 方式二
        from django.forms.models import model_to_dict
        publish_list = Publish.objects.all()
        temp = []
        for obj in publish_list:
            temp.append(model_to_dict(obj))
        return HttpResponse(json.dumps(temp))
        # 方式三
        from django.core import serializers
        publish_list = Publish.objects.all()
        temp = serializers.serialize("json",publish_list)
        return HttpResponse(temp)

使用步骤:

导入模块

from django.core import serializers
def persons(request):
    ret = models.Person.objects.all()
    # person_list = []
    # for i in ret:
    #     person_list.append({"name":i.name,"age":i.age})
    # print(person_list)
    # import json
    # s = json.dumps(person_list)
    # print(s)
    from django.core import serializers
    s = serializers.serialize("json",ret)
    print(s)
    return HttpResponse(s)

二、restframework的序列化组件Serializer

准备工作

models.py

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

【restframework】序列化

from django.shortcuts import *
from django.views import View
from .models import Publish
from rest_framework.views import APIView
import json
# Create your views here.


from rest_framework import serializers
from rest_framework.response import Response


# 为queryset,model对象做序列化
class PublishSerializers(serializers.Serializer):
    # 下面写的字段取决于你需要哪些字段做序列化
    name = serializers.CharField()
    email = serializers.CharField()


class PublishView(View):
    def get(self, request):
        publish_list = Publish.objects.all()    #queryset对象
        pb = PublishSerializers(publish_list, many=True)
        # many = true 代表前面传入的参数是queryset对象
        print(pb.data)

        publish1 = Publish.objects.filter(pk=1).first()
        print(publish1)        # publish对象
        pb = PublishSerializers(publish1,many=False)
        # PublishSerializers(model_obj,many = false)
        # many = false 代表前面传入的参数是model对象
        # 默认many = false,所以如果是model对象可以不用写many
        print(pb.data)
        return HttpResponse(pb.data)

使用步骤:

1.导入模块

from rest_framework import serializers

2.写需要序列化的类(一般以类名+Serializers命名)

# 为queryset,model对象做序列化
class PublishSerializers(serializers.Serializer):
    # 下面写的字段取决于你需要哪些字段做序列化
    name = serializers.CharField()
    email = serializers.CharField()
    # publish=serializers.CharField(source="publish.name")
    # authors=serializers.CharField(source="authors.all")

注意:

source 如果是字段,会显示字段,如果是方法,会执行方法,不用加括号(authors=serializers.CharField(source='authors.all'))

如在模型中定义一个方法,直接可以在在source指定执行

3.在视图函数内调用序列化的类,使用restframework封装的Response类返回Response实例化对象

class PublishView(View):
    def get(self, request):
        publish_list = Publish.objects.all()    #queryset对象
        pb = PublishSerializers(publish_list, many=True)
        # many = true 代表前面传入的参数是queryset对象
        print(pb.data)

        publish1 = Publish.objects.filter(pk=1).first()
        print(publish1)        # publish对象
        pb = PublishSerializers(publish1,many=False)
        # PublishSerializers(model_obj,many = false)
        # many = false 代表前面传入的参数是model对象
        # 默认many = false,所以如果是model对象可以不用写many
        print(pb.data)
        return HttpResponse(pb.data)

注意:

many=true,代表前面传入的参数需是queryset对象,而many=false,代表前面传入的是publish对象,并非指的是1个queryset对象。

上面的演示是基于单表的简单操作,下面对Book表(存在多对多关系)进行演示

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") #这样会返回queryset对象,看起来还不是特别舒服
    authors = serializers.SerializerMethodField() # 这个方法只是为多对多服务的,下面要写类似钩子的方法
    def get_authors(self,obj):
        temp = []
        for obj in obj.authors.all():
            temp.append(obj.name)
        return temp
class BookView(APIView):
    def get(self,request):
        book_list = Book.objects.all()
        bs = BookSerializers(book_list,many=True)
        '''
        序列化BookSerializers(book_list,many=True)做了如下操作:
        temp = []
        for obj in book_list:
            temp.append({
                "title":obj.title,
                "price":obj.price,
                "pub_date":obj.pub_date,
                "publish":obj.publish,   # 这里的返回值就是上面get_authors的返回值
            })                  
        '''
        return Response(bs.data)

    def post(self,request):
        pass

三、restframework的序列化组件ModelSerializer

大幅度简化Serializer操作

 

 如果要指定一对多或多对多显示的传输字段可以参照上面重写get_方法名方法

class BookSerializers(serializers.ModelSerializer):
    class Meta:
        model = models.Book
        # fields = "__all__"
        fields=['nid','title','authors','publish']
        # exclude=('nid',)   #不能跟fields同时用
        # depth = 1    #深度控制,写 几 往里拿几层,层数越多,响应越慢,官方建议0--10之间,个人建议最多3层
    publish=serializers.SerializerMethodField()
    def get_publish(self,obj):
        return obj.publish.name
    authors=serializers.SerializerMethodField()
    def get_authors(self,obj):
        ret=obj.authors.all()
        ss=AuthorSerializer(ret,many=True)
        return ss.data

 如果要指定一对多或多对对显示的传输字段需要重写create方法

多本书的相关操作涉及get和post,那么单本书的操作则涉及get,put,delete

让我们用ModelSerializer尝试做更多操作

【books】BookModelSerializer准备

class BookModelSerializers(serializers.ModelSerializer):
    class Meta:
        model = Book # 表名
        fields = "__all__" # 默认把所有字段写入,不包括自己写的get_authors方法
        # field = ['nid','title','authors','publish'] # 指定字段
        # exclude = ('nid')  # 除了...字段,不能跟fields同时用
        # depth = 1 #深度控制,写 几 往里拿几层,层数越多,响应越慢,官方建议0--10之间,个人建议最多3层
    # 如果要指定一对多,或多对多显示的字段需要重写create方法
    publish = serializers.CharField(source='publish.pk')
    def create(self, validated_data):
        # print('validated_data',validated_data)
        book = Book.objects.create(
            title=validated_data['title'],
            price=validated_data['price'],
            pub_date=validated_data['pub_date'],
            publish_id=validated_data['publish']['pk']
        )
        book.authors.add(*validated_data['authors'])
        return book

books的【get】查看书籍

class BookView(APIView):
    def get(self,request):
        # get请求数据
        book_list = Book.objects.all()
        bs = BookModelSerializers(book_list,many=True)
        '''
        序列化BookSerializers(book_list,many=True)做了如下操作:
        temp = []
        for obj in book_list:
            temp.append({
                "title":obj.title,
                "price":obj.price,
                "pub_date":obj.pub_date,
                "publish":obj.publish,   # 这里的返回值就是上面get_authors的返回值
            })                  
        '''
        return Response(bs.data)

books的【post】添加一本书籍(添加完之后可以再尝试用Postman发送get请求查看所有书籍信息)

class BookView(APIView):
    def get(self,request):
        ...
    def post(self,request):
        # post请求数据
        bs = BookModelSerializers(data=request.data)
        if bs.is_valid():
            print(bs.validated_data)
            bs.save()  # create方法
            return Response(bs.data)
        else:
            return HttpResponse(bs.errors)

 

【bookdetail】BookDetailModelSerializer准备

先处理url 

 

bookdetail的【get】获取一本书

bookdetail的【put】修改一本书

bookdetail的【delete】获取一本书

 

四、restframework的序列化组件超链接字段hypermedialink

让我们再来写个publishdetail,你会发现里面的逻辑竟然和bookdetail一样,那restframework也有对应的方法可以简化

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


res=BookSerializers(ret,many=True,context={'request': request})

五、restframework的序列化组件之请求数据校验和保存功能

class BookSerializers(serializers.ModelSerializer):
    class Meta:
        model=Book
        fields="__all__"

#————————
class BookView(APIView):

    def post(self, request):

        # 添加一条数据
        print(request.data)

        bs=BookSerializers(data=request.data)
        if bs.is_valid():
            bs.save()  # 生成记录
            return Response(bs.data)
        else:

            return Response(bs.errors)
class BookSerializer1(serializers.Serializer):
    title=serializers.CharField(error_messages={'required': '标题不能为空'})

#这种方式要保存,必须重写create方法

通过源码查看留的校验字段的钩子函数:

#is_valid---->self.run_validation-(执行Serializer的run_validation)-->self.to_internal_value(data)---(执行Serializer的run_validation:485行)
def validate_title(self, value):
        from rest_framework import exceptions
        raise exceptions.ValidationError('看你不顺眼')
        return value

#全局
def validate(self, attrs):
    from rest_framework import exceptions
    if attrs.get('title')== attrs.get('title2'):
        return attrs
    else:
        raise exceptions.ValidationError('不想等啊')

附上代码调试用:

"""restdemo URL Configuration

The `urlpatterns` list routes URLs to views. For more information please see:
    https://docs.djangoproject.com/en/1.11/topics/http/urls/
Examples:
Function views
    1. Add an import:  from my_app import views
    2. Add a URL to urlpatterns:  url(r'^$', views.home, name='home')
Class-based views
    1. Add an import:  from other_app.views import Home
    2. Add a URL to urlpatterns:  url(r'^$', Home.as_view(), name='home')
Including another URLconf
    1. Import the include() function: from django.conf.urls import url, include
    2. Add a URL to urlpatterns:  url(r'^blog/', include('blog.urls'))
"""
from django.conf.urls import url
from django.contrib import admin
from app01 import views

urlpatterns = [
    url(r'^admin/', admin.site.urls),
    url(r'^publishes/$',views.PublishView.as_view()),
    url(r'^publishes/(?P<pk>d+)/$',views.PublishDetailView.as_view(),name='publish_detail'),
    url(r'^books/$',views.BookView.as_view()),
    url(r'^books/(d+)/$',views.BookDetailView.as_view())
]
urls.py
from django.db import models

# Create your models here.
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
models.py
from django.shortcuts import *
from django.views import View
from .models import *
import json
# Create your views here.

from rest_framework.views import APIView
from rest_framework.response import Response
from .all_serializers import *



# 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") #这样会返回queryset对象,看起来还不是特别舒服
#     authors = serializers.SerializerMethodField() # 这个方法只是为多对多服务的,下面要写类似钩子的方法
#     def get_authors(self,obj):
#         temp = []
#         for obj in obj.authors.all():
#             temp.append(obj.name)
#         return temp

class BookView(APIView):
    def get(self,request):
        # get请求数据
        book_list = Book.objects.all()
        bs = BookModelSerializers(book_list,many=True)
        '''
        序列化BookSerializers(book_list,many=True)做了如下操作:
        temp = []
        for obj in book_list:
            temp.append({
                "title":obj.title,
                "price":obj.price,
                "pub_date":obj.pub_date,
                "publish":obj.publish,   # 这里的返回值就是上面get_authors的返回值
            })                  
        '''
        return Response(bs.data)

    def post(self,request):
        # post请求数据
        bs = BookModelSerializers(data=request.data)
        if bs.is_valid():
            print(bs.validated_data)
            bs.save()  # create方法
            return Response(bs.data)
        else:
            return HttpResponse(bs.errors)


class BookDetailView(APIView):
    def get(self,request,id):
        book = Book.objects.filter(pk=id).first()
        bs = BookModelSerializers(book)
        return Response(bs.data)
    def put(self,request,id):
        book = Book.objects.filter(pk=id).first()
        # 因为是做更新操作,所以要写data=request.data,把新数据放进去
        bs = BookModelSerializers(book,data=request.data)
        if bs.is_valid():
            bs.save()
            return Response(bs.data)
        else:
            return Response(bs.errors)
    def delete(self,request,id):
        Book.objects.filter(pk=id).delete()
        # 删除操作返回空即可
        return Response()


class PublishView(APIView):
    def get(self, request):
        publish_list = Publish.objects.all()
        ps = PublishModelSerializers(publish_list, many=True)
        return Response(ps.data)
        # 取数据
        # print('restframework', request.data)
        # print('restframework type', type(request.data))
        # print('restframework', request._request.GET)
        # print('restframework type', type(request._request.GET))
        # print('restframework', request.GET)
        # print('restframework type', type(request.GET))
        # return HttpResponse('ok')
        # 序列化
        # publish_list = Publish.objects.all()    #queryset对象
        # pb = PublishSerializers(publish_list, many=True)
        # many = true 代表前面传入的参数是queryset对象
        # print(pb.data)

        # publish1 = Publish.objects.filter(pk=1).first()
        # print(publish1)        # publish对象
        # pb = PublishSerializers(publish1,many=False)
        # PublishSerializers(model_obj,many = false)
        # many = false 代表前面传入的参数是model对象
        # 默认many = false,所以如果是model对象可以不用写many
        # print(pb.data)
        # return HttpResponse(pb.data)

        # 方式一
        # publish_list = list(Publish.objects.all().values('name','email'))
        # return HttpResponse(json.dumps(publish_list))
        # 方式二
        # from django.forms.models import model_to_dict
        # publish_list = Publish.objects.all()
        # temp = []
        # for obj in publish_list:
        #     temp.append(model_to_dict(obj))
        # return HttpResponse(json.dumps(temp))
        # 方式三
        # from django.core import serializers
        # publish_list = Publish.objects.all()
        # temp = serializers.serialize("json",publish_list)
        # return HttpResponse(temp)

    def post(self, request):
        # 取数据
        # 原生的request操作
        # print('POST',request.POST)
        # print('body',request.body)
        # print(type(request))
        # from django.core.handlers.wsgi import WSGIRequest
        # print(request.data)
        # 新的request支持的操作
        # 要想使用之前request的方法
        # ret = request._request.POST
        # django将request的所有方法的数据获取进行了不同的封装
        # 而restframework将request下POST方法的数据封装成request.data
        # print('restframework',request.data)
        # print('restframework type',type(request.data))
        # return HttpResponse('post')
        ps = PublishModelSerializers(data=request.data)
        if ps.is_valid():
            print(ps.validated_data)
            ps.save()  # create方法
            return Response(ps.data)
        else:
            return Response(ps.errors)


class PublishDetailView(APIView):
    def get(self, request, pk):

        publish = Publish.objects.filter(pk=pk).first()
        ps = PublishModelSerializers(publish)
        return Response(ps.data)

    def put(self, request, pk):
        publish = Publish.objects.filter(pk=pk).first()
        ps = PublishModelSerializers(publish, data=request.data)
        if ps.is_valid():
            ps.save()
            return Response(ps.data)
        else:
            return Response(ps.errors)

    def delete(self, request, pk):
        Publish.objects.filter(pk=pk).delete()

        return Response()
view.py
from rest_framework import serializers
from .models import *

# 为queryset,model对象做序列化
# class PublishSerializers(serializers.Serializer):
#     # 下面写的字段取决于你需要哪些字段做序列化
#     name = serializers.CharField()
#     email = serializers.CharField()

class PublishModelSerializers(serializers.ModelSerializer):
    class Meta:
        model=Publish
        fields="__all__"


class BookModelSerializers(serializers.ModelSerializer):
    class Meta:
        model = Book # 表名
        fields = "__all__" # 默认把所有字段写入,不包括自己写的get_authors方法
        # field = ['nid','title','authors','publish'] # 指定字段
        # exclude = ('nid')  # 除了...字段,不能跟fields同时用
        # depth = 1 #深度控制,写 几 往里拿几层,层数越多,响应越慢,官方建议0--10之间,个人建议最多3层
    # 如果要指定一对多,或多对多显示的字段需要重写create方法
    publish = serializers.CharField(source='publish.pk')
    authors = serializers.CharField(source="authors.all")
    def create(self, validated_data):
        # print('validated_data',validated_data)
        book = Book.objects.create(
            title=validated_data['title'],
            price=validated_data['price'],
            pub_date=validated_data['pub_date'],
            publish_id=validated_data['publish']['pk']
        )
        book.authors.add(*validated_data['authors'])
        return book
all_serializers.py

六、补充内容

【get_xxx_display()方法】

models.py

ser.py

这是个固定用法,打印看下结果

 【小技巧】

可以使用https://www.json.cn来查看格式化的json

原文地址:https://www.cnblogs.com/neymargoal/p/9777322.html