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 = []  # 移除已存在所有验证器限制
    
  • 自定义验证器的编写已经在序列化器章节有了更详细的介绍

posted @ 2020-06-28 17:58  言兴  阅读(151)  评论(0)    收藏  举报