zoukankan      html  css  js  c++  java
  • Django RESRframework奇淫技巧

    Django RESRframework

    Mixins, ViewSet和router配合使用

    Mixins的类共有五种

    • CreateModelMixin
    • ListModelMixin
    • RetrieveModelMixin
    • UpdateModelMixin
    • DestroyModelMixin

    他们分别对应了数据库的增、查、改、删的相应操作,使用它们的好处就是不需要再去写重复的相同的代码逻辑了,因为每个mixins内部都写好了对应的逻辑,只需要设置一下querysetserializer_class就可以了

    ViewSet也是有五种,分别是

    • ViewSetMixin
    • ViewSet
    • GenericViewSet
    • ReadOnlyModelViewSet
    • ModelViewSet

    一般的话来说只需要使用GenericViewSet就够了,它继承了ViewSetMixingenerics.GenericAPIView,后者的功能大家都知道,有了它才能设置querysetserializer_class属性,重点是ViewSetMixin

    ViewSetMixin

    它重写了as_view方法,这个能让我们注册url变得非常的简单,还有一个使用的方法就是initialize_request,这个方法主要是给action属性赋值,这个属性在设置动态serializerpermission的时候有巨大的好处!

    So ~ 写上一个APIView的代码如下

    from rest_framework import mixins
    from rest_framework import viewsets
    
    class XXXViewSet(mixins.ListModelMixin, mixins.CreateModelMixin, viewsets.GenericViewSet):
    
        queryset = Model.objects.all()
        serializer_class = ModelSerializer
    

    接下来就是配置url

    from appname.views import XXXViewSet
    
    models = XXXViewSet.asview({
        'get': 'list',
        'post': 'create'
    })
    
    urlpattrtns = [
        url(r'apiAddress/$',models, name="models"),
    

    这样就可以把set请求绑定到list的方法之上了,post请求就绑定到了creat方法,不需要再去重写它们了,其实上面配置url的方法还可以更加简便,于是router就登场啦~

    from rest_framework.routers import DefaultRouter
    from appname.views import XXXViewSet
    
    router = DefaultRouter()
    router.register(r'apiAddress', XXXViewSet, base_name='apiAddress')
    
    urlpattrtns = [
        # 这个已经不需要了
        # url(r'apiAddress/$',models, name="models"),
        url(r'^', include(router.urls)),
    ]
    

    以后再添加url的时候只需要在router里面注册就行了,urlpattrtns列表不需要做任何改动.这样就完成了一个RESTful API的创建,能够合理搭配mixins,ViewSetrouters三者的话,就可以超快速地开发大量的RESTful API!

    使用Django RESTframework的过滤功能

    一个简单的过滤功能,例如查询用户列表,只返回粉丝数大于100的:

    class XXXViewSet(mixins.ListModelMixin,mixins.CreateModelMixin,viewsets.GenericViewSet):
        
        serializer_class = ModelSerializer
        def get_query(self):
            fans_nums = self.request.quer_params.get("fans_min",0)
            if fans_nums:
                return User.object.filter(fans_num__gt=int(fans_min))
            return User.objects.all()
    

    上面的这种方法如果需要写过多的过滤字段的话就需要写一大堆重复冗余的代码,于是乎就需要使用到django-filter来完成

    首先当然是要安装pip install django-filter, 然后将django-filter加到INSTALLED_APPS中去

    代码如下:

    from rest_framework import mixins
    from rest_framework import viewsets
    from django_filters.rest_framework import DjangoFilterBackend
    from django.contrib.auth import get_user_model
    
    User = get_user_model()
    
    class UserViewSet(mixins.ListModelMixin, mixins.CreateModelMixin, viewsets.GenericViewSet):
    
        queryset = User.objects.all()
        serializer_class = ModelSerializer
        filter_backends = (DjangoFilterBackend,)
        # 设置过滤字段,这里设置过滤用户的名字和粉丝数
        filter_fields = ('name', 'fans_num')
    

    然后用浏览器打开127.0.0.1:8000就可以看到相应的路由视图了,(这种需要使用的路由注册的方法)

    undefined

    这样你就会在过滤的界面里看到filter_fields = ('name', 'fans_num')里面设置的过滤条件的字段了

    新建filter.py

    import django_filters
    from django.contrib.auth import get_user_model
    
    User = get_user_model()
    
    class UserFilter(django_filters.rest_framework.FilterSet):
    
        min_fans_num = django_filter.NumberFilter(name='fans_num', lookup_expr='gte')
        max_fans_num = django_filters.NumberFilter(name='fans_num', lookup_expr='lt')
        name = django_filters.CharFilter(name='name',lookup_expr='icontains')
        
        class Meta:
            model = User
            fields = ['name', 'min_fans_num', 'max_fans_num']
    
    

    然后之前的代码改为

    from .filter import UserFilter
    
    class UserViewSet(mixins.ListModelMixin, mixins.CreateModelMixin, viewsets.GenericViewSet):
    
        queryset = User.objects.all()
        serializer_class = ModelSerializer
        filter_backends = (DjangoFilterBackend,)
        filter_class = UserFilter
    
    

    过滤用户名的话其实用SearchFilter也可以实现,用两行代码就够了

    filter_backends = (DjangoFilterBackend, SearchFilter)
    serch_fields = ("name",)
    

    还有一些更强大的配置

    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.
    
    For example:
    
    search_fields = ('=username', '=email')
    
    

    Django分页官方文档

    还有一个排序的filter,例如我们想按照用户的粉丝数量进行排序(升序和降序):

    自定义分页

    from rest_framework.pagination import PageNumberPagination
    
    class UsersPagination(PageNumberPagination):
        # 指定每一页的个数
        page_size = 10
        # 可以让前端来设置page_szie参数来指定每页个数
        page_size_query_param = 'page_size'
        # 设置页码的参数
        page_query_param = 'page'
        
    
    class UserViewSet(mixins.ListModelMixin, mixins.CreateModelMixin, viewsets.GenericViewSet):
        # 设置分页的class
        pagination_class = UsersPagination
    

    就这样几行代码就搞定了,而且在返回的json中加了总数,下一页的链接和上一页的链接.

    回顾我们之前的代码,在UserViewSet这个类里面,才写了7行代码,就已经完成了

    • 获取用户列表
    • create一个用户
    • 分页
    • 搜索
    • 顾虑
    • 排序
      这些功能,如果想要获取指定用户的具体信息,直接继承mixins.RetrieveModelMixin就直接做好了...''

    权限认证

    比如有一些API功能,是需要用户登录才能使用可以的
    或者比如我要删除我这篇博客,也要验证我是作者才能删除

    验证用户是否登录

    from rest_framework.permissions import IsAuthenticated
    
    class XXXViewSet(mixins.CreateModelMixin, mixins.DestroyModelMixin):
        
        permission_classes = (IsAuthenticated,)
        
    

    验证操作时本人需要permission

    permission.py

    from rest_framework import permissions
    
    class IsOwnerOrReadOnly(permissions.BasePermission):
    
        def has_object_permission(self, request, view, object):
            if request.method in permissions.SAFE_METHODS:
                return True
    
            return object.user == request.user
    

    permission_classes = (IsAuthenticated, IsOwnerOrReadOnly)

    使用JWT的用户认证模式

    1. 安装pip install djangorestframework-jwt
    2. url.py中配置
    from rest_framework_jwt.views import obtain_jwt_token
    urlpatterns = [
     ...
     url(r'^api-token-auth/', obtain_jwt_token),
     ...
    ]
    
    1. 在需要的jwt认证的ViewSet的类里面设置
    from rest_framework_jwt.authentication import JSONWebTokenAuthentication
    from rest_framework.authentication import SessionAuthentication
    
    class XXXViewSet(mixins.CreateModelMixin, viewsets.GenericViewSet):
      authentication_classes = (JSONWebTokenAuthentication, SessionAuthentication)
    

    加上SessionAuthentication是为了在网站上调试方便

    现在注册登录有两种方式:

    1. 用户注册之后跳转到登录页面让其登录
    2. 用户注册之后自动帮他登录了
      第一种情况的话我们无需再做其他操作,第二种情况我们应该在用户注册之后返回jwttoken的字段给前台,所以要做两步:

    因为返回字段是mixins帮我们做好了,所以我们要重写对应的方法来修改返回字段
    需要查看djangorestframework-jwt的源码找到生成jwt token的方法

    from rest_framework_jwt.serializers import jwt_encode_handler, jwt_payload_handler
    
    class UserViewSet(CreateModelMixin, RetrieveModelMixin,UpdateModelMixin,viewsets.GenericViewSet):
        # 重写create方法
        def create(self, request, *args, **kwargs):
            serializer = self.get_serializer(data=request.data)
            serializer.is_valid(raise_exception=True)
            user = self.perform_create(serializer)
            
            # 在新建用户保存到数据库之后
            tmp_dict = serializer.data
            # 生成JWT Token
            payload = jwt_payload_handler(user)
            tmp_dict['token'] = jwt_encode_handler(payload)
    
            headers = self.get_success_headers(serializer.data)
            return Response(tmp_dict, status=status.HTTP_201_CREATED, headers=headers)
    

    更多jwt的相关操作可以查看文档

    动态serializers
    这个使用之前说过的action属性就可以很方便的实现

    class UserViewSet(CreateModelMixin, RetrieveModelMixin,UpdateModelMixin,viewsets.GenericViewSet):
        
        # 这个就不需要了
        #serializer_class = XXXSerializer
    
        def get_serializer_class(self):
            if self.action == 'create':
                return XXXSerializer
            elif self.action == 'list':
                return XXXSerializer
            return XXXSerializer
    

    一些实用的Serializer fields
    比如说我要发布这篇文章,需要上传我(用户)的id才能和这篇文章建立关联,但我们这个可以不用前台来上传

    serializer.py

    class XXXSerializer(serializers.ModelSerializer):
        # user默认是当前登录的user
        user = serializers.HiddenField(
            default = serializers.CurrentUserDefault()
        )
    

    还有如果返回的字段逻辑比较复杂,可以用serializer.SerializerMethodField()来完成,例如:

    class XXXSerializer(serializers.ModelSerializer):
        xxx = serializer.SerializerMethodField()
        
        # 把逻辑写在get_的前缀加xxx(字段名),然后返回
        def get_xxx(self, obj):
            # 完成你的业务逻辑
            return 
    

    自定义用户认证
    Django自带的登录是通过usernamepassword来做登录的,但是现在很多网站或者app用手机号来来当做账号,这个时候就需要自定义用户认证:

    from django.contrib.auth.backends import ModelBackend
    
    class CustomBackend(ModelBackend):
        def authenticate(self, request, username=None, password=None, **kwargs):
            try:
                user = User.objects.get(Q(username=username)|Q(mobile=username))
                # 验证密码是否正确
                if user.check_password():
                    return user
            except Exception as e:
                return None
    

    用户注册的时候,如果你在后台查看的是明文,这是因为ModelSerializer在保存的时候直接明文保存了, 解决问题:

    serializer.py

    class UserRegSerializer(serializers.ModelSerializer):
        
        #重写create方法
        def create():
            user = super(UserRegSerializer,self).create(validated_data=validated_data)
            user.set_password(validated_data["password"])
            user.save()
            return user
    

    或者也可以用django的信号量也可以解决

    from django.db.models.signals import post_save
    from django.dispatch import receiver
    from django.contrib.auth import get_user_model
    User = get_user_model()
    
    @receiver(post_save, sender=User)
    def create_user(sender, instance=None, created=False, **kwargs):
        if created:
            password = instance.password
            instance.set_password(password)
            instance.save()
    

    然后还要app.py里面配置

    from django.apps import AppConfig
    
    
    class UsersConfig(AppConfig):
        name = 'users'
        
        def ready(self):
            import users.signals  
    
  • 相关阅读:
    好友面板切换案例
    jquery
    H5 新增API
    深拷贝
    浅拷贝
    call bind apply
    像素鸟
    Django3.0
    三剑客-grep-awk-sed
    Linux中find命令详解
  • 原文地址:https://www.cnblogs.com/Dr-wei/p/11686264.html
Copyright © 2011-2022 走看看