Django Rest Framework之序列化 - Go语言中文社区

Django Rest Framework之序列化


一:安装DRF

pip install django==2.1.7
pip install djangorestframework

settings配置文件中注册rest_framework

INSTALLED_APPS = [
    ...............
    'rest_framework',
]

二:APIView

Django rest framework
APIView与View的区别:

  1. APIView继承了View
  2. APIView中重写了as_view()方法以及dispatch()方法
  3. 在dispatch()里重新封装了request对象,request = Request(),而旧的request对象变成了_request
  4. 获取get请求参数使用:request.query_params()
  5. 获取post请求参数使用:request.data()

不管是View还是APIView最开始调用的都是as_view()方法
APIView
APIView继承了View, 并且执行了View中的as_view()方法,最后把view返回了,用csrf_exempt()方法包裹后去掉了csrf的认证
那我们看看View中的as_view()方法做了什么~
APIView
在View中的as_view方法返回了view函数,而view函数执行了self.dispatch()方法,而这里的dispatch方法是APIView中的方法
APIView
我们去initialize_request中看下把什么赋值给了request,并且赋值给了self.request, 也就是我们在视图中用的request.xxx到底是什么
APIView
我们看到,这个方法返回的是Request这个类的实例对象~~我们注意我们看下这个Request类中的第一个参数request,这是django中旧的request
APIView
这个Request类把原来的request赋值给了self._request, 也就是说以后_request是我们老的request,新的request是我们这个Request类。那我们继承APIView之后请求来的数据都在哪呢
APIView
我们用了rest_framework框架以后,我们的request是重新封装的Request类,request.query_params 存放的是我们get请求的参数,request.data 存放的是我们所有的数据,包括post请求的以及put,patch请求,相比原来的django的request,我们现在的request更加精简,清晰了。

三:serializers.Serializer序列化

3.1 数据模型

from django.db import models
__all__ = ['Book', 'Publisher', 'Author']
class Book(models.Model):
    title = models.CharField(max_length=32)
    CHOICES = (
        (1, 'Python'),
        (2, 'Linux'),
        (3, 'Go')
    )
    category = models.IntegerField(choices=CHOICES)
    pub_time = models.DateField()
    publisher = models.ForeignKey(to = 'Publisher', on_delete=models.CASCADE)
    authors = models.ManyToManyField(to = 'Author')
class Publisher(models.Model):
    title = models.CharField(max_length=32)
class Author(models.Model):
    name = models.CharField(max_length=32)

3.2 创建序列化器

在app目录下创建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 AutherSerializer(serializers.Serializer):
    id = serializers.IntegerField()
    name = serializers.CharField(max_length=32)

class BookSerializer(serializers.Serializer):
    id = serializers.IntegerField(required=False)  # required=False 指明反序时(前端提交数据过来)不做校验
    title = serializers.CharField(max_length=32)
    pub_time = serializers.DateField()
    category = serializers.CharField(source='get_category_display', read_only=True)
    # 外键关系的序列化是嵌套的序列化对象;内部通过外键关系的id找到publisher对象,再使用PublisherSerializer(publisher对象)
    publisher = PublisherSerializer(read_only=True)			# 嵌套序列化器
    authors = AutherSerializer(many=True, read_only=True)  # 多对多关系需要加上参数:many = True
    # read_only:指明该字段只做序列化,反序列化时不走该字段;那么反序列化时就必须重新指定字段名
    # write_only:指明该字段只做反序列化
    post_category = serializers.IntegerField(write_only=True)
    publisher_id = serializers.IntegerField(write_only=True)
    author_list = serializers.ListField(write_only=True)

    def create(self, validated_data):
        # validated_data 校验通过的数据
        # 通过ORM操作给Book表添加数据
        # print(validated_data)
        # {'title': 'Die Hard', 'pub_time': datetime.date(2020, 1, 12), 'post_category': 1, 'publisher_id': 1, 'author_list': [1, 2]}
        book_obj = Book.objects.create(
            title=validated_data['title'],
            pub_time=validated_data['pub_time'],
            category=validated_data['post_category'],
            publisher_id=validated_data['publisher_id']
        )
        book_obj.authors.add(*validated_data['author_list'])
        return book_obj

    def update(self, instance, validated_data):
        # instance为需要更新的对象
        instance.title = validated_data.get('title', instance.title)
        instance.pub_time = validated_data.get('pub_time', instance.pub_time)
        instance.category = validated_data.get('post_category', instance.category)
        instance.publisher_id = validated_data.get('publisher_id', instance.publisher_id)
        if validated_data.get('author_list'):
            instance.authors.set(validated_data['author_list'])
        instance.save()
        return instance
    # 参数校验,与Django中的form校验一样    
    def validate_title(self, value):    
        # 单个字段校验;value就是title的值
        if 'python' not in value.lower():
            raise serializers.ValidationError('标题必须包含python')
        return value

    def validate(self, attrs):
        # 多个字段校验;attrs是个字典,包含前端传递过来的所有字段
        if 'python' in attrs['title'].lower() and attrs['post_category'] == 1:
            return attrs
        raise serializers.ValidationError('参数错误')

