Django REST Framework序列化组件

时间:Nov. 13, 2019 分类:

目录:

序列化组件

序列化组件的使用

serializers.py将序列化的部分写在这里

from rest_framework import serializers


class BookSerializer(serializers.Serializer):
    id = serializers.IntegerField()
    title = serializers.CharField(max_length=32)
    CHOICES = ((1, "Linux"), (2, "Django"), (3, "Python"))
    category = serializers.ChoiceField(choices=CHOICES, source="get_category_display")
    pub_time = serializers.DateField()

类视图

from rest_framework.views import APIView
from rest_framework.response import Response
from .models import Book
from .serializers import BookSerializer

class BookView(APIView):
    def get(self, request):
        book_list = Book.objects.all()
        ret = BookSerializer(book_list, many=True)
        return Response(ret.data)

添加序列化的步骤

  • 定义好model和url
  • 导入序列化组件:from rest_framework import serializers
  • 定义序列化类,继承serializers.Serializer(建议单独创建一个专用的模块用来存放所有的序列化类):class BookSerializer(serializers.Serializer)
  • 定义需要返回的字段(字段类型可以与model中的类型不一致,参数也可以调整),字段名称必须与model中的一致
  • 在GET接口逻辑中,获取QuerySet
  • 开始序列化:将QuerySet作业第一个参数传给序列化类,many默认为False,如果返回的数据是一个列表嵌套字典的多个对象集合,需要改为many=True
  • 返回:将序列化对象的data属性返回即可

序列化类中的字段名也可以与model中的不一致,但是需要使用source参数来告诉组件原始的字段名

class BookSerializer(serializers.Serializer):
    id = serializers.IntegerField()
    title = serializers.CharField(max_length=32)
    CHOICES = ((1, "Linux"), (2, "Django"), (3, "Python"))
    category = serializers.ChoiceField(choices=CHOICES, source="get_category_display")
    pub_time = serializers.DateField()
    publisher = serializers.CharField(max_length=32, source="publisher.title")

外键关系序列化

对于一对多可以直接使用publisher.title的方式

更多的是对外键关系也进行序列化

from rest_framework import serializers


class PublisherSerializer(serializers.Serializer):
    id = serializers.IntegerField()
    title = serializers.CharField(max_length=32)

class AuthorSerializer(serializers.Serializer):
    id = serializers.IntegerField()
    name = serializers.CharField(max_length=32)


class BookSerializer(serializers.Serializer):
    id = serializers.IntegerField()
    title = serializers.CharField(max_length=32)
    CHOICES = ((1, "Linux"), (2, "Django"), (3, "Python"))
    category = serializers.ChoiceField(choices=CHOICES, source="get_category_display")
    pub_time = serializers.DateField()
    publisher = PublisherSerializer()
    author = AuthorSerializer(many=True)

对于多对多需要添加many=True

反序列化

反序列化获取post等请求的数据进行校验写入数据库的操作

from rest_framework.views import APIView
from rest_framework.response import Response
from .models import Book
from .serializers import BookSerializer

class BookView(APIView):
    def get(self, request):
        book_list = Book.objects.all()
        ret = BookSerializer(book_list, many=True)
        return Response(ret.data)

    def post(self, request):
        serializer = BookSerializer(data=request.data)
        if serializer.is_valid():
            serializer.save()
            return Response(serializer.validated_data)
        else:
            return Response(serializer.errors)

这里直接使用request.data详细的原理就是

  • APIView继承了View,as_view返回的是csrf_exempt(view)跨域请求伪造保护
  • 而APIView的view而APIView的view=super(APIView, cls).as_view(**initkwargs),所以这个view还是View的as_view方法,返回的self.dispatch
  • self.dispatch使用的APIView的dispatch

dispatch中

request=initalize_request(request, *arg, **kwargs)
self.request = request
  • initalize_request返回的是一个Request(),与之前的request对象不同的是,没有get和post方法,而是有一些其他的方法取代了
  • Request中进行了重新封装,self._request=request,使用_request替换了旧的request,query_params=self._request.GET获取的GET数据,data=self._full_data获取的POST数据

