DRF总结(二)View中各个配置的使用

DRF总结(二)View中各个配置的使用

      • 常用的属性,以以下代码为例
        • 查询结果集
        • 分页
        • 过滤
        • 搜索
            • 使用Drf进行search_filter
        • 排序
            • 使用DRF进行ordering_filter
        • 配置serializer类
        • 权限验证
        • 在views中获取原始数据和serializer验证后的数据
        • View中可以重写的方法
          • create方法(CreateMinix)
          • perform_create(CreateMinix)
          • lookup_field = "goods_id"
          • get_object(Update和Retrieve和Destroy)
          • get_serializer_class 动态设置serializer_class
          • get_permissions 动态设置permissions
          • get_queryset 根据需求返回指定条件的查询结果集
          • perform_destroy(DestroyMixin)执行删除操作
          • perform_update(Update_Mixin) 执行更新操作
          • retrieve(RetrieveMixin)

常用的属性,以以下代码为例

class GoodsListViewSet(CacheResponseMixin,mixins.ListModelMixin,mixins.RetrieveModelMixin,viewsets.GenericViewSet):
    '''
    商品列表页
    '''
    queryset = Goods.objects.all()
    serializer_class = GoodsSerializer
    pagination_class = GoodsPagination
    
    # authentication_classes = (TokenAuthentication,)

    # def get_queryset(self):
    #     queryset = Goods.objects.all() # 只有for时才会执行
    #     price_min = self.request.query_params.get("price_min",0)
    #     if price_min:
    #         queryset = queryset.filter(shop_price__gt=int(price_min))
    #     return queryset

    # 精确过滤:根据字段过滤
    filter_backends = (DjangoFilterBackend,filters.SearchFilter,filters.OrderingFilter)
    # filter_fields = ('name', 'shop_price')

    # 搜索功能 ^表示name必须匹配开头
    search_fields = ('name', 'good_brief', 'good_desc')
    # 排序
    ordering_fields = ('shop_price','sold_num')
    # 条件筛选
    filter_class = GoodsFilter

    throttle_classes = (UserRateThrottle, AnonRateThrottle)

    """
    Retrieve a model instance.
    """
    def retrieve(self, request, *args, **kwargs):
        instance = self.get_object()
        instance.click_num += 1
        instance.save()
        serializer = self.get_serializer(instance)
        return Response(serializer.data)

查询结果集

  • queryset

查询结果集

  • def get_queryset(self)

获取查询结果集,如果重写了此方法,则默认忽略queryset

分页

  • 默认分页,settings中设置
REST_FRAMEWORK = {
    'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.LimitOffsetPagination',
    'PAGE_SIZE': 10,
}
  • pagination_class 设置自定义的分页类
class GoodsPagination(PageNumberPagination):
    page_size = 12  # 默认每页显示的数据条数
    page_size_query_param = 'page_size' # 获取url参数中设置的每页显示数据条数
    page_query_param = "page"  # 获取url中传入的页码key
    max_page_size = 60  # 最大支持的每页显示的数据条数
  • 更多

https://www.cnblogs.com/haiyan123/p/8433233.html

过滤

  • 简单使用(重写get_queryset方法)
class GoodsListViewSet(mixins.ListModelMixin,viewsets.GenericViewSet):
    '''
    商品列表页
    '''
    queryset = Goods.objects.all()
    serializer_class = GoodsSerializer
    pagination_class = GoodsPagination

    def get_queryset(self):
        queryset = Goods.objects.all() # 只有for时才会执行
        price_min = self.request.query_params.get("price_min",0)
        if price_min:
            queryset = queryset.filter(shop_price__gt=int(price_min))
        return queryset
  • 进阶操作:使用Django-filter

1.精确过滤(根据字段完全匹配)

  • views
class GoodsListViewSet(mixins.ListModelMixin,viewsets.GenericViewSet):
    '''
    商品列表页
    '''
    queryset = Goods.objects.all()
    serializer_class = GoodsSerializer
    pagination_class = GoodsPagination

    # 精确过滤:根据字段过滤
    filter_backends = (DjangoFilterBackend,)
    filter_fields = ('name', 'shop_price')

2.添加筛选条件(模糊查询,范围查询…)

  • 自定义filter类
