drf序列化和反序列化


drf序列化和反序列化

一、自定義序列化

# models.py
from django.db import models
class User(models.Model):
    CHOICE_SEX = ((0, '男'), (1, '女'))
    name = models.CharField(max_length=100)
    height = models.DecimalField(max_digits=6, decimal_places=2, default=1.85)
    age = models.IntegerField(default=0)
    icon = models.ImageField(upload_to='icon', default='default.jpg')
    sex = models.IntegerField(choices=CHOICE_SEX)
    class Meta:
        db_table = 'db_user'
        verbose_name = '用戶表'
        verbose_name_plural = verbose_name

    def __str__(self):
        return self.name
# urls.py
urlpatterns = [
    url(r'^books/$', views.MyAPIView.as_view()),
    url(r'^books/(?P<pk>\d+)/', views.MyAPIView.as_view()),
     # 在路由配置向外面開放的接口 media
    url(r'^media/(?P<path>.*)', serve, {'document_root': settings.MEDIA_ROOT})
]

from api import models
from django.conf import settings
from rest_framework.response import Response
from rest_framework.views import APIView
class MyAPIView(APIView):
    def get(self, *args, **kwargs):
        pk = kwargs.get("pk", None)
        if pk:
            print(pk)
            user_obj = models.User.objects.filter(pk=pk).first()
            return Response({
                "status": 0,
                'msg': "get ok",
                'result': {
                    "name": user_obj.name,
                    'age': user_obj.age,
                    "icon": f'http://127.0.0.1:8000{settings.MEDIA_URL}{user_obj.icon}',
                    "sex": user_obj.get_sex_display()
                }
            })
        else:
            user_list = []
            user_list_obj = models.User.objects.all()

            for user_obj in user_list_obj:
                user_list.append(
                    {
                        'result': {
                            "name": user_obj.name,
                            'age': user_obj.age,
                            "icon": f'http://127.0.0.1:8000{settings.MEDIA_URL}{user_obj.icon}',
                            "sex": user_obj.get_sex_display()
                        }
                    }
                )
            print(user_list)
            return Response({

                "status": 0,
                'msg': "get ok",
                'result': user_list
            })

    def post(self, *args, **kwargs):
        return Response({
            "status": 1
        })
    

總結:

  1. 通過ORM操作獲取數據庫拿到資源數據
  2. 將獲取的的數據進行序列化,序列化之后將數據返回給前台的數據
  3. 通過Response返回格式化后的數據

1.1 設置國際化

# 在settings中設置了國際化才可以顯示中文
LANGUAGE_CODE = 'zh-hans'

TIME_ZONE = 'Asia/Shanghai'

USE_I18N = True

USE_L10N = True

USE_TZ = False

二、通過視圖類的序列化和反序列化

# 在settings中設置了國際化才可以顯示中文
LANGUAGE_CODE = 'zh-hans'

TIME_ZONE = 'Asia/Shanghai'

USE_I18N = True

USE_L10N = True

USE_TZ = False

2.1通過視圖類序列化

自定義序列化類

# 創建自定義.py並創建序列化類 
class MySerialize(serializers.Serializer):
    name = serializers.CharField()
    age = serializers.IntegerField()
    # icon = serializers.ImageField()
    # sex = serializers.IntegerField()
    # 小數字段,必須要設置參數
    height = serializers.DecimalField(max_digits=6, decimal_places=3)

    # 自定義字段
    gender = serializers.SerializerMethodField()
    icon = serializers.SerializerMethodField()
	
    # 自定義序列化字段,序列化的屬性值由方法來提供,
    #   方法的名字:固定為 get_屬性名,
    #   方法的參數:序列化對象,序列化的model對象
    #   強烈建議自定義序列化字段名不要與model已有的屬性名重名
    
    def get_gender(self, obj):
        # print(obj, type(obj))  # obj類<class 'api.models.User'>
        # print(self, type(self))  # class 'api.myserialize.MySerialize'>
        return obj.get_sex_display()

    def get_icon(self, obj):
        return f'http://127.0.0.1:8000/media/{obj.icon}'

視圖類


