1. 程式人生 > 實用技巧 >Django rest framework(二)-路由器、解析器、渲染器、驗證器

Django rest framework(二)-路由器、解析器、渲染器、驗證器

本文為學習官方文件時所做筆記,可以看做是官方文件的全文翻譯

目錄

路由器

  • 自定義一個只讀API

    from rest_framework.routers import Route, DynamicRoute, SimpleRouter
    
    class CustomReadOnlyRouter(SimpleRouter):
        """
        A router for read-only APIs, which doesn't use trailing slashes.
        """
        routes = [
            Route(
                url=r'^{prefix}$',
                mapping={'get': 'list'},
                name='{basename}-list',
                detail=False,
                initkwargs={'suffix': 'List'}
            ),
            Route(
                url=r'^{prefix}/{lookup}$',
                mapping={'get': 'retrieve'},
                name='{basename}-detail',
                detail=True,
                initkwargs={'suffix': 'Detail'}
            ),
            DynamicRoute(
                url=r'^{prefix}/{lookup}/{url_path}$',
                name='{basename}-{url_name}',
                detail=True,
                initkwargs={}
            ) #一般用於額外動作@action時的route設定
        ]
    

    檢視:

    class UserViewSet(viewsets.ReadOnlyModelViewSet):
        """
        A viewset that provides the standard actions
        """
        queryset = User.objects.all()
        serializer_class = UserSerializer
        lookup_field = 'username'
    
        @action(detail=True)
        def group_names(self, request, pk=None):
            """
            Returns a list of all the group names that the given
            user belongs to.
            """
            user = self.get_object()
            groups = user.groups.all()
            return Response([group.name for group in groups])
    

    路由

    router = CustomReadOnlyRouter()
    router.register('users', UserViewSet)
    urlpatterns = router.urls
    

