django的rest framework框架——版本、解析器、序列化

1、rest framework的版本使用git

一、版本能夠寫在URL中,經過GET傳參,如 http://127.0.0.1:8082/api/users/?version=v1django

(1)自定義類獲取版本信息:json

from django.http import JsonResponse
from rest_framework.views import APIView


class ParamVersion(object):
    """獲取版本"""

    def determine_version(self, request, *args, **kwargs):
        version = request.query_params.get("version")  # 獲取請求裏面的版本信息
        return version


class UsersView(APIView):
    """用戶中心"""
    versioning_class = ParamVersion  # 獲取版本信息

    def get(self, request, *args, **kwargs):
        res = {"code": 1000, "msg": None, "data": None}
        version = request.version
        print(version)
        return JsonResponse(res)
views.py

(2)經過內置類來獲取版本api

from django.http import JsonResponse
from rest_framework.views import APIView
from rest_framework.versioning import QueryParameterVersioning


class UsersView(APIView):
    """用戶中心"""
    versioning_class = QueryParameterVersioning  # 獲取版本信息

    def get(self, request, *args, **kwargs):
        res = {"code": 1000, "msg": None, "data": None}
        version = request.version
        print(version)
        return JsonResponse(res)
views.py

在項目的settings中進行參數配置:app

 

二、也能夠寫在URL的路徑中,如 http://127.0.0.1:8082/api/v1/users/ide

能夠經過內置類來獲取:函數

from django.http import JsonResponse
from rest_framework.views import APIView
from rest_framework.versioning import URLPathVersioning

class UsersView(APIView):
    """用戶中心"""
    versioning_class = URLPathVersioning  # 獲取版本信息

    def get(self, request, *args, **kwargs):
        res = {"code": 1000, "msg": None, "data": None}
        version = request.version
        print(version)
        return JsonResponse(res)
views.py

推薦使用第二種方法來配置版本信息。post

三、能夠將版本類寫到配置文件中,進行全局控制:ui

在項目的settings中進行設置:this

from django.http import JsonResponse
from rest_framework.views import APIView

class UsersView(APIView):
    """用戶中心"""

    def get(self, request, *args, **kwargs):
        res = {"code": 1000, "msg": None, "data": None}
        version = request.version  # 獲取版本
        print(version)
        return JsonResponse(res)
views.py

四、源碼流程

路由→as_view()→rest framework的dispatch()→initial()→determine_version()

能夠經過request.version獲取版本信息

經過request.versioning_scheme獲取處理版本的對象,能夠利用這個對象調用reverse()方法來反向生成url:

from django.conf.urls import url
from api import views

urlpatterns = [
    url(r'^(?P<version>[v1|v2]+)/users/$', views.UsersView.as_view(), name="userView"),
]
urls.py
from django.http import JsonResponse
from rest_framework.views import APIView

class UsersView(APIView):
    """用戶中心"""

    def get(self, request, *args, **kwargs):
        res = {"code": 1000, "msg": None, "data": None}
        version = request.version  # 獲取版本
        scheme = request.versioning_scheme  # 處理版本的對象

        # 反向生成URL
        url = scheme.reverse(viewname="userView", request=request)
        print(url)

        return JsonResponse(res)
views.py

 

2、解析器

解析器:對請求體的數據進行解析

一、Django的解析器

from django.core.handlers.wsgi import WSGIRequest

在Django內部會跟據不一樣的狀況將request.body裏面的數據解析到request.POST:

因此只有當 Content_type='application/x-www-form-urlencoded' 時,纔會去解析request.body裏面的數據,另外,數據格式必須是 name=amy&age=18 這種格式才能被解析,

基於如上兩個條件才能使得request.POST有值。

二、rest framework的解析器

一、示例

from django.http import JsonResponse
from rest_framework.views import APIView
from rest_framework.parsers import JSONParser


class ExampleView02(APIView):
    """"""
    parser_classes = [JSONParser]  # 解析器類JSONParser:表示容許用戶發送json格式的數據{"name":"amy", "age":18}

    def post(self, request, *args, **kwargs):
        res = {"code": 1000, "msg": None, "data": None}
        data = request.data  # 獲取解析後的數據

        res["data"] = data
        return JsonResponse(res)
views.py

JSONParser類只能解析Content-type爲applicaton/json的數據,

