• DRF序列化组件


    有Django-JsonResponse序列化,为什么还要用DRF框架?

    第一版:用.values JsonResponse来实现序列化
    Json.dumps(book_list, ensure_ascii=False)方法不能序列化Date类型
    JsonResponse(book_list, safe=False, json_dumps_params={'ensure_ascii':False})可以序列化Date类型
    如果有forienkey传的就是ID,就需要自己拼,这样代码会重复
    如果用DjangoSerializer呢?

    第二版:用django serializers实现序列化
    from django.core import serializers
    ret = serializers.serialize("json", book_list_queryset, ensure_ascii=False)
    return HttpResponse(ret)
    这样的数据外键依然是ID,依然需要自己拼,跟上面的差不多

    第三版:用框架实现序列化
    pip install djangorestframework

    ------------图书的增删改查-----------

    图书列表: get请求  http://127.0.0.1:8000/serdemo/book/list 

    新增图书: post请求  http://127.0.0.1:8000/serdemo/book/list 

    图书详情:get请求  http://127.0.0.1:8000/serdemo/retrieve/<id>

    修改图书:put请求  http://127.0.0.1:8000/serdemo/retrieve/<id>

     

     models.py

    from django.db import models
    
    
    class Book(models.Model):
        title = models.CharField(max_length=32, verbose_name="图书的名称")
        CHOICES = ((1, "Python"), (2, "Go"), (3, "Linux"))
        category = models.IntegerField(choices=CHOICES, verbose_name="图书的类别")
        pub_time = models.DateField(verbose_name="图书的出版日期")
        publisher = models.ForeignKey(to="Publisher", on_delete=None)
        author = models.ManyToManyField(to="Author")
    
        def __str__(self):
            return self.title
    
    
    class Publisher(models.Model):
        title = models.CharField(max_length=32, verbose_name="出版社的名称")
    
        def __str__(self):
            return self.title
    
    
    class Author(models.Model):
        name = models.CharField(max_length=32, verbose_name="作者的姓名")
    
        def __str__(self):
            return self.name

    views.py

    from rest_framework.views import APIView
    from rest_framework.response import Response
    from .models import Book
    # from .serializers import BookSerializer
    from .model_serializer import BookSerializer
    
    
    class BookView(APIView):
        def get(self, request):
            books_list = Book.objects.all()
            ret = BookSerializer(books_list, many=True)  # 如果是单个对象就不加"many=True"
            return Response(ret.data)
    
        def post(self, request):
            serializer = BookSerializer(data=request.data)  # 放在data里,表示是反序列化的数据
            if serializer.is_valid():
                serializer.save()
                print(
                    serializer.data)  # {'id': 22, 'category_display': 'Go', 'publisher_info': {'id': 1, 'title': '春风出版社'}, 'authors_list': [{'id': 3, 'name': '张三'}, {'id': 4, 'name': '李四'}], 'title': 'GO语言编程思想', 'pub_time': '2018-12-25'}
                print(
                    serializer.validated_data)  # OrderedDict([('title', 'GO语言编程思想'), ('category', 2), ('pub_time', datetime.date(2018, 12, 25)), ('publisher', <Publisher: 春风出版社>), ('author', [<Author: 张三>, <Author: 李四>])])
                return Response(serializer.data)  # 如果用serializer.validated_data会报错:"Publish不是JSON可序列化对象"
            else:
                return Response(serializer.errors)
    
    
    class BookEditView(APIView):
        def get(self, request, id):
            try:
                book_obj = Book.objects.get(id=id)
            except Exception as e:
                return Response({"error": e.__str__()})
            ret = BookSerializer(book_obj)
            return Response(ret.data)
    
        def put(self, request, id):
            book_obj = Book.objects.get(id=id)
            # 第一个参数是要更新的对象,第二个参数是要更新的数据,第三个参数表示允许部分更新
            serializer = BookSerializer(book_obj, data=request.data, partial=True)
            if serializer.is_valid():
                serializer.save()  # 要重写serializer中的update方法
                return Response(serializer.validated_data)
            else:
                return Response(serializer.errors)
    
        def delete(self, request, id):
            book_obj = Book.objects.get(id=id)
            book_obj.delete()
            return Response("")
    
        '''
        {
        "title": "GO语言编程思想",
        "w_category": 2,
        "pub_time": "2018-12-25",
        "publisher_id": 1,
        "author_list": [3,4]
        }
        '''

    urls.py

    from django.urls import path
    from . import views
    
    app_name = 'serdemo'
    urlpatterns = [
        path("book/list", views.BookView.as_view()),
        path("retrieve/<int:id>", views.BookEditView.as_view())
    ]

    serializers.py

    from rest_framework import serializers
    from .models import Book
    
    
    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)
    
    
    def my_validator(value):
        '''
        校验器,校验Book的title字段
        :param value:
        :return:
        '''
        if "fuck" in value.lower():
            raise serializers.ValidationError("标题中不能有脏话")
        return value
    
    
    class BookSerializer(serializers.Serializer):
        id = serializers.IntegerField(required=False)  # required=False,表示这个字段不校验
        title = serializers.CharField(max_length=32, validators=[my_validator, ])  # 验证器的优先级比该字段的验证函数高
        CHOICES = ((1, "Python"), (2, "Go"), (3, "Linux"))
        # category序列化和反序列化不一样,read_only=True表示只序列化的时候用,后端传到前端是汉子
        category = serializers.ChoiceField(choices=CHOICES, source="get_category_display", read_only=True)
        # write_only只是反序列化用,前端传到后端的是数字
        w_category = serializers.ChoiceField(choices=CHOICES, write_only=True)
        pub_time = serializers.DateField()
    
        publisher = PublisherSerializer(read_only=True)
        publisher_id = serializers.IntegerField(write_only=True)
        author = AuthorSerializer(many=True, read_only=True)
        author_list = 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['publisher_id'])
            book.author.add(*validated_data['author_list'])
            return book
    
        def update(self, instance, validated_data):
            instance.title = validated_data.get("title", instance.title)
            instance.category = validated_data.get("category", instance.category)
            instance.pub_time = validated_data.get("pub_time", instance.pub_time)
            instance.publisher_id = validated_data.get("publisher_id", instance.publisher_id)
            author_list = validated_data.get("author_list")
            if author_list:
                instance.author.set(*author_list)
            instance.save()
            return instance
    
        def validate_title(self, value):
            '''
            钩子函数validate_字段,单独校验title
            :param value:
            :return:
            '''
            if "python" not in value.lower():
                raise serializers.ValidationError("标题中必须含有‘python’")
            return value
    
        def validate(self, attrs):
            w_category = attrs.get('w_category')
            publisher_id = attrs.get('publisher_id')
            if w_category == 1 and publisher_id == 1:
                return attrs
            else:
                raise serializers.ValidationError("分类及标题不符合要求")
    
    
    """
    反序列化的数据结构:
    {
        "title": "GO语言编程思想",
        "w_category": 2,
        "pub_time": "2018-12-25",
        "publisher_id": 1,
        "author_list": [3,4]
    }
    """

    model_serializers.py

    from rest_framework import serializers
    from .models import Book
    
    
    class BookSerializer(serializers.ModelSerializer):
        '''
        使用ModelSerializer反序列化,只配置class Meta就能实现。
        所以,只需要重写原字段的序列化功能就可以了,重写的时候序列化字段跟原字段不一样,
        例如category改成category_display,如果跟原字段一样就都重写了,这里只重写原字段的序列化
        同时,需要配置xx将原字段配置为只反序列化
        所以,序列化用category_display,反序列化用category
        '''
        category_display = serializers.SerializerMethodField(read_only=True)
        publisher_info = serializers.SerializerMethodField(read_only=True)
        authors_list = serializers.SerializerMethodField(read_only=True)
    
        def get_category_display(self, obj):
            return obj.get_category_display()
    
        def get_publisher_info(self, obj):
            # obj是序列化的每个Book对象,publiser是外键
            publisher_obj = obj.publisher
            return {"id": publisher_obj.id, "title": publisher_obj.title}
    
        def get_authors_list(self, obj):
            # author是多对多
            authors_queryset = obj.author.all()
            return [{"id": author_obj.id, "name": author_obj.name} for author_obj in authors_queryset]
    
        class Meta:
            model = Book
            # fields = ["id", "title", "pub_time"]
            fields = "__all__"  # 排序就列表分开写,不排序就写__all__
            # depth = 1  # 根据外键关系向下找一层,这样把外键所有字段都查出来,method_field
            extra_kwargs = {"category": {"write_only": True},
                            "publisher": {"write_only": True},
                            "author": {"write_only": True}
                            }
  • 相关阅读:
    Python编程-数据库
    Django框架之自定义分页
    Python编程-多线程
    Python编程-多进程二
    慕课学习--OSI与TCP/IP网络协议
    VMwaretools、共享文件夹、全屏
    Linux--慕课学习
    随想
    Linux--初次体验
    正则表达式——初次尝试
  • 原文地址:https://www.cnblogs.com/staff/p/12556150.html
Copyright © 2020-2023  润新知