美文网首页
drf相关技术要点

drf相关技术要点

作者: tomtiddler | 来源:发表于2018-09-04 00:40 被阅读0次

    1.基础view为apiview
    2.modelserializer类比modelform
    3.serializer内部嵌套覆盖
    class GoodListView(mixins.ListModelMixin, generics.GenericAPIView):
    class GoodsListView(generics.ListAPIView):
    setting中配置:

    REST_FRAMEWORK = {
        'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.PageNumberPagination',
        'PAGE_SIZE': 10,
    }
    
    # 取消setting中配置
    class StandardResultsSetPagination(PageNumberPagination):
        page_size = 20
        page_size_query_param = 'page_size'
        page_query_param = 'p'
        max_page_size = 100
    view中加入
    pagination_class = StandardResultsSetPagination  # 此参数及功能在GenericApiView中
    
    

    viewset

    1.重写了 as_view方法,使注册url更加简单。
    2.动态设置serializer时的action

    绑定方法1
    goods_list = GoodsListViewset.as_view({
        'get': 'list',
    })
    方法二(后期用法)
    router.register('goods', GoodsListViewset)
    path('', include(router.urls)),  # 此处为空字符串,切记
    
    view之间的继承关系,最后一个view属于django
    GenericViewSet
        GenericAPIView  配合mixin组合成了很多功能类
            APIView
                View
    mixins和genericviewset增加view功能
    

    drf的request和response

    过滤

    类中必须重载queryset,如果queryset为None就会报错
    django自带的过滤
        queryset = Goods.objects.all()
        serializer_class = GoodsSerializer
        pagination_class = StandardResultsSetPagination  # 此参数及功能在GenericApiView中
    
        def get_queryset(self):
            price_min = self.request.query_params.get('price_min', 0)
            if price_min:
                self.queryset = self.queryset.filter(shop_price__gt=price_min)
            return self.queryset
    

    drf过滤

    django_filter
    install_app
    rest_framework配置 或者对特定的view设置

    drf的过滤器简单使用
    from django_filters.rest_framework import DjangoFilterBackend
    
        filter_backends = (DjangoFilterBackend,)
        filter_fields = ('name', 'shop_price')
    
    较高级使用(新建filters文件)
    class GoodsFilter(filters.FilterSet):
        min_price = filters.NumberFilter(field_name="shop_price", lookup_expr='gte')
        max_price = filters.NumberFilter(field_name="shop_price", lookup_expr='lte')
    
        class Meta:
            model = Goods
            fields = ['name', 'min_price', 'max_price']
    
    view中:
        filter_class = GoodsFilter
    

    搜索和排序

    在上文新建的filters中简单模糊搜索:关键词lookup应该跟数据库查询关键字相关,不确定
        name = filters.CharFilter(field_name="name", lookup_expr='icontains')
    
    drf自带的search_filter: 可以采用类似正则表达式的方法(排序类似)
        from rest_framework import filters
    
        filter_backends = (DjangoFilterBackend, filters.SearchFilter)
        search_fields = ('name', 'goods_brief', 'goods_desc')
    
    The search behavior may be restricted by prepending various characters to the search_fields.
    
    '^' Starts-with search.
    '=' Exact matches.
    '@' Full-text search. (Currently only supported Django's MySQL backend.)
    '$' Regex search.
    

    category层级结构序列化

    class CategorySerializer3(serializers.ModelSerializer):
        class Meta:
            model = GoodsCategory
            fields = '__all__'
    
    
    class CategorySerializer2(serializers.ModelSerializer):
        sub_cat = CategorySerializer3(many=True)
    
        class Meta:
            model = GoodsCategory
            fields = '__all__'
    
    
    class CategorySerializer(serializers.ModelSerializer):
        sub_cat = CategorySerializer2(many=True)
    
        class Meta:
            model = GoodsCategory
            fields = '__all__'
    

    filter中获取指定top_category的所属商品

        top_category = filters.NumberFilter(method='top_category_filter')
    
        def top_category_filter(self, queryset, name, value):
            return queryset.filter(Q(category_id=value) | 
                                   Q(category__parent_category_id=value) | 
                                   Q(category__parent_category__parent_category_id=value))
    

    用户登录和手机注册

    drf的token登录和原理

    前后端不分离的带有csrf验证,如果采用前后端分离不方便带csrf_code方法
    install_app中: 'rest_framework.authtoken' # token
    makemageration->magerate->数据表迁移
    url配置

    from rest_framework.authtoken import views
        path('api-token-auth', views.obtain_auth_token),
    

    chorme的servistate插件:用于测试url的返回数据等功能
    此时POST http://127.0.0.1:8000/api-token-auth (用户数据)->就能返回token

    采用token验证

    django默认采用session登录机制?
    写入类似Authorization: Token 9944b09199c62bcf9418ad846dd0e4bbdfc6ee4b的http header。
    setting中的REST_FRAMEWORK加入tokenauth拦截器。(此方法用于测试,全局的,测试完要删除。)
    实际开发中将token拦截写入类中,防止公共页面的权限不够。
    middleware拦截机制,重载process_request&process_response
    drf token的问题, 存储在服务器,分布式服务器要多处存储,没设置过期时间。
    viewset配置认证类

    from rest_framework.authentication import TokenAuthentication
        authentication_classes = (TokenAuthentication, )  # 类内配置用户
    

    JSON WEB TOKEN (一个规范)

    好处:采用加密解密算法在服务器端运行验证,无需再存入数据库,增加速度,减少验证时的服务器负载。
    jwt介绍

    使用jwt进行认证

    pip install djangorestframework-jwt
    setting中rest_framework配置:(实际开发时在类中加入,不在全局加入) 'rest_framework_jwt.authentication.JSONWebTokenAuthentication',

    from rest_framework_jwt.views import obtain_jwt_token
        # jwt的认证接口
        path('jwt-auth', obtain_jwt_token),
    

    drf-jwt官方文档,很重要。

    genericviewset中重写了as_view,从而实现router注册功能(猜测,学过,忘了)

    class SmsSerializer(serializers.Serializer):
        mobile = serializers.CharField(max_length=11)
    
        def validate_mobile(self, mobile):  # 函数的名称必须是validate+字段名
    

    非常重要:def validate_mobile(self, mobile): # 函数的名称必须是validate+字段名,在serializer类下该函数是自运行函数

    drf官方文档中关于serializer的fields中的core arguments
    其中有个参数是write_only: code设置这个参数,防止返回时序列化报错->返回时该属性已经被删除。

    class UserRegisterSerializer(serializers.ModelSerializer):
        code = serializers.CharField(write_only=True)
    -------------------------------------------------------------------------------------------
        def create(self, request, *args, **kwargs):
            serializer = self.get_serializer(data=request.data)
            serializer.is_valid(raise_exception=True)
            self.perform_create(serializer)
            headers = self.get_success_headers(serializer.data)
            return Response(serializer.data, status=status.HTTP_201_CREATED, headers=headers)
    
    用户密码设置方法1(serializer中)
        def create(self, validated_data):
            user = super(UserRegisterSerializer, self).create(validated_data)
            user.set_password(validated_data["password"])
            user.save()
            return user
    
    **新写信号量文件的方式 -> appsconfig中重载ready函数**
    from django.conf import settings
    from django.db.models.signals import post_save
    from django.dispatch import receiver
    from rest_framework.authtoken.models import Token
    from django.contrib.auth import get_user_model
    
    User = get_user_model()
    @receiver(post_save, sender=User)
    def create_auth_token(sender, instance=None, created=False, **kwargs):
        if created:
            password = instance.password
            instance.set_password(password)
            instance.save()
            # Token.objects.create(user=instance) 采用了jwt,此种方式删除
    以下函数必须重载,否则返回的对象为空,导致bug
    def perform_create(self, serializer):  # 此处重载将user返回
        return serializer.save()
    

    用户收藏

    serializer绑定当前用户

      user = serializers.HiddenField(
            default=serializers.CurrentUserDefault()
        )
    

    model 构建联合唯一索引防止重复收藏

    # 构建联合唯一索引防止重复收藏
        class Meta:
            verbose_name = '用户收藏'
            verbose_name_plural = verbose_name
            unique_together = ("user", "goods")
    
    此时重复收藏就会报错 (browser  api_root)
    {
        "non_field_errors": [
            "字段 user, goods 必须能构成唯一集合。"
        ]
    }
    

    另一种方法写入serializer中的meta =>作用于所有字段

        class meta:
            validators = [
                UniqueTogetherValidator(
                    queryset=UserFav.objects.all(),
                    fields=('user', 'goods'),
                    message="已经收藏"
                )]
    

    permission

    用户认证和权限
    配置认证和权限是两回事,不可混为一谈。auth和permission。
    配置对象即验证,最好不要全局setting认证,防止token过期导致公共页面无法访问

    drf权限认证,自定义对象级权限。配置认证方式,根据代码判断=>如果配置了对象级认证,那么访问该对象时将可能有双重认证。

    个人中心

    drf文档功能

    1.read: 和retrieve: 都可以对单个get进行文档注释
    2.help_text加在serializer上和model上和filter上都可以

    动态设置serializer和permission

    在userviewset当中重载函数,该函数需要查看源码了解功能

        def get_permissions(self):
            if self.action == "retrieve":
                return [permissions.IsAuthenticated()]
            elif self.action == "create":
                return []
    
            return []
    
        def get_serializer_class(self):
            if self.action == "retrieve":
                return UserDetailSerializer
            elif self.action == "create":
                return UserRegisterSerializer
    
            return UserDetailSerializer
    

    还有一个问题,本用户只能获取自身的信息

        # 重写该方法,不管传什么id,都只返回当前用户
        def get_object(self):
            return self.request.user
    

    前端修改生日弹出提示框,其他没有。可以再看下。
    **此字段非常重要=>lookup_field = "goods_id" **
    单项查找时的搜索字段,默认为pk,可能表示model_id。查询是在query_set之后,已经经过了过滤。

    购物车

    加入购物车

    此处不使用modelserializer,因为该类会验证unique_together字段,然而更新时该字段是存在的,验证会报错。暂时没理解。最好看一遍源码。

    *******************************model中*******************************
            unique_together = ("user", "goods")
    *******************************serializer中*******************************
    class ShopCartSerializer(serializers.Serializer):
        user = serializers.HiddenField(
            default=serializers.CurrentUserDefault()
        )
        nums = serializers.IntegerField(required=True, min_value=1, label="数量",
                                        error_messages={
                                            "min_value": "商品数量不能小于1",
                                            "required": "请填写购买数量",
                                        })
        goods = serializers.PrimaryKeyRelatedField(queryset=Goods.objects.all(), required=True, label="商品")
    
        def create(self, validated_data):
            user = self.context["request"].user
            nums = validated_data["nums"]
            goods = validated_data["goods"]
    
            existed = ShoppingCart.objects.filter(user=user, goods=goods)
    
            if existed:
                existed = existed[0]
                existed.nums += nums
                existed.save()
            else:
                existed = ShoppingCart.objects.create(**validated_data)
    
            return existed
    
    

    相关文章

      网友评论

          本文标题:drf相关技术要点

          本文链接:https://www.haomeiwen.com/subject/orrbwftx.html