基于APIView&ModelSerializer写接口

​引言,首先路由写法还是不变、视图层的视图类写法不变,在序列化类要改变写法、慢慢的靠近序列化器组件;而且需要创建关联表,因为现实生活当中不可能仅仅建单表,会使用大量的多表关联的表数据。好吧!咱们上干货把,首先准备一下路由吧,毕竟就两条代码而且配好就逻辑写完可以马上测试接口了。本篇文章重点介绍了序列化定制字段的多种方法,也演示了序列化重要字段DictField()、ListField()的用法和反序列化重要参数read_only=True和write_only=True

一、首先准备前提工作

1.模型代码

from django.db import models


class Book(models.Model):
    name = models.CharField(verbose_name='书名', max_length=32)
    price = models.CharField(verbose_name='价格', max_length=32)

    # 外键 书跟出版社是一对多
    publish = models.ForeignKey(to='Publish', on_delete=models.CASCADE)
    # 外键 书跟作者是多对多
    authors = models.ManyToManyField(to='Author')


class Publish(models.Model):
    name = models.CharField(verbose_name='出版社名称', max_length=32)
    address = models.CharField(verbose_name='出版社地址', max_length=32)


class Author(models.Model):
    name = models.CharField(verbose_name='作者姓名', max_length=32)
    phone = models.CharField(verbose_name='电话号码', max_length=11)

录入数据的顺序不能乱来,因为有外键关系、那么小编在这里详细的列出录入数据的过程
先在Author表录入两条
基于APIView&ModelSerializer写接口-LMLPHP

再在publish表录入两条
基于APIView&ModelSerializer写接口-LMLPHP

然后在book表录入两条
基于APIView&ModelSerializer写接口-LMLPHP

最后在关联表添加数据
基于APIView&ModelSerializer写接口-LMLPHP

2.路由代码

from django.contrib import admin
from django.urls import path
from app01 import views

urlpatterns = [
    path('admin/', admin.site.urls),
    path('books/', views.BookView.as_view()),
    path('books/<int:pk>/', views.BookDetailView.as_view()),
]

3.视图代码

from django.shortcuts import render
from rest_framework.views import APIView
from .models import Book
from .serializer import BookSerializer
from rest_framework.response import Response


class BookView(APIView):
    def get(self, request):  # 查询所有
        # 查询所有数据
        books = Book.objects.all()
        # 序列化数据
        ser = BookSerializer(instance=books, many=True)
        return Response(ser.data)

    def post(self, request):
        ser = BookSerializer(data=request.data)
        if ser.is_valid():
            ser.save()
            return Response({'code': 100, 'msg': '新增成功', 'result': ser.data})
        else:
            return Response({'code': 101, 'msg': ser.errors})


class BookDetailView(APIView):
    def put(self, request, pk):
        book = Book.objects.filter(pk=pk).first()
        ser = BookSerializer(data=request.data, instance=book)
        if ser.is_valid():
            ser.save()
            return Response({'code':100, 'msg':'修改成功'})
        else:
            return Response({'code':101, 'msg':ser.errors})

    def get(self, request, pk):
        book = Book.objects.filter(pk=pk)
        ser = BookSerializer(instance=book)
        return Response(ser.data)

    def delete(self, request, pk):
        Book.objects.filter(pk=pk).delete()
        return Response('删除成功')

二、继承Serializer序列化定制字段的三种方法

1.通过source关键词定制

# 用source关键字定制字段的代码

from rest_framework import serializers

class BookSerializer(serializers.Serializer):
    real_name = serializers.CharField(source='name')
    real_price = serializers.CharField(source='price')
    publish = serializers.CharField(source='publish.name')
    authors = serializers.CharField()

基于APIView&amp;ModelSerializer写接口-LMLPHP
基于APIView&amp;ModelSerializer写接口-LMLPHP
第一个解决方案是在模型表中写下面的方法
基于APIView&amp;ModelSerializer写接口-LMLPHP
第二种解决方案在序列化类处理看下面代码框
基于APIView&amp;ModelSerializer写接口-LMLPHP

2.SerializerMethodField定制

该方法能够序列化定制所有的任何的字段先想好定制成什么样子,之后新字段名跟get_后面即可,记得定制一个就一定要配合一个get_方法,可以自定义返回格式,就说明个性化能力强。


"""关键字段SerializerMethodField定制"""

