1. 程式人生 > >Django 的DRF框架 Django rest Framework詳解

Django 的DRF框架 Django rest Framework詳解

一、DRF簡單操作:

1、建立序列化器

class BookInfoSerializer(serializers.ModelSerializer):
    """圖書資料序列化器"""
    class Meta:
        model = BookInfo
        fields = '__all__'
  • model 指明該序列化器處理的資料欄位從模型類BookInfo參考生成
  • fields 指明該序列化器包含模型類中的哪些欄位,'__all__'指明包含所有欄位

2、編寫檢視:

from rest_framework.viewsets import ModelViewSet
from .serializers import BookInfoSerializer
from .models import BookInfo

class BookInfoViewSet(ModelViewSet):
    queryset = BookInfo.objects.all()
    serializer_class = BookInfoSerializer
  • queryset 指明該檢視集在查詢資料時使用的查詢集
  • serializer_class 指明該檢視在進行序列化或反序列化時使用的序列化器

3、定義路由

from . import views
from rest_framework.routers import DefaultRouter

urlpatterns = [
    ...
]

router = DefaultRouter()  # 可以處理檢視的路由器
router.register(r'books', views.BookInfoViewSet)  # 向路由器中註冊檢視集

urlpatterns += router.urls  # 將路由器中的所以路由資訊追到到django的路由列表中

4、執行測試:

python manage.py runserver

二、定義序列化器Serializer

1、定義方法:

Django REST framework中的Serializer使用類來定義,須繼承rest_framework.serializers.Serializer

1.1建立資料庫模型類BookInfo:

class BookInfo(models.Model):
    btitle = models.CharField(max_length=20, verbose_name='名稱')
    bpub_date = models.DateField(verbose_name='釋出日期', null=True)
    bread = models.IntegerField(default=0, verbose_name='閱讀量')
    bcomment = models.IntegerField(default=0, verbose_name='評論量')
    image = models.ImageField(upload_to='booktest', verbose_name='圖片', null=True)

1.2 為模型類提供一個序列化器(寫法一):

class BookInfoSerializer(serializers.Serializer):
    """圖書資料序列化器"""
    id = serializers.IntegerField(label='ID', read_only=True)
    btitle = serializers.CharField(label='名稱', max_length=20)
    bpub_date = serializers.DateField(label='釋出日期', required=False)
    bread = serializers.IntegerField(label='閱讀量', required=False)
    bcomment = serializers.IntegerField(label='評論量', required=False)
    image = serializers.ImageField(label='圖片', required=False)

1.2.1為模型類提供一個序列化器(寫法二):

class BookInfoSerializer(serializers.ModelSerializer):
    """圖書資料序列化器"""
    class Meta:
        model = BookInfo
        fields = '__all__'

1.3 建立Serializer物件

Serializer(instance=None, data=empty, **kwarg)

說明:

序列化時,將模型類物件傳入instance引數

用於反序列化時,將要被反序列化的資料傳入data引數

除了instance和data引數外,在構造Serializer物件是,還可以通過context引數額外新增資料

serializer = AccountSerializer(account, context={'request': request})

通過content引數附加的資料,可以通過Serializer物件的context屬性獲取。

1.4序列化使用

1.4.1 查詢一個圖書物件

from booktest.models import BookInfo

book = BookInfo.objects.get(id=2)

1.4.2 構造序列化器物件

from booktest.serializers import BookInfoSerializer

serializer = BookInfoSerializer(book)

1.4.3 獲取序列化資料通過data屬性可以獲取序列化後的資料

serializer.data

1.4.4 如果要被序列化的是包含多條資料的查詢集QuerySet,可以通過新增many=True引數補充說明

book_qs = BookInfo.objects.all()
serializer = BookInfoSerializer(book_qs, many=True)
serializer.data
# [OrderedDict([('id', 2), ('btitle', '天龍八部'), ('bpub_date', '1986-07-24'), ('bread', 36), ('bcomment', 40), ('image', N]), OrderedDict([('id', 3), ('btitle', '笑傲江湖'), ('bpub_date', '1995-12-24'), ('bread', 20), ('bcomment', 80), ('image'ne)]), OrderedDict([('id', 4), ('btitle', '雪山飛狐'), ('bpub_date', '1987-11-11'), ('bread', 58), ('bcomment', 24), ('ima None)]), OrderedDict([('id', 5), ('btitle', '西遊記'), ('bpub_date', '1988-01-01'), ('bread', 10), ('bcomment', 10), ('im', 'booktest/xiyouji.png')])]

三、反序列化使用

1、驗證

在獲取反序列化的資料前,必須呼叫is_valid()方法進行驗證,驗證成功返回True,否則返回False。

驗證成功,可以通過序列化器物件的validated_data屬性獲取資料。

驗證失敗,可以通過序列化器物件的errors屬性獲取錯誤資訊,返回字典,包含了欄位和欄位的錯誤。如果是非欄位錯誤,可以通過修改REST framework配置中的NON_FIELD_ERRORS_KEY來控制錯誤字典中的鍵名。