# 通過serialize實現序列化
from .myserialize import MySerialize
class MyAPIView(APIView):
    def get(self, request, *args, **kwargs):
        pk = kwargs.get("pk", None)
        if pk:
            user_obj = models.User.objects.filter(pk=pk).first()
            # 自定義單序列化
            my_serialize = MySerialize(user_obj)
            print(my_serialize.data)
            # if my_serialize.is_valid():
            # res = my_serialize
            return Response({
                "status": 0,
                'msg': "get ok",
                'result': my_serialize.data

            })

        else:
            user_list_obj = models.User.objects.all()
            # 群序列化
            my_serialize = MySerialize(user_list_obj, many=True)

            print(my_serialize.data, type(my_serialize.data))
            return Response({

                "status": 0,
                'msg': "get ok",
                'result': my_serialize.data
            })

總結:

  1. 直接將要序列化的數據傳給序列化類
  2. 要序列化的數據如果是單個對象,序列化的參數many為False,數據如果是多個對象(list,queryset)序列化的參數many為True
  3. 序列化類的中字段必須與model中字段屬性同名,如果不參與序列化的model屬性,在序列化類中不做聲明,也不會返回
  4. 可以自定義序列化字段,自定義序列化字段用 SerializerMethodField() 作為字段類型,序列化的屬性有方法類提供,方法的名字:get_屬性名,方法參數:序列化對象,序列化的model對象,注意自定義的序列化字段不要與model已有字段屬性重名,返回方法的返回值

2.2通過視圖類反序列化

class UserCreateSerialize(serializers.Serializer):
    # 系統校驗規則
    # 系統必須反序列化的字段,序列化的字段,序列化屬性名不是必須有與model屬性名字對應
    # 但是與之對應會方便序列化將校驗通過的數據與數據庫進行交互,
    name = serializers.CharField(min_length=3, max_length=6, required=False,error_messages={
        'required': "姓名必填",
        "min_length": "太短"
    })
    # # 系統可選的反序列化字段:沒有提供不進行校驗(數據庫中有默認值或可以為空),提供了就進行校驗
    age = serializers.IntegerField(min_value=3, max_value=6, required=False)
    # icon = serializers.ImageField()
    sex = serializers.IntegerField()
    # 小數字段,必須要設置參數
    height = serializers.DecimalField(max_digits=6, decimal_places=3)

    pwd = serializers.CharField(min_length=3, max_length=64)

    # 自定義反序列化字段:一定參與校驗,且要在校驗過程中,將其從入庫的數據中取出,剩余與model對應的數據才會入庫
    re_pwd = serializers.CharField(min_length=3, max_length=64)

    par = serializers.CharField(max_length=10)

    # 自定義校驗規則:局部鈎子,全局鈎子
    # 局部鈎子: validate_字段名(slef, 字段值)
    # 規則: 成功返回value,校驗錯誤拋異常
    def validate_par(self, value):
        if 'rand' in value.lower():
            raise serializers.ValidationError("名字不能有randy")
        return value

    # 全局鈎子:validate(self, 所有校驗的數據字典)
    # 規則:成功返回attrs,失敗拋異常
    def validate(self, attrs):
        pwd = attrs.get("pwd")
        re_pwd = attrs.pop('re_pwd')
        if pwd != re_pwd:
            raise serializers.ValidationError({"re_pwd": "兩次密碼不能一樣!"})
        return attrs
    
    # create重寫,
    def create(self, validated_data):
        return models.User.objects.create(**validated_data)
    
class MyAPIView(APIView):
    # 單增和群增
    def post(self, request, *args, **kwargs):
        # 從請求對象中拿到前台的數據
        # 校驗數據是否合法
        # 反序列化成后台Model對象與數據交互
        request_data = request.data
        user_seria = UserCreateSerialize(data=request_data)

        # 調用反序列化的校驗規則,系統規則,自定義規則(局部鈎子,全局鈎子)
        result = user_seria.is_valid()
        if result:
            # 校驗通過,可以與數據庫進行交互增 create,該(update)
            user_obj = user_seria.save()

            return Response({
                'status': 0,
                'msg': 'ok',
                'results': MySerialize(user_obj).data

            })
        else:

            # 校驗失敗,返回錯誤信息,在使用 .errors必須先要調用 is_valid()方法
            return Response({
                "status": 1,
                "msg": user_seria.errors,

            }, status=status.HTTP_400_BAD_REQUEST
            )