反序列化对于POST请求需要重写create方法,并且区分序列化的字段

from rest_framework import serializers
from models import Book


class PublisherSerializer(serializers.Serializer):
    id = serializers.IntegerField(read_only=True)
    title = serializers.CharField(max_length=32)


class AuthorSerializer(serializers.Serializer):
    id = serializers.IntegerField(read_only=True)
    name = serializers.CharField(max_length=32)


class BookSerializer(serializers.Serializer):
    id = serializers.IntegerField(read_only=True)
    title = serializers.CharField(max_length=32)
    CHOICES = ((1, "Linux"), (2, "Django"), (3, "Python"))
    category = serializers.ChoiceField(choices=CHOICES, source="get_category_display", read_only=True)
    pub_time = serializers.DateField()
    publisher = PublisherSerializer(read_only=True)
    authors = AuthorSerializer(many=True, read_only=True)

    w_category = serializers.IntegerField(write_only=True)
    w_publisher = serializers.IntegerField(write_only=True)
    w_authors = serializers.ListField(write_only=True)

    def create(self, validated_data):
        book = Book.objects.create(
            title=validated_data["title"],
            category=validated_data["w_category"],
            pub_time=validated_data["pub_time"],
            publisher_id=validated_data["w_publisher"],
        )
        book.authors.add(*validated_data["w_authors"])
        return book

直接使用DRF的前端post数据即可

    {
        "title": "Django REST Framework(第四版)",
        "w_category": 1,
        "pub_time": "2019-11-14",
        "w_publisher": 1,
        "w_authors": [
            1, 2
        ]
    }

反序列化修改数据

from rest_framework import serializers
from models import Book


class PublisherSerializer(serializers.Serializer):
    id = serializers.IntegerField(read_only=True)
    title = serializers.CharField(max_length=32)


class AuthorSerializer(serializers.Serializer):
    id = serializers.IntegerField(read_only=True)
    name = serializers.CharField(max_length=32)


class BookSerializer(serializers.Serializer):
    id = serializers.IntegerField(read_only=True)
    title = serializers.CharField(max_length=32)
    CHOICES = ((1, "Linux"), (2, "Django"), (3, "Python"))
    category = serializers.ChoiceField(choices=CHOICES, source="get_category_display", read_only=True)
    pub_time = serializers.DateField()
    publisher = PublisherSerializer(read_only=True)
    authors = AuthorSerializer(many=True, read_only=True)

    w_category = serializers.IntegerField(write_only=True)
    w_publisher = serializers.IntegerField(write_only=True)
    w_authors = serializers.ListField(write_only=True)

    def create(self, validated_data):
        book = Book.objects.create(
            title=validated_data["title"],
            category=validated_data["w_category"],
            pub_time=validated_data["pub_time"],
            publisher_id=validated_data["w_publisher"],
        )
        book.authors.add(*validated_data["w_authors"])
        return book

    def update(self, instance, validated_data):
        instance.title = validated_data.get("title", instance.title)
        instance.category = validated_data.get("w_category", instance.category)
        instance.pub_time = validated_data.get("pub_time", instance.pub_time)
        instance.publisher_id = validated_data.get("w_publisher", instance.publisher_id)
        if validated_data.get("w_authors"):
            instance.user.set(validated_data.get("w_authors"))
        instance.save()
        return instance

view

class BookFilterView(APIView):
    def get(self, request, id):
        book_obj = Book.objects.filter(id=id).first()
        ser_obj = BookSerializer(book_obj)
        return Response(ser_obj.data)

    def put(self, request, id):
        book_obj = Book.objects.filter(id=id).first()
        ser_obj = BookSerializer(instance=book_obj, data=request.data, partial=True)
        if ser_obj.is_valid():
            ser_obj.save()
            return Response(ser_obj.validated_data)
        else:
            return Response(ser_obj.errors)

url

urlpatterns = [
    url(r'^admin/', admin.site.urls),
    url(r'^api/', include('drfdemo.urls'))
]

反序列化字段验证

单个字段验证