class BookInfoSerializer(serializers.Serializer):
    """圖書資料序列化器"""
    id = serializers.IntegerField(label='ID', read_only=True)
    btitle = serializers.CharField(label='名稱', max_length=20)
    bpub_date = serializers.DateField(label='釋出日期', required=False)
    bread = serializers.IntegerField(label='閱讀量', required=False)
    bcomment = serializers.IntegerField(label='評論量', required=False)
    image = serializers.ImageField(label='圖片', required=False)

通過構造序列化器物件,並將要反序列化的資料傳遞給data構造引數,進而進行驗證

from booktest.serializers import BookInfoSerializer
data = {'bpub_date': 123}
serializer = BookInfoSerializer(data=data)
serializer.is_valid()  # 返回False
serializer.errors
# {'btitle': [ErrorDetail(string='This field is required.', code='required')], 'bpub_date': [ErrorDetail(string='Date has wrong format. Use one of these formats instead: YYYY[-MM[-DD]].', code='invalid')]}
serializer.validated_data  # {}

data = {'btitle': 'python'}
serializer = BookInfoSerializer(data=data)
serializer.is_valid()  # True
serializer.errors  # {}
serializer.validated_data  #  OrderedDict([('btitle', 'python')])

is_valid()方法還可以在驗證失敗時丟擲異常serializers.ValidationError,可以通過傳遞raise_exception=True引數開啟,REST framework接收到此異常,會向前端返回HTTP 400 Bad Request響應。

# Return a 400 response if the data was invalid.
serializer.is_valid(raise_exception=True)

驗證的深入三種方式:

1)validate_<field_name>

<field_name>欄位進行驗證,如

class BookInfoSerializer(serializers.Serializer):
    """圖書資料序列化器"""
    ...

    def validate_btitle(self, value):
        if 'django' not in value.lower():
            raise serializers.ValidationError("圖書不是關於Django的")
        return value

測試

from booktest.serializers import BookInfoSerializer
data = {'btitle': 'python'}
serializer = BookInfoSerializer(data=data)
serializer.is_valid()  # False   
serializer.errors
#  {'btitle': [ErrorDetail(string='圖書不是關於Django的', code='invalid')]}

2)validate在序列化器中需要同時對多個欄位進行比較驗證時,可以定義validate方法來驗證,如

class BookInfoSerializer(serializers.Serializer):
    """圖書資料序列化器"""
    ...

    def validate(self, attrs):
        bread = attrs['bread']
        bcomment = attrs['bcomment']
        if bread < bcomment:
            raise serializers.ValidationError('閱讀量小於評論量')
        return attrs

測試

from booktest.serializers import BookInfoSerializer
data = {'btitle': 'about django', 'bread': 10, 'bcomment': 20}
s = BookInfoSerializer(data=data)
s.is_valid()  # False
s.errors
#  {'non_field_errors': [ErrorDetail(string='閱讀量小於評論量', code='invalid')]}

3)validators在欄位中新增validators選項引數,也可以補充驗證行為,如

def about_django(value):
    if 'django' not in value.lower():
        raise serializers.ValidationError("圖書不是關於Django的")

class BookInfoSerializer(serializers.Serializer):
    """圖書資料序列化器"""
    id = serializers.IntegerField(label='ID', read_only=True)
    btitle = serializers.CharField(label='名稱', max_length=20, validators=[about_django])
    bpub_date = serializers.DateField(label='釋出日期', required=False)
    bread = serializers.IntegerField(label='閱讀量', required=False)
    bcomment = serializers.IntegerField(label='評論量', required=False)
    image = serializers.ImageField(label='圖片', required=False)

測試:

from booktest.serializers import BookInfoSerializer
data = {'btitle': 'python'}
serializer = BookInfoSerializer(data=data)
serializer.is_valid()  # False   
serializer.errors
#  {'btitle': [ErrorDetail(string='圖書不是關於Django的', code='invalid')]}

2、儲存

驗證成功後,如果需要在返回資料物件的時候,也將資料儲存到資料庫中,則可以進行如下修改

class BookInfoSerializer(serializers.Serializer):
    """圖書資料序列化器"""
    ...

    def create(self, validated_data):
        """新建"""
        return BookInfo.objects.create(**validated_data)

    def update(self, instance, validated_data):
        """更新,instance為要更新的物件例項"""
        instance.btitle = validated_data.get('btitle', instance.btitle)
        instance.bpub_date = validated_data.get('bpub_date', instance.bpub_date)
        instance.bread = validated_data.get('bread', instance.bread)
        instance.bcomment = validated_data.get('bcomment', instance.bcomment)
        instance.save()
        return instance

實現了上述兩個方法後,在反序列化資料的時候,就可以通過save()方法返回一個數據物件例項了

book = serializer.save()

如果建立序列化器物件的時候,沒有傳遞instance例項,則呼叫save()方法的時候,create()被呼叫,相反,如果傳遞了instance例項,則呼叫save()方法的時候,update()被呼叫。