總結:

  1. 在視圖類中,從請求對象中獲取前台的數據,校驗前台數據是否合法,然后將合法的數據返回給Model對象與數據進行交互,保存數據
  2. 反序列化的數據如果是單個數據,反序列化的參數mangy為False,數據如果是多個字典的列表,反序列化的參數many為True
  3. user_seria.errors,在報錯的時候一定要先使用 is_valid()函數
  4. 反序列化類:系統的字段可以在Filed類型中設置系統校驗的規則(name=serializers.CharField(min_length=3))
  5. required校驗規則對該字段是必校驗還是可選校驗字段(默認required為True,數據庫字段有默認值或可以為空的字段可以將required賦值為False)
  6. 自定義的反序列字段,設置系統校驗規則同系統字段,但是需要在自定義校驗規則中(局部、全局鈎子)將自定義反序列化字段取出(返回剩余的數據與數據庫交互)
  7. 局部鈎子的方法命名 validate_屬性名(self, 屬性的value),校驗規則為 成功返回屬性的value 失敗拋出校驗錯誤的異常
  8. 全局鈎子的方法命名 validate(self, 所有屬性attrs),校驗規則為 成功返回attrs 失敗拋出校驗錯誤的異常
  9. 校驗有個順序先校驗全部聲明的字段,只有全部校驗成功之后才會去校驗局部鈎子,然后在校驗全局鈎子;,
  10. 反序列化需要重寫create,update方法

三、ModelSerializer類實現序列化和反序列化

3.1 序列化

from rest_framework.serializers import ModelSerializer
# 自定反序列化類
class UserModelSerializer(ModelSerializer):
    # source指定數據的來源,"product.product_id"是models中表的字段
    product_id = serializers.IntegerField(source="product.product_id")
    image_url = serializers.ImageField(source="image.image_url")
    class Meta:
        model = models.User
        fields = ["name", "age", "height", "sex", 'gender']  # 序列化字段
        
# 自定義插拔序列化字段(在model類中實現):
#替換了在Serializer類中自定義的序列化字段(SerializerMethodField)
# 自定義插拔序列化字段一定不參與反序列化過程
@property
def gender(self):
    return self.get_sex_display()

from .myserialize import UserModelSerializer

# 視圖類
class MyAPIView(APIView):
    # 單查和群查
    def get(self, request, *args, **kwargs):
        pk = kwargs.get("pk", None)
        if pk:
            user_obj = models.User.objects.filter(pk=pk).first()

            if not user_obj:
                return Response(data={"status": 1, 'msg': "數據不存在"})

            # 序列化數據context 將request傳遞過去則會自動憑借url的資源,比如圖片
            user_data = UserModelSerializer(user_obj, many=False,context={"request": request}).data

            return Response({
                'status': 0,
                'msg': '單查 ok',
                'results': user_data
            })

        # 群查
        user_query = models.User.objects.all()
        # 獲取序列化
        user_list_data = UserModelSerializer(user_query, many=True).data

        return Response({
            "status": 0,
            "msg": "群查 ok",
            "results": user_list_data
        })

3.2 反序列化

from rest_framework.serializers import ModelSerializer
# 自定反序列化類
from rest_framework.serializers import ModelSerializer


class UserModelSerializer(ModelSerializer):
    # 將序列化類與Model類進行綁定
    # 設置序列化與反序列化所有字段(並划分序列化字段與反序列化字段)
    # 設置反序列化的局部與全局鈎子

    # 自定義反序列化字段,校驗規則只能生命自定義反序列化字段是設置,且一定要寫write_only
    re_pwd = serializers.CharField(min_length=3, max_length= 5, write_only=True)

    class Meta:
        model = models.User
        fields = ["name", "age", "height", "sex", 'gender', "pwd", "re_pwd"]
        extra_kwargs = {
            "name": {
                'required': True,
                "min_length": 3,
                "error_messages": {
                    "min_length": "太短了"
                }
            },

            "age": {
                # 數據庫中有默認值或可以為空字段, required默認為false,前台發送的數據不會進行校驗,只有為true時候才會進行校驗
                "required": True,
                'min_value': 5,
                "read_only": True,  # 只參與序列化,不參與反序列化進行校驗
            },
            "pwd": {
                "required": True,
                "write_only": True,  # 只參與反序列化 設置這個就是在反序列化的時候不會返回給前台,不會序列化

            },
            "height": {
                # 只參與序列化,不參與反序列化進行校驗,就是校驗數據傳來了height這個參數,不會對數據進行校驗只會返回原先的數據或者是默認的數據
                "read_only": True,
            }

        }

    # 局部鈎子
    def validate_name(self, value):
        if 'randy' in value.lower():
            raise serializers.ValidationError('名字中不能有randy')
        return value
	# 全局鈎子
    def validate(self, attrs):
        pwd = attrs.get('pwd')
        # 一定要移除
        re_pwd = attrs.pop('re_pwd')
        if pwd != re_pwd:
            raise serializers.ValidationError({'re_pwd': '兩次密碼不一致'})
        return attrs