def validate_title(self, value):
        if "python" not in value.lower():
            raise serializers.ValidationError("title must include python")
        return value

多个字段验证

def validate(self, value):
        if "python" not in value["title"].lower() and value["w_category"] == 1:
            raise serializers.ValidationError("分类不符合要求")
        return value

字段验证器

def my_validate(value):
    if "fuck" in value.lower:
        raise serializers.ValidationError("包含敏感词汇,请重新提交")
    return value

然后使用字段验证器

title = serializers.CharField(max_length=32, validators=[my_validate])

ModelSerializer与Model相对应的序列化器

序列化

直接的创建ModelSerializer

class BookModelSerializer(serializers.ModelSerializer):
    class Meta:
        model = Book
        fields = "__all__"
        # fields = ["id", "title", "pub_time"]
        # exclude = ["user"]
        # 分别是所有字段 包含某些字段 排除某些字段

将get指向的序列化器改为BookModelSerializer

from .serializers import BookSerializer, BookModelSerializer

class BookView(APIView):
    def get(self, request):
        book_list = Book.objects.all()
        ret = BookModelSerializer(book_list, many=True)
        return Response(ret.data)

请求可以看到对应的数据

HTTP 200 OK
Allow: GET, POST, HEAD, OPTIONS
Content-Type: application/json
Vary: Accept

[
    {
        "id": 1,
        "title": "Django REST Framework(第二版)",
        "category": 1,
        "pub_time": "2019-11-12",
        "publisher": 1,
        "authors": [
            1,
            2
        ]
    },
    {
        "id": 2,
        "title": "Django REST Framework(第三版)",
        "category": 1,
        "pub_time": "2019-11-13",
        "publisher": 1,
        "authors": [
            1,
            2
        ]
    },
    {
        "id": 3,
        "title": "Django REST Framework(第四版)",
        "category": 1,
        "pub_time": "2019-11-14",
        "publisher": 1,
        "authors": [
            1,
            2
        ]
    }
]

外键关系序列化

对于显示ID的问题可以通过depth解决

class BookModelSerializer(serializers.ModelSerializer):
    class Meta:
        model = Book
        fields = "__all__"
        depth = 1

depth代表找嵌套关系的第几层,但是depth会让外键变为只读

覆盖默认字段

class BookSerializer(serializers.ModelSerializer):
    category = serializers.CharField(source="get_category_display", read_only=True)
    class Meta:
        model = Book
        fields = "__all__"
        depth = 1

使用字段验证器

class BookSerializer(serializers.ModelSerializer):
    category = serializers.CharField(source="get_category_display", read_only=True)
    class Meta:
        model = Book
        fields = "__all__"
        depth = 1
        read_only_fields = ["id"]
        extra_kwargs = {"title": {"validators": [my_validate,], 'write_only': True}}

反序列化

因为字段中支持序列化和反序列化,更简单的方式就是不使用depth,对get的数据进行处理

class BookModelSerializer(serializers.ModelSerializer):
    r_category = serializers.CharField(source="get_category_display", read_only=True)
    r_publisher = serializers.SerializerMethodField(read_only=True)
    r_authors = serializers.SerializerMethodField(read_only=True)

    def get_r_publisher(self, obj):
        publisher_obj = obj.publisher
        return {"id": publisher_obj.id, "title": publisher_obj.title}

    def get_r_authors(self, obj):
        authors_queryset = obj.authors.all()
        return [{"id": author.id, "name": author.name } for author in authors_queryset]

    class Meta:
        model = Book
        fields = "__all__"
        # depth = 1
        extra_kwargs = {
            "id": {"read_only": True},
            "publisher": {"write_only": True},
            "authors": {"write_only": True},
        }

直接获取结果或者post数据

    {
        "title": "Django REST Framework(第五版)",
        "category": 2,
        "pub_time": "2019-11-15",
        "publisher": 1,
        "authors": [
            1
        ]
    }

对于删除

def delete(self, request, id):
        query_set = Book.objects.filter(id=id).first()
        if query_set:
            query_set.delete()
            return Response("")
        else:
            return Response("删除的书籍不存在")