FormParser類只能解析Content-type爲applicaton/x-www-form-urlencoded的數據,

三、rest framework的解析器源碼流程

執行dispatch()中的initialize_request()方法:

  • 全局解析器配置:

  • 視圖中配置:

 

在視圖中調用requst.data觸發:

request的data方法調用_load_data_and_files():

以JSONParser類爲例:

 3、rest framework序列化處理數據

一、示例1  序列化類的使用

from django.db import models


class UserGroup(models.Model):
    title = models.CharField(max_length=32)


class UserInfo(models.Model):
    user_type_choices = (
        (1, "普通用戶"),
        (2, "vip"),
        (3, "svip"),
    )
    user_type = models.IntegerField(choices=user_type_choices)
    username = models.CharField(max_length=32, unique=True)
    password = models.CharField(max_length=64)
    group = models.ForeignKey(to="UserGroup", on_delete=models.CASCADE, null=True, blank=True)
    roles = models.ManyToManyField(to="Role", blank=True)


class UserToken(models.Model):
    user = models.OneToOneField(to="UserInfo")
    token = models.CharField(max_length=64)


class Role(models.Model):
    title = models.CharField(max_length=32)


class Order(models.Model):
    id = models.AutoField(primary_key=True)
    name = models.CharField(max_length=32)
    price = models.DecimalField(max_digits=5, decimal_places=2)
    create_time = models.DateTimeField(auto_now_add=True)
    user = models.ForeignKey(to="UserInfo", on_delete=models.CASCADE, null=True, blank=True)
models.py
from django.conf.urls import url
from api import views

urlpatterns = [
    url(r'^(?P<version>[v1|v2]+)/roles/$', views.RolesView.as_view()),
]
api/urls.py
import json

from django.shortcuts import HttpResponse
from rest_framework.views import APIView
from rest_framework import serializers

from api import models


class RolesSerializer(serializers.Serializer):
    """對獲取的數據進行序列化操做"""
    # 要與數據表中的字段向對應
    id = serializers.CharField()
    title = serializers.CharField()


class RolesView(APIView):
    """獲取角色"""

    def get(self, request, *args, **kwargs):

        # 方法一:
        # roles = models.Role.objects.all().values("id", "title")
        # roles = list(roles)
        # res = json.dumps(roles, ensure_ascii=False)

        # 方法二:使用序列化類
        # roles = models.Role.objects.all()
        # # 實例化序列化類  若是是對多條數據進行序列化要設置 many=True
        # serl = RolesSerializer(instance=roles, many=True)
        # res = serl.data  # 獲取序列化後的結果
        # res = json.dumps(res, ensure_ascii=False)

        role = models.Role.objects.all().first()
        # 實例化序列化類  若是是對一條數據進行序列化要設置 many=False
        serl = RolesSerializer(instance=role, many=False)
        res = serl.data  # 獲取序列化後的結果
        res = json.dumps(res, ensure_ascii=False)

        return HttpResponse(res)
views.py

二、序列化自定義字段

from django.conf.urls import url
from api import views

urlpatterns = [
    url(r'^(?P<version>[v1|v2]+)/userInfo/$', views.UserInfoView.as_view()),
]
urls.py
import json

from django.shortcuts import HttpResponse
from rest_framework.views import APIView
from rest_framework import serializers

from api import models


class UserInfoSerializer(serializers.Serializer):
    """對獲取的數據進行序列化操做"""
    username = serializers.CharField()
    password = serializers.CharField()
    user_type = serializers.CharField(source="get_user_type_display")  # 獲取choices字段的文本
    group = serializers.CharField(source="group.title")  # 獲取ForeignKey字段
    roles = serializers.SerializerMethodField()  # 自定義顯示ManyToMany字段

    def get_roles(self, row):
        """獲取角色對象信息"""
        role_obj_list = row.roles.all()
        res = []
        for item in role_obj_list:
            res.append({"id": item.id, "title": item.title})
        return res


class UserInfoView(APIView):
    """獲取用戶信息"""

    def get(self, request, *args, **kwargs):
        users = models.UserInfo.objects.all()
        serl = UserInfoSerializer(instance=users, many=True)
        res = json.dumps(serl.data, ensure_ascii=False)
        return HttpResponse(res)
views.py

三、除了繼承Serializer類實現序列化,還可使用ModelSerializer類來實現。ModelSerializer繼承了Serializer

import json

from django.shortcuts import HttpResponse
from rest_framework.views import APIView
from rest_framework import serializers

from api import models


class UserInfoSerializer(serializers.ModelSerializer):
    """對獲取的數據進行序列化操做"""
    class Meta:
        model = models.UserInfo
        fields = "__all__"  # 對全部的字段作序列化


class UserInfoView(APIView):
    """獲取用戶信息"""

    def get(self, request, *args, **kwargs):
        users = models.UserInfo.objects.all()
        serl = UserInfoSerializer(instance=users, many=True)
        res = json.dumps(serl.data, ensure_ascii=False)
        return HttpResponse(res)
views.py

這種方式生成的數據比較」簡陋「,能夠根據需求定製:

class UserInfoSerializer(serializers.ModelSerializer):
    """對獲取的數據進行序列化操做"""
    user_type = serializers.CharField(source="get_user_type_display")
    group = serializers.CharField(source="group.title")
    roles = serializers.SerializerMethodField()  # 自定義顯示ManyToMany字段

    def get_roles(self, row):
        """獲取角色對象信息"""
        role_obj_list = row.roles.all()
        res = []
        for item in role_obj_list:
            res.append({"id": item.id, "title": item.title})
        return res

    class Meta:
        model = models.UserInfo
        fields = ["id", "username", "password", "user_type", "group", "roles"]  


class UserInfoView(APIView):
    """獲取用戶信息"""

    def get(self, request, *args, **kwargs):
        users = models.UserInfo.objects.all()
        serl = UserInfoSerializer(instance=users, many=True)
        res = json.dumps(serl.data, ensure_ascii=False)
        return HttpResponse(res)
views.py

四、序列化深度控制

自動序列化實現連表操做

class UserInfoSerializer(serializers.ModelSerializer):
    """對獲取的數據進行序列化操做"""

    class Meta:
        model = models.UserInfo
        fields = "__all__"
        depth = 1  # 深度控制  建議取0~10層,層數越多響應速度也會受到影響


class UserInfoView(APIView):
    """獲取用戶信息"""

    def get(self, request, *args, **kwargs):
        users = models.UserInfo.objects.all()
        serl = UserInfoSerializer(instance=users, many=True)
        res = json.dumps(serl.data, ensure_ascii=False)
        return HttpResponse(res)
views.py

五、序列化生成HyperMediaLink

需求:經過查看用戶信息頁面獲取到查看分組信息的URL

from django.conf.urls import url
from api import views

urlpatterns = [
    url(r'^(?P<version>[v1|v2]+)/userInfo/$', views.UserInfoView.as_view()),
    url(r'^(?P<version>[v1|v2]+)/group/(?P<pk>\d+)$', views.GroupView.as_view(), name="groupView"),
]
urls.py
import json

from django.shortcuts import HttpResponse
from rest_framework.views import APIView
from rest_framework import serializers

from api import models


class GroupSerializer(serializers.ModelSerializer):
    """對獲取的數據進行序列化操做"""

    class Meta:
        model = models.UserGroup
        fields = "__all__"


class UserInfoSerializer(serializers.ModelSerializer):
    """對獲取的數據進行序列化操做"""
    # 反向生成查看group的URL
    group = serializers.HyperlinkedIdentityField(view_name="groupView", lookup_field="group_id", lookup_url_kwarg="pk")

    class Meta:
        model = models.UserInfo
        fields = ["id", "username", "password", "user_type", "group"]


class GroupView(APIView):
    """獲取分組信息"""

    def get(self, request, *args, **kwargs):
        pk = kwargs.get("pk")
        group_obj = models.UserGroup.objects.filter(id=pk).first()
        serl = GroupSerializer(instance=group_obj, many=False)
        res = json.dumps(serl.data, ensure_ascii=False)
        return HttpResponse(res)


class UserInfoView(APIView):
    """獲取用戶信息"""

    def get(self, request, *args, **kwargs):
        users = models.UserInfo.objects.all()
        serl = UserInfoSerializer(instance=users, many=True, context={"request": request})
        res = json.dumps(serl.data, ensure_ascii=False)
        return HttpResponse(res)
views.py

六、序列化源碼流程

若是是處理QuerySet數據,就會執行many_init()方法:

至關於在內部,若是是處理對象(一條數據),就使用Serializer進行處理;若是是處理QuerySet(多條數據),就用ListSerializer處理。

實例化完成後,能夠經過「對象名.data」方法 獲取數據:

父類的data方法:

若是是處理對象(一條數據),就到Serializer類裏面找to_representation()方法;若是是處理QuerySet(多條數據),就去ListSerializer裏面找。

 

 

get_attribute()方法的具體實現:

循環字段列表獲取字段對應的值:

  獲取instance對象對應的字段的屬性值,而後把這個屬性值賦值給instance,而後繼續循環,直到instance從字段裏面獲取不到值爲止,即取數據表中真正的數值,

如 UserInfo 表中有外鍵group,想要獲取到group字段對應的表對象的title字段:group = serializers.CharField(source="group.title"),則此時attrs=['group', 'title'],

第一輪循環時:instance=getattr(UserInfo object,group)=UserGroup object,

第二輪循環時instance=getattr(UserGroup object,title)=A組,此時便經過外鍵獲取到UserGroup表中的title字段的值了。

  當字段是一個能夠被調用的對象(如函數或方法)時,就在這個字段後面加上「()」,直接執行這個函數或者方法,將返回值賦值給instance。

如 獲取一個choices字段的文本:user_type=serializers.CharField(source="get_user_type_display") ,這個get_user_type_display就是一個可調用的方法。

 

對於字段是serializers.HyperlinkedIdentityField()對象的時候執行的to_representation()方法:

因此,咱們在使用HyperlinkedIdentityField()的時候要傳遞參數:

 

4、rest framework序列化驗證用戶請求數據

一、自定義類 驗證用戶請求數據

from django.conf.urls import url
from api import views

urlpatterns = [
    url(r'^(?P<version>[v1|v2]+)/userGroup/$', views.UserGroupView.as_view()),
]
urls.py
import json

from django.shortcuts import HttpResponse
from rest_framework.views import APIView
from rest_framework import serializers

from api import models


class MyValidator(object):
    """自定製校驗規則"""
    def __init__(self, base):
        self.base = base

    def __call__(self, value):
        # 設置一個必須以self.base開頭的規則
        if not value.startswith(self.base):
            message = "this field must start with %s." % self.base
            raise serializers.ValidationError(message)

    def set_context(self, serializer_field):
        pass


class UserGroupSerializer(serializers.Serializer):
    title = serializers.CharField(
        error_messages={"required": "標題不能爲空"},
        validators=[MyValidator("a-")],
    )  # 校驗的字段:title


class UserGroupView(APIView):
    """驗證用戶請求數據"""

    def post(self, request, *args, **kwargs):
        # request.data :用戶提交過來的數據
        serl = UserGroupSerializer(data=request.data)

        # 若是請求數據是合法的,就獲取到的數據,不然打印錯誤信息
        if serl.is_valid():
            print(serl.validated_data["title"])
        else:
            print(serl.errors)
        return HttpResponse("xxx")
views.py

二、源碼

在Serializer類中實現了請求數據校驗:

 

from django.conf.urls import url
from api import views

urlpatterns = [
    url(r'^(?P<version>[v1|v2]+)/userGroup/$', views.UserGroupView.as_view()),
]
urls.py
from django.shortcuts import HttpResponse
from rest_framework.views import APIView
from rest_framework import serializers
from api import models


class UserGroupSerializer(serializers.Serializer):
    title = serializers.CharField(
        error_messages={"required": "標題不能爲空"},
        # validators=[MyValidator("a-")],
    )  # 校驗的字段:title

    def validate_title(self, value):
        """自定義title字段鉤子方法"""
        if not value.startswith("a-"):
            message = "值必須以'a-'開頭."
            raise serializers.ValidationError(message)
        return value


class UserGroupView(APIView):
    """驗證用戶請求數據"""

    def post(self, request, *args, **kwargs):
        # request.data :用戶提交過來的數據
        serl = UserGroupSerializer(data=request.data)

        # 若是請求數據是合法的,就獲取到的數據,不然打印錯誤信息
        if serl.is_valid():
            print(serl.validated_data["title"])
        else:
            print(serl.errors)
        return HttpResponse("xxx")
views.py
相關文章
相關標籤/搜索