zoukankan      html  css  js  c++  java
  • ModelViewSet+ModelSerializer

    1.DRF初始化

    • 1.DRF框架的8个核心功能

      1.认证(用户登录校验用户名密码或者token是否合法)

      2.权限(根据不同的用户角色,可以操作不同的表)

      3.限流(限制接口访问速度)

      4.序列化(返回json)

      5.分页

      6.版本(接口版本号,用 v1/v2/v3)

      #api.example.com/v1/login # 只有用户名密码登录
      # api.example.com/v2/login # 手机号,微信 登录

      7.过滤(username=zhangsan)

      8.排序(ordering=-id)

    • 2.相关包

      1.序列化相关


      serializer
      ModelSerializer

      2.DRF视图函数继承


      APIView
      ModelViewSet

    1.1 安装DjangoRestFramework


    pip install djangorestframework==3.11.1
    pip install django-filter==2.3.0 # 过滤器
    pip install markdown # Markdown support for the browsable
    API.

    1.2 在syl/settings.py中注册


    INSTALLED_APPS = [
    'django_filters',
    'rest_framework',
    ]

    1.3 syl/settings.py配置DRF: 全局配置

    # 过滤器
    # 1,安装 django-filter
    # 2,注册应用
    # 3,配置settings, 在view里配置可过滤的字段
    # 4,使用 查询字符串携带过滤信息
    REST_FRAMEWORK = {
    # 文档报错: AttributeError: ‘AutoSchema’ object has no attribute ‘get_link’
    # 用下面的设置可以解决
    'DEFAULT_SCHEMA_CLASS': 'rest_framework.schemas.AutoSchema',
    # 默认设置是:
    # 'DEFAULT_SCHEMA_CLASS': 'rest_framework.schemas.openapi.AutoSchema',
    # 异常处理器
    # 'EXCEPTION_HANDLER': 'user.utils.exception_handler',
    # Base API policies
    'DEFAULT_RENDERER_CLASSES': [
    'rest_framework.renderers.JSONRenderer',
    'rest_framework.renderers.BrowsableAPIRenderer',
    ],
    'DEFAULT_PARSER_CLASSES': [
    'rest_framework.parsers.JSONParser',
    'rest_framework.parsers.FormParser',
    'rest_framework.parsers.MultiPartParser'
    ],
    # 1.认证器(全局):用户登录校验用户名密码或者token是否合法
    'DEFAULT_AUTHENTICATION_CLASSES': [
    # 'rest_framework_jwt.authentication.JSONWebTokenAuthentication', # 在
    DRF中配置JWT认证
    # 'rest_framework.authentication.SessionAuthentication', # 使用session时
    的认证器
    # 'rest_framework.authentication.BasicAuthentication' # 提交表单时的认
    证器
    ],
    #2.权限配置(全局): 顺序靠上的严格(根据不同的用户角色,可以操作不同的表)
    'DEFAULT_PERMISSION_CLASSES': [
    # 'rest_framework.permissions.IsAdminUser', # 管理员可以访问
    # 'rest_framework.permissions.IsAuthenticated', # 认证用户可以访

    # 'rest_framework.permissions.IsAuthenticatedOrReadOnly', # 认证用户可以访
    问, 否则只能读取
    # 'rest_framework.permissions.AllowAny', # 所有用户都可以
    访问
    ],
    #3.限流(防爬虫)
    'DEFAULT_THROTTLE_CLASSES': [
    'rest_framework.throttling.AnonRateThrottle',
    'rest_framework.throttling.UserRateThrottle',
    ],
    #3.1限流策略
    'DEFAULT_THROTTLE_RATES': {
    'user': '100/hour', # 认证用户每小时100次
    'anon': '3/day', # 未认证用户每天能访问3次
    },
    'DEFAULT_CONTENT_NEGOTIATION_CLASS':
    'rest_framework.negotiation.DefaultContentNegotiation',
    'DEFAULT_METADATA_CLASS': 'rest_framework.metadata.SimpleMetadata',
    'DEFAULT_VERSIONING_CLASS': None,
    #4.分页(全局):全局分页器, 例如 省市区的数据自定义分页器, 不需要分页
    'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.PageNumberPagination',
    # 每页返回数量
    'PAGE_SIZE': 10, # 默认 None
    #5.过滤器后端
    'DEFAULT_FILTER_BACKENDS': [
    'django_filters.rest_framework.DjangoFilterBackend',
    # 'django_filters.rest_framework.backends.DjangoFilterBackend', 包路径有变

    ],
    #5.1过滤排序(全局):Filtering 过滤排序
    'SEARCH_PARAM': 'search',
    'ORDERING_PARAM': 'ordering',
    'NUM_PROXIES': None,
    #6.版本控制:Versioning 接口版本控制
    'DEFAULT_VERSION': None,
    'ALLOWED_VERSIONS': None,
    'VERSION_PARAM': 'version',
    # Authentication 认证
    # 未认证用户使用的用户类型
    'UNAUTHENTICATED_USER': 'django.contrib.auth.models.AnonymousUser',
    # 未认证用户使用的Token值
    'UNAUTHENTICATED_TOKEN': None,
    # View configuration
    'VIEW_NAME_FUNCTION': 'rest_framework.views.get_view_name',
    'VIEW_DESCRIPTION_FUNCTION': 'rest_framework.views.get_view_description',
    'NON_FIELD_ERRORS_KEY': 'non_field_errors',
    # Testing
    'TEST_REQUEST_RENDERER_CLASSES': [
    'rest_framework.renderers.MultiPartRenderer',
    'rest_framework.renderers.JSONRenderer'
    ],
    'TEST_REQUEST_DEFAULT_FORMAT': 'multipart',
    # Hyperlink settings
    'URL_FORMAT_OVERRIDE': 'format',
    'FORMAT_SUFFIX_KWARG': 'format',
    'URL_FIELD_NAME': 'url',
    # Encoding
    'UNICODE_JSON': True,
    'COMPACT_JSON': True,
    'STRICT_JSON': True,
    'COERCE_DECIMAL_TO_STRING': True,
    'UPLOADED_FILES_USE_URL': True,
    # Browseable API
    'HTML_SELECT_CUTOFF': 1000,
    'HTML_SELECT_CUTOFF_TEXT': "More than {count} items...",
    # Schemas
    'SCHEMA_COERCE_PATH_PK': True,
    'SCHEMA_COERCE_METHOD_NAMES': {
    'retrieve': 'read',
    'destroy': 'delete'
    },
    }

    2.DRF 认证、权限、限流、分页、过滤、序列化

    2.1 user/urls.py

    • ModelViewSet注册路由三部曲


    from django.urls import include, path
    from user import views
    from rest_framework.routers import SimpleRouter, DefaultRouter
    # 自动生成路由方法, 必须使用视图集
    # router = SimpleRouter() # 没有根路由 /user/ 无法识别
    router = DefaultRouter() # 1.有根路由
    router.register(r'user', views.UserViewSet) # 2.配置路由
    urlpatterns = [
    path('index/', views.index),
    path('api-auth/', include('rest_framework.urls', namespace='rest_framework'))
    # 认证地址
    ]
    urlpatterns += router.urls # 3.模块地址
    [<URLPattern '^user/$' [name='user-list']>,
    <URLPattern '^user.(?P<format>[a-z0-9]+)/?$' [name='user-list']>,
    <URLPattern '^user/actived/$' [name='user-actived']>,
    <URLPattern '^user/actived.(?P<format>[a-z0-9]+)/?$' [name='user-actived']>,
    <URLPattern '^user/unactived/$' [name='user-unactived']>,
    <URLPattern '^user/unactived.(?P<format>[a-z0-9]+)/?$' [name='userunactived']>, <URLPattern '^user/(?P<pk>[^/.]+)/$' [name='user-detail']>,
    <URLPattern '^user/(?P<pk>[^/.]+).(?P<format>[a-z0-9]+)/?$' [name='userdetail']>, <URLPattern '^$' [name='api-root']>, <URLPattern '^.(?P<format>[a-z0-
    9]+)/?$' [name='api-root']>] DRF

    2.2 创建 user/serializers.py 写序列化器

    • 功能一:数据校验, 创建/修改数据

      • 在创建数据或者修改数据时校验用户提交的数据是否合法

      • 用户名必须是8位以上,邮箱、手机号是合法的

    • 功能二:序列化

      • 把通过model查询的queryset对象转换成JSON格式


    from rest_framework import serializers
    from user.models import User
    def address_validate(data):
    print(data)
    # data:是用户提交的地址这个字段的数据(河南省 郑州市)
    # 独立校验器
    # raise serializers.ValidationError('请填写实际地址') # 有错就抛出异常
    # 没错就返回数据
    return data
    class UserSerializer(serializers.ModelSerializer):
    # 1.独立校验器:重新设定字段, 替换掉模型中的设定, 重新设定地址的长度为5
    # address_validate是自定义的数据校验函数
    address = serializers.CharField(max_length=255, min_length=5, validators=
    [address_validate])
    # 2.单一字段验证(validate_字段名), 验证地址
    def validate_address(self, data):
    if data == '测试':
    raise serializers.ValidationError('请填写实际地址') # 有错就抛出异常
    return data # 没错就返回结果
    def validate_phone(self, data):
    # 不符合手机号格式
    # raise serializers.ValidationError('手机号格式不正确')
    model = self.root.Meta.model
    num = model.objects.filter(phone=data).count()
    if num > 0:
    raise serializers.ValidationError('手机号已存在')
    return data
    # 3.所有属性验证器
    def validate(self, attrs):
    # attrs:{"username":"zhangsan", "phone":"18538752511", ....}
    # 所有属性验证器
    # self.context 中有request和view上下文
    # self.context['view'].action 可以取到动作
    # attrs 是需要序列化的数据
    # raise serializers.ValidationError('xxx错误') # 有问题报错
    return attrs # 没问题返回数据
    class Meta:
    model = User
    # fields = ('id', ) # 临时添加字段也需要写在这里
    fields = '__all__' # 所有字段
    # exclude = ['id'] # 排除 id 字段
    read_only_fields = ('',) # 指定字段为 read_only,
    # 扩展address: extra_kwargs = {} # 局部替换某些字段的设定, 或者新增设定
    extra_kwargs = {
    "address": {
    "min_length": 5, # 给地址增加 最小长度限制
    "default": '默认测试地址', # 增加默认值
    }
    }
    class UserUnActiveSerializer(serializers.ModelSerializer):
    class Meta:
    model = User
    fields = ('id', 'username', 'is_active') # 临时添加字段也需要写在这里
    # fields = '__all__' # 所有字段

    2.3 user/views.py


    from django.http import HttpResponse
    from django_filters.rest_framework import DjangoFilterBackend
    from rest_framework import viewsets
    from rest_framework.authentication import BasicAuthentication,
    SessionAuthentication
    from rest_framework.decorators import action
    from rest_framework.filters import OrderingFilter
    from rest_framework.permissions import AllowAny, IsAdminUser, IsAuthenticated,
    IsAuthenticatedOrReadOnly
    from rest_framework.response import Response
    from rest_framework.throttling import UserRateThrottle
    from rest_framework.pagination import PageNumberPagination
    from rest_framework.views import APIView
    from rest_framework.permissions import BasePermission, SAFE_METHODS
    from user.models import User
    from user.serializers import UserSerializer, UserUnActiveSerializer
    def index(request):
    # 需要认证才能访问的视图
    return HttpResponse('hello')
    # 分页(局部):自定义分页器 局部
    class PageNum(PageNumberPagination):
    # 查询字符串中代表每页返回数据数量的参数名, 默认值: None
    page_size_query_param = 'page_size'
    # 查询字符串中代表页码的参数名, 有默认值: page
    # page_query_param = 'page'
    # 一页中最多的结果条数
    max_page_size = 2
    # 自定义权限(局部)
    class MyPermission(BasePermission):
    # has_permission 是用户对这个视图有没有 GET POST PUT PATCH DELETE 权限的分别判断
    def has_permission(self, request, view):
    print('has_perm')
    # print(view.kwargs.get("pk"), request.user.id)
    """判断用户对模型有没有访问权"""
    # 任何用户对使用此权限类的视图都有访问权限
    if request.user.is_superuser:
    # 管理员对用户模型有访问权
    return True
    elif view.kwargs.get('pk') == str(request.user.id):
    # 携带的id和用户的id相同时有访问权
    return True
    return False
    # has_object_permission 是用户过了 has_permission 判断有权限以后,再判断这个用户有
    没有对一个具体的对象有没有操作权限
    # 这样设置以后,即使是django admin管理员也只能查询自己user标的信息,不能查询其他用户的
    单条信息
    def has_object_permission(self, request, view, obj):
    print('has_object_perm')
    """获取单个数据时,判断用户对某个数据对象是否有访问权限"""
    if request.user.id == obj.id:
    return True
    return False
    class UserViewSet(viewsets.ModelViewSet):
    """
    完成产品的增删改查
    """
    queryset = User.objects.all()
    serializer_class = UserSerializer # 优先使用 get_serializer_class 返回的序列化

    # # 1.认证:自定义认证类, 自定义会覆盖全局配置
    # authentication_classes = (BasicAuthentication, SessionAuthentication)
    # # 2.权限:自定义权限类
    # permission_classes = (MyPermission,)
    # 3.分页:自定义分页器 覆盖全局配置
    pagination_class = PageNum
    # 4.限流:自定义限流类
    throttle_classes = [UserRateThrottle]
    # 5.过滤:指定过滤方法类, 排序方法类, 一个或多个
    filter_backends = (DjangoFilterBackend, OrderingFilter) # 同时支持过滤和排序
    # 5.1指定排序字段, 不设置, 排序功能不起效
    ordering_fields = ('date_joined', 'id') # ?ordering=-id
    # 5.2指定过滤字段, 不设置, 过滤功能不起效
    filter_fields = ('username', 'phone', 'is_active') # ?
    username=tom&phone=&is_active=true
    # 根据不同的请求, 获得不同的序列化器
    def get_serializer_class(self):
    if self.action == 'unactived':
    return UserUnActiveSerializer
    else:
    return UserSerializer
    @action(methods=['get'], detail=False)
    def unactived(self, request, *args, **kwargs):
    # 获取查询集, 过滤出未激活的用户
    qs = self.queryset.filter(is_active=False)
    # 使用序列化器, 序列化查询集, 并且是
    ser = self.get_serializer(qs, many=True)
    return Response(ser.data)
    @action(methods=['get'], detail=False)
    def actived(self, request, *args, **kwargs):
    # 获取查询集, 过滤出未激活的用户
    qs = self.queryset.filter(is_active=True)
    # 使用序列化器, 序列化查询集, 并且是
    ser = self.get_serializer(qs, many=True)
    return Response(ser.data)

    3.测试接口

    3.1 查询 接口


    #1.查询所有用户
    http://192.168.56.100:8888/user/user/
    #2.查询id=1的用户
    http://192.168.56.100:8888/user/user/1/
    #3.查询 用户名(tom),激活的用户
    http://192.168.56.100:8888/user/user/?username=tom&phone=&is_active=true
    #4.查询所有用户 用id 反向排序
    http://192.168.56.100:8888/user/user/?ordering=-id
    #5.查询用户表中第一页,每页显示一条数据
    http://192.168.56.100:8888/user/user/?page=1&page_size=1

    3.2 自定义认证权限

    3.2.1 测试 全局权限 功能

    • 在浏览器中,打开任意接口,未登录用户只能发送get请求,只有登录用户才能发送post等请求


    # http://192.168.56.100:8888/user/user/
    '''syl/settings.py中设置,只有认证用户可以访问, 否则只能读取
    REST_FRAMEWORK = {
    #2.权限配置(全局): 顺序靠上的严格
    'DEFAULT_PERMISSION_CLASSES': [
    'rest_framework.permissions.IsAuthenticatedOrReadOnly', # 认证用户可以访
    问, 否则只能读取
    ],
    }

    3.2.2 测试 自定义权限 功能

    • 注:自定义权限会覆盖settings.py中配置的全局权限

    • 1.自定义权限( 其实应该写到一个单独的文件内进行公用,这里先写到这里进行测试 )

    # 自定义权限(局部)
    class MyPermission(BasePermission):
    # has_permission 是用户对这个视图有没有 GET POST PUT PATCH DELETE 权限的分别判断
    def has_permission(self, request, view):
    print('has_perm')
    # print(view.kwargs.get("pk"), request.user.id)
    """判断用户对模型有没有访问权"""
    # 任何用户对使用此权限类的视图都有访问权限
    if request.user.is_superuser:
    # 管理员对用户模型有访问权
    return True
    elif view.kwargs.get('pk') == str(request.user.id):
    # 携带的id和用户的id相同时有访问权
    return True
    return False
    # has_object_permission 是用户过了 has_permission 判断有权限以后,再判断这个用户有
    没有对一个具体的对象有没有操作权限
    def has_object_permission(self, request, view, obj):
    print('has_object_perm')
    """获取单个数据时,判断用户对某个数据对象是否有访问权限"""
    if request.user.id == obj.id:
    return True
    return False
    • user/viesws.py视图函数中指定当前视图要用的 权限类


    class UserViewSet(viewsets.ModelViewSet):
    # 2.权限:自定义权限类
    permission_classes = (MyPermission,)

    3.3 限流

    • syl/settings.py中配置限流


    http://192.168.56.100:8888/user/apiview/
    '''修改syl/settings.py配置限速设置'''
    REST_FRAMEWORK = {
    #3.1 限流策略
    'DEFAULT_THROTTLE_RATES': {
    'user': '3/hour', # 认证用户每小时10次
    'anon': '3/day', # 未认证用户每天
    },
    }

    3.4 序列化

    • user/serialzers.py


    from rest_framework import serializers
    from user.models import User
    def address_validate(data):
    # 独立校验器
    # raise serializers.ValidationError('请填写实际地址') # 有错就抛出异常
    # 没错就返回数据
    return data
    class UserSerializer(serializers.ModelSerializer):
    # 1.独立校验器:重新设定字段, 替换掉模型中的设定, 重新设定地址的长度为5
    address = serializers.CharField(max_length=255, min_length=5, validators=
    [address_validate])
    # 2.单一字段验证, 验证地址,validate_字段名
    def validate_address(self, data):
    if data == '测试':
    raise serializers.ValidationError('请填写实际地址') # 有错就抛出异常
    return data # 没错就返回结果
    def validate_phone(self, data):
    # 不符合手机号格式
    # raise serializers.ValidationError('手机号格式不正确')
    model = self.root.Meta.model
    num = model.objects.filter(phone=data).count()
    if num > 0:
    raise serializers.ValidationError('手机号已存在')
    return data
    # 3.所有属性验证器
    def validate(self, attrs):
    # attrs:{"username":"zhangsan", "phone":"18538752511", ....}
    # 所有属性验证器
    # self.context 中有request和view上下文
    # self.context['view'].action 可以取到动作
    # attrs 是需要序列化的数据
    # raise serializers.ValidationError('xxx错误') # 有问题报错
    return attrs # 没问题返回数据
    class Meta:
    model = User # 指定表
    # fields = ('id', ) # 临时添加字段也需要写在这里
    fields = '__all__' # 所有字段
    # exclude = ['id'] # 排除 id 字段
    read_only_fields = ('id',) # 指定字段为 read_only,
    # 扩展address: extra_kwargs = {} # 局部替换某些字段的设定, 或者新增设定
    extra_kwargs = {
    "address": {
    "min_length": 5, # 给地址增加 最小长度限制
    "default": '默认测试地址', # 增加默认值
    }
    }

    3.5 自定义分页

    http://192.168.56.100:8888/user/user/?page=1&page_size=1

    • user/views.py 中定义自定义分页类


    # 分页(局部):自定义分页器 局部
    class PageNum(PageNumberPagination):
    # 查询字符串中代表每页返回数据数量的参数名, 默认值: None
    page_size_query_param = 'page_size'
    # 查询字符串中代表页码的参数名, 有默认值: page
    # page_query_param = 'page'
    # 一页中最多的结果条数
    max_page_size = 2
    • user/views.py 视图函数中使用

    class UserViewSet(viewsets.ModelViewSet):
    # 3.分页:自定义分页器 覆盖全局配置
    pagination_class = PageNum

    3.6 过滤和排序

    • 测试url


    #1.过滤:查询 用户名(tom),激活的用户
    http://192.168.56.100:8888/user/user/?username=tom&phone=&is_active=true
    #2.排序:查询所有用户 用id 反向排序
    http://192.168.56.100:8888/user/user/?ordering=-id
    • user/views.py 视图函数中配置过滤和排序字段


    class UserViewSet(viewsets.ModelViewSet):
    # 5.过滤:指定过滤方法类, 排序方法类, 一个或多个
    filter_backends = (DjangoFilterBackend, OrderingFilter) # 同时支持过滤和排序
    # 5.1指定排序字段, 不设置, 排序功能不起效
    ordering_fields = ('date_joined', 'id') # ?ordering=-id
    # 5.2指定过滤字段, 不设置, 过滤功能不起效
    filter_fields = ('username', 'phone', 'is_active') # ?
    username=tom&phone=&is_active=true
  • 相关阅读:
    Android(java)学习笔记68:使用proguard混淆android代码
    SGU 194 Reactor Cooling
    关于流量有上下界的网络流问题的求解
    关于最小割的求解方法
    HDU 5311 Hidden String
    POJ 3548 Restoring the digits
    POJ 2062 HDU 1528 ZOJ 2223 Card Game Cheater
    ZOJ 1967 POJ 2570 Fiber Network
    HDU 1969 Pie
    HDU 1956 POJ 1637 Sightseeing tour
  • 原文地址:https://www.cnblogs.com/Aurora-y/p/13886552.html
Copyright © 2011-2022 走看看