Django REST_framework 序列化与反序列化


models部分

from django.db import models


# Create your models here.
class Books(models.Model):
    title = models.CharField(max_length=32)
    price = models.DecimalField(max_digits=8, decimal_places=2)
    publish_date = models.DateField(auto_now_add=True)
type = models.IntegerField(choice=((1, '男'),(2,'女'))) publish
= models.ForeignKey('Publish', null=TabError) author = models.ManyToManyField('Author') class Publish(models.Model): name = models.CharField(max_length=32) addr = models.CharField(max_length=32) email = models.EmailField()
def xxx(self):
    return 123
class Author(models.Model): name = models.CharField(max_length=32) age = models.IntegerField() authordetail = models.OneToOneField('AuthorDetail') yyy = models.IntegerField(choices=((1, ''),(2, '')), default=1) class AuthorDetail(models.Model): addr = models.CharField(max_length=32) phone = models.CharField(max_length=32)

 view部分

序列化的两种方式:

1. Serializer:没有指定模型表

serialize文件
class
BookSerilizer(serializers.Serializer):
   # 根据模型表的字段校验, 名字必须对应模型表的名字, CharField只要是模型表中的Field在这里都可以使用校验 title
= serializers.CharField(read_only=True) # read_only=True 反序列化的时候, 该字段不传
   # 使用source指定对应模型表的名字, 但是两个名字不能相同, 相同就报错 price5
= serializers.CharField(source='price', write_only=True) # write_only 序列化的时候, 该字段不显示
# publish为模型类中的外键字段, 相当于一个模型对象, 可以使用点的方式继续得到该模型表的字段
publish = serializers.CharField(source='publish.name')
# type为enum类型,models中使用choice,每个数字对应一个中文, 要获取中文可以使用get_字段名_display
type = serializers.CharField(source='get_type_display')
# 还可以指定方法, 如模型表中有xxx方法, xxx的值为方法的返回值
xxx1 = serializers.CharField(source='xxx')
# 序列化出版社的详情,指定SerializerMethodField之后可以对应一个方法,返回什么内容,author就是什么内容
author = serializers.SerializeMethodField()
# 该方法有两个参数, 第二个参数为该模型的对象 这里的obj相当于Book模型对象
def get_author(self, obj):
return [{'name': author.name} for author in obj.author.all()]
# 也可以在方法被再实例一个Serialize对象
def get_author(self, obj):
ret = AuthorSerializer(obj.author.all(), many=True)
return ret.data def create(self, validated_data): ret
= models.Books.objects.create(**validated_data) return ret

class AuthorSerializer(serializers.Serializer):
name = serializer.CharField()
age = serializer.CharField()
views文件
class
Books(Author): def get(self, request): response = {'code': 100, 'msg': ''} user_list = models.Books.objects.all()
# 实例化序列化对象, 参数一为所需要的数据, 参数二当数据为单条时可以不传, 但是数据为多条时必须穿many=True ret
= BookSerilizer(user_list, many=True)
# APIView重写了request方法, 将所序列化的数据全都封装进data方法中 response[
'data'] = ret.data return Response(response) def post(self, request): response = {'code': 100, 'msg': '添加成功'}
# 实例化对象时需要指定data参数, 如果不指定会默认interface bookser
= BookSerilizer(data=request.data)
# 数据全部验证成功is_valid为True, 并且验证成功的数据都添加进validated_data中
if bookser.is_valid(): bookser.create(bookser.validated_data)
# Response为APIView封装的返回数据的方法
return Response(response)

2. ModelSerializers: 指定了表模型

class AuthorSerilizer(serializers.ModelSerializer):
    class Meta:
# 使用model指定表模型 model
= models.Author
# 使用fields指定要显示的数据 fields
= '__all__' # 全部数据
fields = ('name', 'age') # 指定要显示的数据
# 要排除的字段
exclude = ('name')
# 控制的深度
depth = 1 # 深度相当于自动连表查询只要指定了外键字段就会直接查询出所需要的数据, 最大指定为10 , 但是推荐最多为3
# 重写某些字段, 方法与Serializer相同
name5 = serializers.CharField(source='name')
# 反序列化局部校验
def validate_name(self, value):
if value.startswith('sb'):
raise VaildationError('不能以Sb开头')
return value
# 反序列化全局校验
def validate(self, attrs):
if attrs.get('name') != attrs.get('confirm_name'):
raise VaildationError('字段不相等')
return attrs
class Author(APIView):
    def get(self, request):
        response = {'code': 100, 'msg': ''}
        user_list = models.Author.objects.all() 
        ret = AuthorSerilizer(user_list, many=True)
        response['data'] = ret.data
        return Response(response)

    def post(self, request):
        return HttpResponse('post')

 实例化序列化对象源码简单解读

    # instance为要序列化的数据, 单条数据可以不传many, 多条数据必须many为True, data为要反序列化的数据
def __init__(self, instance=None, data=empty, **kwargs):
# 为实例化的对象添加属性 self.instance
= instance if data is not empty: self.initial_data = data self.partial = kwargs.pop('partial', False) self._context = kwargs.pop('context', {}) kwargs.pop('many', None) super(BaseSerializer, self).__init__(**kwargs) def __new__(cls, *args, **kwargs): # We override this method in order to automagically create # `ListSerializer` classes instead when `many=True` is set.
# 判断many参数是否存在,存在就走many_init方法
if kwargs.pop('many', False): return cls.many_init(*args, **kwargs)
# many不存在则继续开辟一个空间
return super(BaseSerializer, cls).__new__(cls, *args, **kwargs)
    @classmethod
    def many_init(cls, *args, **kwargs):
        """
        This method implements the creation of a `ListSerializer` parent
        class when `many=True` is used. You can customize it if you need to
        control which keyword arguments are passed to the parent, and
        which are passed to the child.

        Note that we're over-cautious in passing most arguments to both parent
        and child classes in order to try to cover the general case. If you're
        overriding this method you'll probably want something much simpler, eg:

        @classmethod
        def many_init(cls, *args, **kwargs):
            kwargs['child'] = cls()
            return CustomListSerializer(*args, **kwargs)
        """
        allow_empty = kwargs.pop('allow_empty', None)
        child_serializer = cls(*args, **kwargs)
# 将实例化的BaseSerializer对象保存进list_kwargs中 list_kwargs
= { 'child': child_serializer, } if allow_empty is not None: list_kwargs['allow_empty'] = allow_empty
# 将传过来的参数全部更新入list_kwargs中 list_kwargs.update({ key: value
for key, value in kwargs.items() if key in LIST_SERIALIZER_KWARGS }) meta = getattr(cls, 'Meta', None) list_serializer_class = getattr(meta, 'list_serializer_class', ListSerializer)
# 最后用ListSerializer将对象实例化
return list_serializer_class(*args, **list_kwargs)

总结:

序列化时:

 

  如果是单条数据: 第一个参数instance参数直接写需要序列化的数据, many可以不指定, instance可以省略, 因为instance为第一个参数

  如果十多条数据: 第一个参数instance参数写需要序列化的数据的列表, many指定为True

反序列化时:

       所传的数据为data, data为第二个参数, 所以要反序列化必须指定data=数据

       is_valid通过之后需要重写create, 在源码中直接抛出异常, 所以必须重写create

    def create(self, validated_data):
        raise NotImplementedError('`create()` must be implemented.')

ListSerializer.data源码

    @property
    def data(self):
# 继承父类的data ret
= super(ListSerializer, self).data return ReturnList(ret, serializer=self)

父类的data

    @property
    def data(self):
# 验证数据是否验证, 必须使用is_valid值后才可以使用data, 没有验证的话直接抛出异常
if hasattr(self, 'initial_data') and not hasattr(self, '_validated_data'): msg = ( 'When a serializer is passed a `data` keyword argument you ' 'must call `.is_valid()` before attempting to access the ' 'serialized `.data` representation.\n' 'You should either call `.is_valid()` first, ' 'or access `.initial_data` instead.' ) raise AssertionError(msg)
# 判断self.data是否存在, 存在就直接返回, 不存在就添加入self.data
if not hasattr(self, '_data'):
# 判断是否有错误, 没有错误的化就执行序列化
if self.instance is not None and not getattr(self, '_errors', None):
# 将instance传入开始序列化 self._data
= self.to_representation(self.instance) elif hasattr(self, '_validated_data') and not getattr(self, '_errors', None): self._data = self.to_representation(self.validated_data) else: self._data = self.get_initial() return self._data

序列化方法to_representation

 def to_representation(self, instance):
        """
        Object instance -> Dict of primitive datatypes.
        """
# 实例化一个有序字典
ret = OrderedDict() fields = self._readable_fields for field in fields: try: attribute = field.get_attribute(instance) except SkipField: continue # We skip `to_representation` for `None` values so that fields do # not have to explicitly deal with that case. # # For related fields with `use_pk_only_optimization` we need to # resolve the pk value. check_for_none = attribute.pk if isinstance(attribute, PKOnlyObject) else attribute
# 将每个字段对应的名字作为key,添加进入实例化的有序字典ret
if check_for_none is None: ret[field.field_name] = None else: ret[field.field_name] = field.to_representation(attribute) return ret