from .myserialize import UserModelSerializer
# 視圖類
class MyAPIView(APIView):   
    # 單增和群增
    def post(self, request, *args, **kwargs):
       # 獲取校驗數據
        request_data = request.data
        user_seria = UserModelSerializer(data=request_data)

        # 校驗失敗直接拋異常,反饋異常信息給前台,只要校驗通過代碼才會往下執行
        # result = user_seria.is_valid(raise_exception=True)
        result = user_seria.is_valid(raise_exception=True)
        if result:
            # 校驗通過,可以與數據庫進行交互增 create,該(update)
            user_obj = user_seria.save()

            return Response({
                'status': 0,
                'msg': 'ok',
                'results': UserModelSerializer(user_obj).data

            })
        else:

            # 校驗失敗,返回錯誤信息,在使用 .errors必須先要調用 is_valid()方法
            return Response({
                "status": 1,
                "msg": user_seria.errors,

            }, status=status.HTTP_400_BAD_REQUEST
            )

總結:

  1. 將序列化與反序列化功能整合到一個類,這個類繼承 ModelSerializer來實現

  2. 繼承ModelSerializer類的資源序列化類內部包含三部分, Meta子類, 局部鈎子、全局鈎子

    • 注:create和update方法ModelSerializer已經重寫了,使用不需要重寫,默認不能實現群該功能
    • 通過集成ListSerializer類update方法,實現群該功能,將其綁定給list_serializer_class
  3. 在Meta子類中:

    • 用model來綁定所關聯的Model類
    • 用fields來設置所有的序列化反序列化字段
    • 用extra_kwargs來設置系統的校驗規則
  4. 重要字段校驗規則

    • read_only校驗規則:代表該字段只參與序列化,不參與反序列化進行校驗,就是校驗數據傳來了height這個參數,不會對數據進行校驗只會返回原先的數據或者是默認的數據
    • write_only校驗規則:只參與反序列化 設置這個就是在反序列化的時候不會返回給前台,不會序列化
    • required校驗規則:代表該字段在反序列化,在數據庫中有默認值或可以為空字段, required默認為false,前台發送的數據不會進行校驗,只有為true時候才會進行校驗
      • 如果一個字段有默認值或是可以為空,沒設置required規則,默認為False,反之默認值為True
      • 如果一個Model字段即沒有設置read_only也沒設置write_only,該字段默認參與序列化及反序列化
  5. 自定義序列化字段:在Model類中,定義方法屬性(可以返回特殊值,還可以完成連表操作),在序列化類的fields屬性中可以選擇性插拔(重要)自定義插拔序列化字段一定不參與反序列化過程

  6. 自定義反序列化字段:在Serializer類中,自定義校驗字段,校驗規則也只能在聲明字段時設置,自定義的反序列化字段(如re_pwd),必須設置write_only為True 自定義插拔序列化字段一定不參與反序列化過程

  7. user_seria = UserModelSerializer(data=request_data),校驗數據要將數據給data,才可以校驗

  8. 在全局鈎子中 一定要移除重復確認的字段

  9. 在反序列化的時候如果需要字段一定要傳則在model中不給他設置默認值,一旦設置了默認值,反序列化的時候就可以不傳值

  10. 使用fields來明確字段,__all__表名包含所有字段,也可以寫明具體哪些字段,

  11. 使用exclude可以明確排除掉哪些字段

  12. 可以通過read_only_fields指明只讀字段,即僅用於序列化輸出的字段

  13. 可以使用extra_kwargs參數為ModelSerializer添加或修改原有的選項參數


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM