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