attribute数据是从get_attribute中获得

 def get_attribute(self, instance):
        """
        Given the *outgoing* object instance, return the primitive value
        that should be used for this field.
        """
        try:
# source_attrs通过'.'将所有的字段取出(self.source_attrs=self.cource.split('.')), 得到一个列表, 因此就可以将传过来的字段的值都取到
# get_attribute没有带self参数,代表只是一个函数不是方法, 根据定定义的字段获取不同的数据
return get_attribute(instance, self.source_attrs) except (KeyError, AttributeError) as exc: if self.default is not empty: return self.get_default() if self.allow_null: return None if not self.required: raise SkipField() msg = ( 'Got {exc_type} when attempting to get a value for field ' '`{field}` on serializer `{serializer}`.\nThe serializer ' 'field might be named incorrectly and not match ' 'any attribute or key on the `{instance}` instance.\n' 'Original exception text was: {exc}.'.format( exc_type=type(exc).__name__, field=self.field_name, serializer=self.parent.__class__.__name__, instance=instance.__class__.__name__, exc=exc ) ) raise type(exc)(msg)

函数get_attribute

def get_attribute(instance, attrs):
    """
    Similar to Python's built in `getattr(instance, attr)`,
    but takes a list of nested attributes, instead of a single attribute.

    Also accepts either attribute lookup on objects or dictionary lookups.
    """
# attr是通过split切割来的为一个列表
for attr in attrs: try:
# 如果该字段是从model中传入过来的, 则直接调用model类的
if isinstance(instance, Mapping): instance = instance[attr] else:
# 否则反射取结果 instance
= getattr(instance, attr) except ObjectDoesNotExist: return None
# 判断是否可以调用, 也就是说在source指定字段时, 如果只是一个字段就直接得到一个字段, 如果是一个对象,就将它实例化, 这样就可以点外键的字段得到外键对应的表的字段(就相当于一个queryset对象)
if is_simple_callable(instance): try: instance = instance() # 重新赋值, 加()执行 except (AttributeError, KeyError) as exc: # If we raised an Attribute or KeyError here it'd get treated # as an omitted field in `Field.get_attribute()`. Instead we # raise a ValueError to ensure the exception is not masked. raise ValueError('Exception raised in callable attribute "{0}"; original exception was: {1}'.format(attr, exc)) return instance

钩子函数代码解析

 Serializer模型中  使用source指定方法名     get_方法名  写钩子函数

    def bind(self, field_name, parent):
        # In order to enforce a consistent style, we error if a redundant
        # 'method_name' argument has been used. For example:
        # my_field = serializer.SerializerMethodField(method_name='get_my_field')
# 使用字符串拼接得到方法名 default_method_name
= 'get_{field_name}'.format(field_name=field_name) assert self.method_name != default_method_name, ( "It is redundant to specify `%s` on SerializerMethodField '%s' in " "serializer '%s', because it is the same as the default method name. " "Remove the `method_name` argument." % (self.method_name, field_name, parent.__class__.__name__) ) # The method name should default to `get_{field_name}`.
# 判断方法名是否存在, 不存在则将拼接的方法名添加入对象中
if self.method_name is None: self.method_name = default_method_name super(SerializerMethodField, self).bind(field_name, parent

获取POST的字段并验证    validate_字段名局部钩子       validate 全局钩子   

is_valid --> self.run_validation --> self.internal_value --> Serializer.internal_value

    def to_internal_value(self, data):
        """
        Dict of native values <- Dict of primitive datatypes.
        """
        if not isinstance(data, Mapping):
            message = self.error_messages['invalid'].format(
                datatype=type(data).__name__
            )
            raise ValidationError({
                api_settings.NON_FIELD_ERRORS_KEY: [message]
            }, code='invalid')

        ret = OrderedDict()
        errors = OrderedDict()
# 所有要显示的字段对象 fields
= self._writable_fields for field in fields:
# 通过拼接字段名并使用反射 validate_method
= getattr(self, 'validate_' + field.field_name, None) primitive_value = field.get_value(data) try: validated_value = field.run_validation(primitive_value) if validate_method is not None:
# 不是空,就加括号执行 validated_value
= validate_method(validated_value) except ValidationError as exc:
# 出现异常就将异常添加进errors errors[field.field_name]
= exc.detail except DjangoValidationError as exc: errors[field.field_name] = get_error_detail(exc) except SkipField: pass else: set_value(ret, field.source_attrs, validated_value) if errors: raise ValidationError(errors) return ret

 


免责声明!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系本站邮箱yoyou2525@163.com删除。



 
粤ICP备18138465号  © 2018-2025 CODEPRJ.COM