解析器

  • 解析器

    • JSONParser

    • FormParser:解析HTML表單內容

    • MultiPartParser:解析多部分HTML表單內容,支援檔案上傳

    • FileParser:解析原始檔案上傳內容 ,request.file,由於該解析器的media_type匹配任何內容型別,因此FileUploadParser通常應該是API檢視上設定的惟一解析器。

      # views.py
      class FileUploadView(views.APIView):
          parser_classes = [FileUploadParser]
      
          def put(self, request, filename, format=None):
              file_obj = request.data['file']
              # ...
              # do some stuff with uploaded file
              # ...
              return Response(status=204)
      
      # urls.py
      urlpatterns = [
          # ...
          url(r'^upload/(?P<filename>[^/]+)$', FileUploadView.as_view())
      
    • 第三方包

      • $ pip install djangorestframework-xml #解析XML
        $ pip install djangorestframework-yaml #解析YAML
        
  • 設定解析器

    @api_view(['GET'])
    @parser_classes([JSONParser]) #裝飾器設定
    
    -----------
    REST_FRAMEWORK = {
        'DEFAULT_PARSER_CLASSES': [
            'rest_framework.parsers.JSONParser',
        ]
    }#全域性設定
    
    ------------
    parser_classes = [FileUploadParser] #檢視設定
    
  • 文字解析器為例

    class PlainTextParser(BaseParser):
        """
        Plain text parser.
        """
        media_type = 'text/plain'
    
        def parse(self, stream, media_type=None, parser_context=None):
            """
            Simply return a string representing the body of the request.
            stream:表示請求主體的流物件。
            media_media:請求內容的格式,通常由header決定而非伺服器決定
            parser_context:可選的。如果提供,此引數將是一個字典,其中包含解析請求內容可能需要的任何附加上下文。
            
            """
            return stream.read()
    

渲染器

  • 渲染器

    • JSONRenderer

    • TemplateHTMLRenderer:解析為Django標準的模板呈現,不需要Serializer序列化

      class UserDetail(generics.RetrieveAPIView):
          """
          A view that returns a templated HTML representation of a given user.
          """
          queryset = User.objects.all()
          renderer_classes = [TemplateHTMLRenderer]
      
          def get(self, request, *args, **kwargs):
              self.object = self.get_object()
              return Response({'user': self.object}, template_name='user_detail.html')
      

      如果要通過一個服務端同時提供標準模板和序列化資料,那麼該渲染器必須放在其他所有渲染器的前面

    • StaticHTMLRenderer:返回已經預渲染好的HTML內容

      @api_view(['GET'])
      @renderer_classes([StaticHTMLRenderer])
      def simple_html_view(request):
          data = '<html><body><h1>Hello, world</h1></body></html>'
          return Response(data)
      
    • BrowsableAbleAPIRenderer:將渲染資料到一個可供瀏覽的HTML頁面,該渲染器將確定其他的某個渲染器將獲得最高優先順序,並使用該優先順序在HTML頁面中顯示API樣式響應。

      • 自定義該渲染器使其以Json格式渲染而不是以某個最高優先順序的渲染器進行渲染

        class CustomBrowsableAPIRenderer(BrowsableAPIRenderer):
            def get_default_renderer(self, view):
                return JSONRenderer()
        
    • AdminRenderer:以後臺管理風格返回渲染,注意具有巢狀或列表序列化器的檢視,AdminRenderer無法正確的處理它們,因為HTML表單不能良好地地支援它們

    • HTMLFormRender:將序列化器返回的資料呈現為HTML表單格式,但注意:該渲染器的輸出不包括封閉的

      標記、隱藏的CSRF輸入或任何提交按鈕。 這個渲染器通常不會直接使用,而是可以通過傳遞一個序列化例項給render_form模板標記來在模板中使用。

      {% load rest_framework %}
      
      <form action="/submit-report/" method="post">
          {% csrf_token %}
          {% render_form serializer %}
          <input type="submit" value="Save" />
      </form>
      
    • MultiPartRenderer:該渲染器用於呈現HTML複合表單資料 ,它不適合作為響應渲染器,而是通常用於建立測試請求。

  • 設定

    #全域性設定
    REST_FRAMEWORK = {
        'DEFAULT_RENDERER_CLASSES': [
            'rest_framework.renderers.JSONRenderer', #json渲染,第一個為預設
            'rest_framework.renderers.BrowsableAPIRenderer', #可瀏覽API渲染
        ]
    } 
    
    --------------
    #檢視設定
    class UserCountView(APIView):
        renderer_classes = [JSONRenderer]
        
    --------------
    #裝飾器設定
    @api_view(['GET'])
    @renderer_classes([JSONRenderer])
    
  • 自定義渲染器

    • 重寫 BaseRenderer ,設定media_typeformat屬性,重寫render(self, data, media_type=None, renderer_context=None) 方法,返回一個位元組字串

      • dataResponse()例項化中設定的資料。
      • media_type=None:可選的,為可接收資料資料型別,由內容協商階段決定,通常由請求頭中設定的型別決定
      • render_context=None:可選的,為檢視提供額外的上下文資訊
    • 例子:以純文字和影象為data資料作為響應內容的渲染器。

      from django.utils.encoding import smart_unicode
      from rest_framework import renderers
      
      
      class PlainTextRenderer(renderers.BaseRenderer):
          media_type = 'text/plain'
          format = 'txt'
          charset = 'iso-8859-1' #預設為UTF8編碼,若返回的data是二進位制則設定該屬性為None
          render_style = 'binary' #這樣做還可以確保(可瀏覽API)不會嘗試將二進位制內容顯示為字串。
      
          def render(self, data, media_type=None, renderer_context=None):
              return data.encode(self.charset)
      
      class JPEGRenderer(renderers.BaseRenderer):
          media_type = 'image/jpeg'
          format = 'jpg'
          charset = None
          render_style = 'binary'
      
          def render(self, data, media_type=None, renderer_context=None):
              return data
      
    • 根據請求媒體型別決定序列化類:單個檢視函式返回不同渲染型別的資料

      @api_view(['GET'])
      @renderer_classes([TemplateHTMLRenderer, JSONRenderer])
      def list_users(request):
          """
          A view that can return JSON or HTML representations
          of the users in the system.
          """
          queryset = Users.objects.filter(active=True)
      
          if request.accepted_renderer.format == 'html':
              # TemplateHTMLRenderer takes a context dict,
              # and additionally requires a 'template_name'.
              # It does not require serialization.
              data = {'users': queryset}
              return Response(data, template_name='list_users.html')
      
          # JSONRenderer requires serialized data as normal.
          serializer = UserSerializer(instance=queryset)
          data = serializer.data
          return Response(data)
      
    • 服務一個大類的媒體型別

      #渲染器中
      media_type = image/* #表示所有圖片型別
      media_type = */* #表示所有型別
      
      #如果渲染器中沒有設定media_type,則必須在響應中明確指出
      return Response(data, content_type='image/png')
      
    • 第三方包

      $ pip install djangorestframework-xml #渲染為XML
      $ pip install djangorestframework-yaml #渲染為YAML
      $ pip install djangorestframework-jsonp #渲染為JSONP
      $ pip install MessagePace #快速、高效的二進位制序列化格式
      $ pip install drf-renderer-xlsx #XLSX是世界上最流行的二進位制電子表格格式
      $ pip install djangorestframework-csv #csv格式
      $ pip drf-ujson-renderer #可以提供顯著更快的JSON渲染
      ¥pip djangorestframework-camel-case #為REST框架提供駝峰式JSON呈現器和解析器,允許序列化器使用python風格的下標欄位名,但在API中以javascript風格的駝峰大小寫欄位名公開。
      Pandas #Django REST panda提供了一個序列化器和渲染器,支援通過panda DataFrame API進行的額外的資料處理和輸出。Django REST panda包括Pandas風格的CSV檔案、Excel工作簿(包括.xls和.xlsx)和許多其他格式的呈現器。
      $ pip Rest Framework Latex #通過Laulatex輸出PDFs
      

驗證器

驗證器使用

  • DRF中的驗證器與ModelForm中驗證器的不同:前者全部在序列化類中進行驗證,後者一部分在表單中驗證,一部分在模型例項中驗證。前者的驗證規則有以下好處

    • 程式碼解耦,引入了適當的關注點分離,使程式碼行為更加明顯
    • 在使用快捷的ModelSerializer類和使用顯式序列化器類之間切換很容易。ModelSerializer使用的任何驗證行為都能很方便的重用
    • 列印序列化例項的repr函式將直接顯示它應用的所有驗證規則,而不會在模型例項上還會有額外的隱藏驗證規則,方便開發者直接閱讀

    模型:

    class CustomerReportRecord(models.Model):
        time_raised = models.DateTimeField(default=timezone.now, editable=False)
        #在模型上使用unique驗證
        reference = models.CharField(unique=True, max_length=20)
        description = models.TextField()
    

    序列化:

    #ModelSerializer將自動處理驗證規則
    class CustomerReportSerializer(serializers.ModelSerializer):
        class Meta:
            model = CustomerReportRecord
    

    列印:

    >>> serializer = CustomerReportSerializer()
    >>> print(repr(serializer))
    CustomerReportSerializer():
        id = IntegerField(label='ID', read_only=True)
        time_raised = DateTimeField(read_only=True)
        reference = CharField(max_length=20, validators=[<UniqueValidator(queryset=CustomerReportRecord.objects.all())>]) #打印出了在模型上的驗證規則
        description = CharField(style={'type': 'textarea'})
    
  • 有時您希望將驗證邏輯放在可重用元件中,以便在整個程式碼庫中輕鬆重用。這可以通過使用驗證器函式和驗證器類來實現。 並且由於前文這種更顯式的驗證規則,DRF框架包含了一些在核心Django中不可用的驗證器類。下面將詳細介紹這些類。

    • UniqueValidator驗證器:強制執行unique=True約束

      • queryset :必須引數,在該查詢集中強制唯一性。
      • message:驗證錯誤提示資訊
      • lookup:查詢行為,預設為exact
      from rest_framework.validators import UniqueValidator
      
      #該序列化器用於序列化欄位上
      slug = SlugField(
          max_length=100,
          validators=[UniqueValidator(queryset=BlogPost.objects.all())]
      )
      
    • UniqueTogetherValidator:強制unique_together約束

      • queryset :必須引數,在該查詢集中強制唯一性
      • message:驗證錯誤提示資訊
      • fields:必須引數,欄位名的列表或元組,這些欄位構成一個惟一的集合。
      from rest_framework.validators import UniqueTogetherValidator
      
      class ExampleSerializer(serializers.Serializer):
          # ...
          #該驗證器用在序列化元類中
          class Meta:
              validators = [
                  UniqueTogetherValidator(
                      queryset=ToDoItem.objects.all(),
                      fields=['list', 'position']
                  )
              ]
      
    • UniqueForDateValidatorUniqueForMonthValidatorUniqueForYearValidator:分別強制unique_for_date, unique_for_monthunique_for_year 約束

      • queryset :必須引數,在該查詢集中強制唯一性
      • fields:必須引數,根據給定日期範圍對該欄位進行唯一性驗證
      • date_field :必須引數,用於確定惟一性約束的日期範圍
      • message:驗證錯誤提示資訊
      from rest_framework.validators import UniqueForYearValidator
      
      class ExampleSerializer(serializers.Serializer):
          published = serializers.DateTimeField(required=True)#如果希望日期欄位是可寫的,惟一值得注意的是應該確保它始終在輸入資料中可用,可以通過設定預設引數,也可以通過設定required=True來保證。
          
          #該驗證器用在序列化元類中
          class Meta:
              validators = [
                  UniqueForYearValidator(
                      queryset=BlogPostItem.objects.all(),
                      field='slug',
                      date_field='published'
                  )
              ]
      

      注意:日期欄位有以下三種情況

      • 可寫的:如果希望日期欄位是可寫的,惟一值得注意的是應該確保它始終在輸入資料中可用,可以通過設定預設引數,也可以通過設定required=True來保證。

        published = serializers.DateTimeField(required=True)
        
      • 只讀的:設定read_only=True 並給與一個預設值

        published = serializers.DateTimeField(read_only=True, default=timezone.now)
        
      • 隱藏的:使用HiddenField 並設定預設值。此欄位型別不接受使用者輸入,但總是將其預設值返回給序列化器中的validated_data

        published = serializers.HiddenField(default=timezone.now)
        

高階特性:預設值

有時序列化器不需要使用者輸入,但依然需要一個值作為驗證輸入

  • 使用HiddenField 並設定預設值。此欄位型別不接受使用者輸入,但總是將其預設值返回給序列化器中的validated_data

  • 使用read_only=True 並同時使用default 給與一個預設值,此欄位將在序列化器輸出表示中使用,但不能由使用者直接設定。

  • 高階特性:兩個可用的預設類

    • CurrentUserDefault:可用於表示當前使用者的預設類。使用它必須在例項化序列化器時將request作為上下文字典的一部分提供。

      owner = serializers.HiddenField(
          default=serializers.CurrentUserDefault()
      )
      
    • createOnlyDefault:在建立操作期間只能用於設定預設引數的預設類 ,在更新期間,該欄位被省略

      #在建立時,creat_at預設直接建立一個當前時間作為值,之後的更新將不能再對此欄位進行更新,
      created_at = serializers.DateTimeField(
          default=serializers.CreateOnlyDefault(timezone.now)
      )
      

自定義驗證器

  • 在一些模稜兩可,如巢狀或更復雜的情況下,可能需要顯式地處理驗證,而不是依賴於ModelSerializer自動生成的序列化器類,在這種情況下在元類中設定validators = []即可排除掉預設生成的所有的驗證器,再用以下三種方式顯式的編寫驗證器邏輯

    class BillingRecordSerializer(serializers.ModelSerializer):
    	published = serializers.DateTimeField(required=True)# 1.在欄位中設定
        def validate_date(self,value)
        	#2.2 為某個欄位編寫驗證邏輯.
            
        def validate(self, attrs):
            #2.2 為所有欄位編寫驗證邏輯.
    
        class Meta:
            fields = ['client', 'date', 'amount']
            extra_kwargs = {'client': {'required': False}} #3.使用extra_kwargs對欄位新增引數
            validators = []  # 移除已存在所有驗證器限制
    
  • 自定義驗證器的編寫已經在序列化器章節有了更詳細的介紹