class BookSerializer(serializers.Serializer):
    name = serializers.CharField()
    price = serializers.CharField()

    publish_detail = serializers.SerializerMethodField()

    def get_publish_detail(self, obj):
        return {'name':obj.publish.name, 'address':obj.publish.address}

    author_list = serializers.SerializerMethodField()

    def get_author_list(self, obj):
        list =[]
        for author in obj.authors.all():
            list.append({'name':author.name, 'phone':author.phone})
            return list

3.在模型表中写方法来定制

这个方法其实算不上方法,因为逻辑是一样的,只不过把方法写到模型表里面,而序列化类里面只需要写新定制字段,所以相当于做了所谓的解耦合吧,但是我感觉完全没这个必要,毕竟要序列化,那么属性和方法统一写到一个位置比较省心。当然这是我的个人想法,仅供参考。

基于APIView&amp;ModelSerializer写接口-LMLPHP

# 序列化类代码
class BookSerializer(serializers.Serializer):
    name = serializers.CharField()
    price = serializers.CharField()
    publish_detail = serializers.DictField()
    author_list = serializers.ListField()
# 模型表写定制方法代码
def publish_detail(self):
    return {'name': self.publish.name, 'address': self.publish.address}

def author_list(self):
    list = []
    for author in self.authors.all():
        list.append({'name': author.name, 'phone': author.phone})
        return list

基于APIView&amp;ModelSerializer写接口-LMLPHP

三、继承Serializer反序列化

当然定制字段的方法也要写,不管在序列化类里写还是模型表里写,但凡涉及到定制字段就要写定制方法,因为涉及到反序列化所以要重写create方法和update方法,而且也要写数据校验,毕竟不能前端传什么就录入,一定要有校验机制的

class BookSerializer(serializers.Serializer):
    # 如果一个字段既用来序列化又用来反序列化就不用写参数read_only或write_only
    name = serializers.CharField(max_length=8, error_messages={'max_length':'太长了'})
    price = serializers.CharField()

    # 只用来序列化 写参数read_only=True
    publish_detail = serializers.DictField(read_only=True)
    author_list = serializers.ListField(read_only=True)

    # 只用来反序列化 写参数write_only=True
    publish = serializers.CharField(write_only=True)
    author = serializers.ListField(write_only=True)

    # 反序列化要重写create方法和update方法
    def create(self, validated_data):
        # 新增一本书
        book = Book.objects.create(name=validated_data.get('name'),
                                   price=validated_data.get('price'),
                                   publish_id=validated_data.get('publish'))
        # 关联作者
        book.authors.add(*validated_data.get('author'))
        # 返回book
        return book

    def update(self, instance, validated_data):
        # 序列出数据
        instance.name = validated_data.get('name')
        instance.price = validated_data.get('price')
        instance.publish_id = validated_data.get('publish')
        # 先清空数据在add
        authors = validated_data.get('author')
        print(validated_data)
        instance.authors.clear()
        instance.authors.add(*authors)
        # 修改完保存
        instance.save()
        # 返回数据
        return instance


    # 修改要重写update
    def update(self, instance, validated_data):
        # validated_data 校验过后的数据,{name:红楼梦,price:19,publish:1,authors:[1,2]}
        instance.name = validated_data.get('name')
        instance.price = validated_data.get('price')
        instance.publish_id = validated_data.get('publish')

        # 先清空,再add
        authors = validated_data.get('authors')
        instance.authors.clear()
        instance.authors.add((*authors)

        instance.save()

        return instance

四、用ModelSerializer进行序列化与反序列化

"""ModelSerializer的用法"""


class BookSerializer(serializers.ModelSerializer):
    class Meta:
        # 跟book表关联
        model = Book
        # fields = ['写需要序列化的字段名',[]···]
        # 如果fields = '__all__'这样写就表明序列化所有字段
        fields = '__all__'
        # extra_kwargs = {'字段名': {'约束条件': 约束参数},是反序列化字段
        extra_kwargs = {'name': {'max_length': 8},
                        'publish_detail': {'read_only': True},
                        'authors_list': {'read_only': True},
                        'publish': {'write_only': True},
                        'authors': {'write_only': True}
                        }

    def validate_name(self, name):
        if name.startswith('sb'):
            raise ValidationError('书名不能以sb开头')
        else:
            return name

展示效果如下
基于APIView&amp;ModelSerializer写接口-LMLPHP

02-10 17:02