class GoodsFilter(filters.FilterSet):
    '''
    商品的过滤类
    '''
    # field_name要配置的字段  lookup_expr 要满足的条件
    min_price = filters.NumberFilter(field_name="shop_price", lookup_expr='gte')
    max_price = filters.NumberFilter(field_name="shop_price", lookup_expr='lte')
    # contains 模糊查询(like) i忽略大小写
    name = filters.CharFilter(field_name="name", lookup_expr='icontains')

    class Meta:
        model = Goods
        fields = ['min_price', 'max_price', 'name']
  • 在filter类中的自定义方法进行复杂逻辑的过滤
  top_category = filters.NumberFilter(method='top_category_filter')

    def top_category_filter(self,queryset,name,value):
        # category是外键 category_id找到对应的外键表的id(其实category_id是在数据库中保存的外键名)
        # category_id = 3 就是找类别为3的所有商品

        # parent_category_id是外键表自关联的外键,
        # category__parent_category_id=2就是找parent_category_id=2的所有category,
        # 然后再找属于这些category下面的所有goods
        # 或者说goods外键表中的parent_category_id=2等于二的goods

        # category__parent_category__parent_category_id=1
        # 就是找 goods所关联的category的所关联的parent_category中的parent_category_id为1的所有goods
        # goods对应的category是三级,可以通过这个category自身的parent_category_id找到所有的二级所对应的id
        # 或者通过parent_category找到所有的二级对象,而二级对象又可以通过parent_category_id找到所有的三级的id
        # 双下划线表示引出这个对象下所对应的某个值
        return queryset.filter(Q(category_id=value)|Q(category__parent_category_id=value)
                               |Q(category__parent_category__parent_category_id=value))

配置到filter_class

    # 精确过滤:根据字段过滤
    filter_backends = (DjangoFilterBackend,)
    # filter_fields = ('name', 'shop_price')

    # 条件筛选
    filter_class = GoodsFilter

搜索

使用Drf进行search_filter
  • 只需要配置search_filter
    # 搜索功能 ^表示name必须匹配开头
    search_fields = ('^name', 'good_brief', 'good_desc')

排序

使用DRF进行ordering_filter
    # 精确过滤:根据字段过滤
    filter_backends = (DjangoFilterBackend,filters.SearchFilter,filters.OrderingFilter)
    # 排序
    ordering_fields = ('market_price','sold_num')

配置serializer类

serializer_class = GoodsSerializer

权限验证

  • 使用JWT和Session进行验证本次登录的合法性

这个类的设置主要是为了使用token或者session验证方式获取用户信息,只有permission才会做真正的拦截处理
views

    # 设置JSONWebTokenAuthentication为局部,让用户访问Goods列表时不做登录限制
    # 设置SessionAuthentication,让已登录用户访问收藏列表时不会提示用户认证未提供,方便开发时的调试(因为调试时,往往不会讲token放到header中),
    # 这个类的设置主要是为了使用token或者session验证方式获取用户信息,然后交给permission验证
    authentication_classes = (JSONWebTokenAuthentication, SessionAuthentication)
  • permission 对该用户进行权限控制

栗子:让用户只能对自己的收藏列表进行操作

    # IsAuthenticated 判断是否登录,
    # 只有已经登录的用户才能获取到个人信息
    # 未登录,返回 401 "detail": "Authentication credentials were not provided."
    # IsOwnerOrReadOnly 让用户只能对自己的收藏列表进行操作
    # 如果没有权限,则抛出404,找不到
    permission_classes = (IsAuthenticated,IsOwnerOrReadOnly)

自己定义的permission类

permissions.py

from rest_framework import permissions

class IsOwnerOrReadOnly(permissions.BasePermission):
    """
    Object-level permission to only allow owners of an object to edit it.
    Assumes the model instance has an `owner` attribute.
    """
    
    def has_object_permission(self, request, view, obj):
        # Read permissions are allowed to any request,
        # so we'll always allow GET, HEAD or OPTIONS requests.
        if request.method in permissions.SAFE_METHODS:
            return True

        # Instance must have an attribute named `owner`.
        # obj就是数据库中查出来的那个对象
        return obj.user == request.user

在views中获取原始数据和serializer验证后的数据

  • 获取原始数据,直接从request中获取
    def create(self, request, *args, **kwargs):
        serializer = self.get_serializer(data=request.data)

