drf相关技术要点

2018-09-04  本文已影响0人  tomtiddler

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

上一篇下一篇

猜你喜欢

热点阅读