自定义数据校验:

def my_validate(value):
    if '敏感信息' in value.lower():
        raise serializers.ValidationError('some message')
    return value

class BookSerializer(serializers.Serializer):
    id = serializers.IntegerField(required=False)  # required=False 指明反序时(前端提交数据过来)不做校验
    title = serializers.CharField(max_length=32, validators = [my_validate,])
    ............................

validate做数据校验错误返回的key问题:

    def validate(self, attrs):
        password = attrs.get('password')
        re_password = attrs.pop('re_password')
        print(password, re_password)

        if password != re_password:
            raise serializers.ValidationError('两次密码不一致')
        return attrs

校验如有错误,报错的格式如下:

{
    "status": 1,
    "message": {
        "non_field_errors": [
            "两次密码不一致"
        ]
    }
}

message的key为non_field_errors,传递到前端不方便处理,可以自定义一个key

raise serializers.ValidationError({'password': '两次密码不一致'})

报错格式如下:

{
    "status": 1,
    "message": {
        "password": [
            "两次密码不一致"
        ]
    }
}

3.3 类视图

class BookView(APIView):

    def get(self, request):
        book_queryset = Book.objects.all()
        ser_obj = BookSerializer(book_queryset, many = True)
        return Response(ser_obj.data)

    def post(self, request):
        book_obj = request.data
        # print(book_obj, type(book_obj))
        ser_obj = BookSerializer(data = book_obj)
        if ser_obj.is_valid():
            # ser_obj.save()使用该方法时,序列化器中必须实例create()方法
            ser_obj.save()
            return Response(ser_obj.validated_data)
        return Response(ser_obj.errors)

class BookEditView(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()
        # partial=True 指明为部分字段做校验,即传入什么字段就校验什么字段,其他字段不做校验
        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)
        return Response(ser_obj.errors)

3.4 serializers.Serializer序列总结

使用serializers.Serializer方法序列化时注意以下几点:
1:正序与反序字段不一致
2:字段中的参数 required=False 指明反序时(前端提交数据过来)不做数据校验
3:read_only=True 参数指明该字段只做序列化,反序列化时不走该字段;那么反序列化时就必须重新指定字段名
4:write_only 参数指明该字段只用做反序列化
5:序列化器中必须实现create()与update()方法,分别对应创建与更新
6:前端提交的参数校验与Django中的form表单校验数据一样

四:seriarizers.ModelSerializer序列化

4.1 depth

# 只修改了book表的序列化器,其他文件不动
class BookSerializer(serializers.ModelSerializer):
    class Meta:
        model = Book
        fields = '__all__'

序列化后的数据如下:
seriarizer.ModelSerializer序列化
外键关联的字段只显示了id,添加一个depth参数

class BookSerializer(serializers.ModelSerializer):
    class Meta:
        model = Book
        fields = '__all__'
        depth = 1

序列化后的数据如下:
seriarizer.ModelSerializer序列化
depth=1,意为找到一层关联的数据,即找到publisher关联表,如果publisher中有还有关联的表,不会再往下去找。
注意:

  1. depth最多不要超过四层
  2. depth会让所有的外键关系字段变成read_only=True
  3. 生产环境depth不常用

4.2 反序列化

class BookSerializer(serializers.ModelSerializer):

    class Meta:
        model = Book
        fields = '__all__'
        # exclude = ['id']
        # depth = 1   # 注意: depth会让所有的外键关系字段变成read_only=True
        extra_kwargs = {
            'publisher': {'write_only': True},
            'authors': {'write_only': True},
        }
    # 重写正序字段
    publisher_info = serializers.SerializerMethodField(read_only=True)
    # SerializerMethodField是一个方法字段,即需要一个方法的返回值再赋给该字段
    authors_info = serializers.SerializerMethodField(read_only=True)

    def get_publisher_info(self, obj):
        publisher_obj = obj.publisher
        print(publisher_obj)
        return {'id': publisher_obj.id, 'title': publisher_obj.title}
    def get_authors_info(self, obj):
        authors_queryset = obj.authors.all()
        return [{'id': author.id, 'name': author.name} for author in authors_queryset]

外键关联的字段正序使用的是publisher_info与authors_info,那么反序还是使用的publisher与authors字段。字段的校验与上面的一样。