request也可以直接通过self点出来

  • 获取验证后的数据 serializer.save()
    def perform_create(self, serializer):
        return serializer.save()

View中可以重写的方法

create方法(CreateMinix)

一般要保存对象需要做额外操作时重写

def create(self, request, *args, **kwargs):
        serializer = self.get_serializer(data=request.data)
        serializer.is_valid(raise_exception=True)
        user = self.perform_create(serializer)

        # 这一步会序列化对象,所有fields中的字段都会被序列号,而code已经被删除了
        # ,所以code中加入了write_only=True,让他不要被序列化和返回前端
        # register的用户对象
        re_dict = serializer.data

        # 将token返回给前端
        payload = jwt_payload_handler(user)
        re_dict["token"] = jwt_encode_handler(payload)
        re_dict["name"] = user.name if user.name else user.username

        headers = self.get_success_headers(serializer.data)
        return Response(re_dict, status=status.HTTP_201_CREATED, headers=headers)
perform_create(CreateMinix)

执行保存对象的操作,被create地调用,一般和信号量配合使用

    # 重写次方法,返回user
    def perform_create(self, serializer):
        # 这里需要操作数据库,而密码需要加密,所以用到了信号量
        # 在每次post保存的时候,将密码加密
        return serializer.save()
lookup_field = “goods_id”

设置retrieve的查询字段是goods_id,而不是表中的默认主键id

lookup_field = “goods_id” # 因为goods是外键,所有加下划线

lookup_field是在get_queryset进行筛选之后执行的,因此不用担心查到其他不属于当前用户的good

查询或删除的url

http://127.0.0.1:8000/userfavs/16/ 16为goods_id

get_object(Update和Retrieve和Destroy)

获取根据filter_kwargs再次queryset中查找匹配的一个QuerySet实例,查不到返回404,查到多个抛出异常

源码

        filter_kwargs = {self.lookup_field: self.kwargs[lookup_url_kwarg]}
        obj = get_object_or_404(queryset, **filter_kwargs)
    try:
        return queryset.get(*args, **kwargs)
    except queryset.model.DoesNotExist:
        raise Http404('No %s matches the given query.' % queryset.model._meta.object_name)

此处重写该方法,是为了直接返回后端定位到的当前的user,而不用前端去手动的传值查询

    # 当Update和Retrieve和Destroy的时候都会用到该方法
    def get_object(self):
        return self.request.user
get_serializer_class 动态设置serializer_class

栗子:本处,添加收藏时,序列化当前用户
获取收藏时,返回goods的序列化结果

    def get_serializer_class(self):
        if self.action == "list":
            return UserFavDetailSerializer
        elif self.action == "create":
            return UserFavSerializer

        return UserFavSerializer
get_permissions 动态设置permissions

栗子:本处,创建用户时,不要求验证是否已经登录,其他操作,如更新,获取个人信息都需要验证

    def get_permissions(self):
        if self.action == "retrieve":
            return [permissions.IsAuthenticated()]
        elif self.action == "create":
            return []
        return []
get_queryset 根据需求返回指定条件的查询结果集
  # 重写get_queryset,只返回当前用户的收藏信息
    def get_queryset(self):
        return UserFav.objects.filter(user=self.request.user)
perform_destroy(DestroyMixin)执行删除操作
  def perform_destroy(self, instance):
        goods = instance.goods
        goods.goods_num += instance.nums
        goods.save()
        instance.delete()
perform_update(Update_Mixin) 执行更新操作
    def perform_update(self, serializer):
        # 修改商品的库存,通过比对购物车更新前后该商品的数量
        # newstorage = oldstorage + (after-before)
        existed_record = ShoppingCart.objects.get(id=serializer.instance.id)
        existed_nums = existed_record.nums
        saved_record = serializer.save()
        nums = saved_record.nums-existed_nums
        goods = saved_record.goods
        goods.goods_num -= nums
        goods.save()
retrieve(RetrieveMixin)
  • 栗子:每当前端使用get/:id访问Retrieve时,增加该商品的点击数量,get_object是拿到model单个纪录的实例化对象
    def retrieve(self, request, *args, **kwargs):
        instance = self.get_object()
        instance.click_num += 1
        instance.save()
        serializer = self.get_serializer(instance)
        return Response(serializer.data)

你可能感兴趣的:(Django,Rest,Framework)