from db.serializers import BookInfoSerializer
data = {'btitle': '封神演義'}
serializer = BookInfoSerializer(data=data)
serializer.is_valid()  # True
serializer.save()  # <BookInfo: 封神演義>

from db.models import BookInfo
book = BookInfo.objects.get(id=2)
data = {'btitle': '倚天劍'}
serializer = BookInfoSerializer(book, data=data)
serializer.is_valid()  # True
serializer.save()  # <BookInfo: 倚天劍>
book.btitle  # '倚天劍'

兩點說明:

1) 在對序列化器進行save()儲存時,可以額外傳遞資料,這些資料可以在create()和update()中的validated_data引數獲取到

serializer.save(owner=request.user)

四、模型類序列化器ModelSerializer

ModelSerializer與常規的Serializer相同,但提供了:

  • 基於模型類自動生成一系列欄位
  • 基於模型類自動為Serializer生成validators,比如unique_together
  • 包含預設的create()和update()的實現

1、定義

我們建立一個BookInfoSerializer

class BookInfoSerializer(serializers.ModelSerializer):
    """圖書資料序列化器"""
    class Meta:
        model = BookInfo
        fields = '__all__'
  • model 指明參照哪個模型類
  • fields 指明為模型類的哪些欄位生成

 

2、新增額外欄位

我們可以使用extra_kwargs引數為ModelSerializer新增或修改原有的選項引數

class BookInfoSerializer(serializers.ModelSerializer):
    """圖書資料序列化器"""
    class Meta:
        model = BookInfo
        fields = ('id', 'btitle', 'bpub_date', 'bread', 'bcomment')
        extra_kwargs = {
            'bread': {'min_value': 0, 'required': True}},
            'bcomment': {'max_value': 0, 'required': True}},
        }

五、Request與Response

1、request

常用屬性:

1).request.data 返回解析之後的請求體資料。類似於Django中標準的request.POSTrequest.FILES屬性,但提供如下特性:

  • 包含了解析之後的檔案和非檔案資料
  • 包含了對POST、PUT、PATCH請求方式解析後的資料
  • 利用了REST framework的parsers解析器,不僅支援表單型別資料,也支援JSON資料

2).query_params

request.query_params與Django標準的request.GET相同,只是更換了更正確的名稱而已。

2、response

REST framework提供了一個響應類Response,使用該類構造響應物件時,響應的具體資料內容會被轉換(render渲染)成符合前端需求的型別。

REST framework提供了Renderer 渲染器,用來根據請求頭中的Accept(接收資料型別宣告)來自動轉換響應資料到對應格式。如果前端請求中未進行Accept宣告,則會採用預設方式處理響應資料,我們可以通過配置來修改預設響應格式。

REST_FRAMEWORK = {
    'DEFAULT_RENDERER_CLASSES': (  # 預設響應渲染類
        'rest_framework.renderers.JSONRenderer',  # json渲染器
        'rest_framework.renderers.BrowsableAPIRenderer',  # 瀏覽API渲染器
    )
}

構造方式

Response(data, status=None, template_name=None, headers=None, content_type=None)
  • data: 為響應準備的序列化處理後的資料;
  • status: 狀態碼,預設200;
  • template_name: 模板名稱,如果使用HTMLRenderer 時需指明;
  • headers: 用於存放響應頭資訊的字典;
  • content_type: 響應資料的Content-Type,通常此引數無需傳遞,REST framework會根據前端所需型別資料來設定該引數。

1).data

傳給response物件的序列化後,但尚未render處理的資料

2).status_code

狀態碼的數字

3).content

經過render處理後的響應資料

 

六、檢視基類

1、兩個基類

1)APIView

APIView中仍以常規的類檢視定義方法來實現get() 、post() 或者其他請求方式的方法。

from rest_framework.views import APIView
from rest_framework.response import Response

# url(r'^books/$', views.BookListView.as_view()),
class BookListView(APIView):
    def get(self, request):
        books = BookInfo.objects.all()
        serializer = BookInfoSerializer(books, many=True)
        return Response(serializer.data)

2)GenericAPIView

繼承APIView

  • 列表檢視與詳情檢視通用:
    • queryset 列表檢視的查詢集
    • serializer_class 檢視使用的序列化器
  • 列表檢視使用:
    • pagination_class 分頁控制類
    • filter_backends 過濾控制後端
  • 詳情頁檢視使用:
    • lookup_field 查詢單一資料庫物件時使用的條件欄位,預設為'pk'
    • lookup_url_kwarg 查詢單一資料時URL中的引數關鍵字名稱,預設與look_field相同

get_object(self) 返回詳情檢視所需的模型類資料物件,預設使用lookup_field引數來過濾queryset。 在試圖中可以呼叫該方法獲取詳情資訊的模型類物件。

舉例:

# url(r'^books/(?P<pk>\d+)/$', views.BookDetailView.as_view()),
class BookDetailView(GenericAPIView):
    queryset = BookInfo.objects.all()
    serializer_class = BookInfoSerializer

    def get(self, request, pk):
        book = self.get_object()
        serializer = self.get_serializer(book)
        return Response(serializer.data)