4.3 自定义序列字段的两种写法

这里所说的自定义序列字段可以另一张表中的字段,也可以自定要序列化的字段,也可以是外键关联的字段。
这种字段可以使用serializers.SerializerMethodField方法写在序列化器中
也可以在模型类中定义方法
1:写在序列化器中

class BookSerializer(serializers.ModelSerializer):
    publisher = serializers.SerializerMethodField()

    def get_publisher(self, obj):
        return obj.publisher.name

    class Meta:
        model = Book
        fields = ['name', 'price', 'publisher']

注意:使用serializers.SerializerMethodField方法时,在fields列表中必须写上该字段,如上面代码中的publisher。如果fields列表中没有指定publiser字段,会报如下异常:

The field 'publisher' was declared on serializer BookSerializer, but has not been included in the 'fields' option.

2:写在模型类中

class Book(BaseModel):
    name = models.CharField(max_length=64)
    price = models.DecimalField(max_digits=5, decimal_places=2)
    img = models.ImageField(upload_to='img', default='img/default.jpg')
    publisher = models.ForeignKey(to='Publisher', on_delete=models.CASCADE)
    authors = models.ManyToManyField(to='Author')

    class Meta:
        db_table = 'gin_book'

    def __str__(self):
        return self.name

    @property
    def publisher_name(self):			# 定义成一个方法属性,方法名与属性名不能重复
        return self.publisher.name

序列化类中的fields列表,可以指定publisher_name;不指定也不报异常

class BookSerializer(serializers.ModelSerializer):

    class Meta:
        model = Book
        fields = ['name', 'price', 'publisher_name']	# 不指定publisher_name字段不会抛异常

使用该方法时反序列化如下:

class BookSerializer(serializers.ModelSerializer):
    class Meta:
        model = Book
        fields = ['name', 'price', 'publisher_name', 'img', 'author_list', 'publisher', 'authors']
        extra_kwargs = {
            'publisher': {'write_only': True},
            'authors': {'write_only': True},
            'name': {
                'required': True,
                'min_length': 3,
                'error_messages': {
                    'required': '必填项',
                    'min_length': '至少3个字符'
                }
            }
        }

视图类:

class BooksView(APIView):
    def get(self, request):
        queryset = Book.objects.filter(is_delete = False)
        serializer = BookSerializer(queryset, many = True)
        return Response(serializer.data)
    def post(self, request):
        ser_obj = BookSerializer(data = request.data)
        ser_obj.is_valid(raise_exception=True)  # 当校验失败会马上终止当前视图方法,抛出异常给前台
        book_obj = ser_obj.save()
        return Response({'status': 0, 'message': 'OK', 'result': BookSerializer(book_obj).data})

4.4 自定义联表深度(嵌套)

模型类如下:

class Book(BaseModel):
    name = models.CharField(max_length=64)
    price = models.DecimalField(max_digits=5, decimal_places=2)
    img = models.ImageField(upload_to='img', default='img/default.jpg')
    publisher = models.ForeignKey(to='Publisher', on_delete=models.CASCADE)
    authors = models.ManyToManyField(to='Author')
    class Meta:
        db_table = 'gin_book'
    def __str__(self):
        return self.name

class Publisher(BaseModel):
    name = models.CharField(max_length=64)
    address = models.CharField(max_length=64)
    class Meta:
        db_table = 'gin_publisher'
    def __str__(self):
        return self.name

需求:Book表序列化时,也要序列化Pulisher表中指定的字段

class PublisherSerializer(serializers.ModelSerializer):
    class Meta:
        model = Publisher
        fields = ['name', 'address']

class BookSerializer(serializers.ModelSerializer):
    # 自定义连表深度 - 子序列化方式
    publisher = PublisherSerializer()
    class Meta:
        model = Book
        fields = ['name', 'price', 'publisher']

Django Rest Framework之序列化器

4.5 单增群增

序列化器如下:

class BookSerializer(serializers.ModelSerializer):

    class Meta:
        model = Book
        fields = ['name', 'price', 'publisher_name', 'img', 'author_list', 'publisher', 'authors']
        extra_kwargs = {
            'publisher': {'write_only': True},
            'authors': {'write_only'
                        
                        
版权声明:本文来源CSDN,感谢博主原创文章,遵循 CC 4.0 by-sa 版权协议,转载请附上原文出处链接和本声明。
原文链接:https://blog.csdn.net/pcn01/article/details/103952474
站方申明:本站部分内容来自社区用户分享,若涉及侵权,请联系站方删除。

0 条评论

请先 登录 后评论

官方社群

GO教程

猜你喜欢