开始之前,假设你已经有Django和Django REST framework的一些基础了
mixins,ViewSet和routers配合使用 minxis
的类有5种
CreateModelMixin
ListModelMixin
RetrieveModelMixin
UpdateModelMixin
DestroyModelMixin
他们分别对应了对数据库的增查改删操作,使用它们的好处是不用重复写着相同的业务代码逻辑,因为每个mixins
内部都写好了对应的逻辑,只需要设置一下queryset
和serializer_class
就可以了.
ViewSet
也有5种,分别是
ViewSetMixin
ViewSet
GenericViewSet
ReadOnlyModelViewSet
ModelViewSet
一般来说我们只需要用```GenericViewSet```就可以了.它继承了```ViewSetMixin```和```generics.GenericAPIView```,后者的功能大家都知道,有了它才能设置```queryset```和```serializer_class```属性.重点是```ViewSetMixin```.
它重写了方法as_view
,这个能让我们注册url变得更加简单,还有一个方法是initialize_request
,这个方法主要是给action
属性赋值,这个属性在设置动态serializer
和permission
的时候有很大的用处!之后会写到
所以写一个APIView
就变得很简单,如下:
1 2 3 4 5 6 7 8 from rest_framework import mixinsfrom rest_framework import viewsetsclass XXXViewSet (mixins.ListModelMixin, mixins.CreateModelMixin, viewsets.GenericViewSet ): queryset = Model.objects.all () serializer_class = ModelSerializer
接下来就是配置url了
1 2 3 4 5 6 7 8 9 10 11 from appname.views import XXXViewSetmodels = XXXViewSet.asview({ 'get' : 'list' , 'post' : 'create' }) urlpattrtns = [ url(r'apiAddress/$' ,models, name="models" ), ]
这样就可以把get
请求绑定到list
的方法上,post
请求就绑定到了create
方法.不需要再去重写它们了
其实上面配置url的方法还是过于繁琐,这时候就是router
登场了,上面的代码改为:
1 2 3 4 5 6 7 8 9 10 11 12 from rest_framework.routers import DefaultRouterfrom appname.views import XXXViewSetrouter = DefaultRouter() router.register(r'apiAddress' , XXXViewSet, base_name='apiAddress' ) urlpattrtns = [ url(r'^' , include(router.urls)), ]
以后再添加url的时候只需要在router
里面注册就行了,urlpattrtns
列表不需要做任何改动. 这样就完成了一个RESTful API
的创建, 能够合理搭配mixins
,ViewSet
和routers
三者的话,就可以超快速地开发大量的RESTful API
!
使用Django REST framework 的过滤功能 一个最简单的过滤功能, 例如查询用户列表,只返回用户粉丝数大于100的:
1 2 3 4 5 6 7 8 9 class XXXViewSet (mixins.ListModelMixin, mixins.CreateModelMixin,viewsets.GenericViewSet ): serializer_class = ModelSerializer def get_queryset (self ): fans_min = self.reuqest.query_params.get("fans_min" , 0 ) if fans_min: return User.objects.filter (fans_num__gt=int (fans_min)) return User.objects.all ()
上面这种方法如果要过滤的字段多的话,就要写大量繁琐的业务逻辑代码
如果想以少量的代码实现功能强大的过滤要用其他方案了,就要使用django-filter
来完成. 首先 pip install django-filter
, 然后把 django-filter
加到 INSTALLED_APPS
列表中
代码实现:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 from rest_framework import mixinsfrom rest_framework import viewsetsfrom django_filters.rest_framework import DjangoFilterBackendfrom django.contrib.auth import get_user_modelUser = 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' )
然后使用浏览器打开url, 就会发现页面多了一个过滤器,点开之后输入信息就可以使用过滤功能了. 这种方式还是用局限性,比如用户名想用模糊搜索,或者想要查询粉丝数大于100小于200的用户,这种方式是做不到的.这时候可以使用自定义filter
来实现!
新建 filter.py
1 2 3 4 5 6 7 8 9 10 11 12 13 14 import django_filtersfrom django.contrib.auth import get_user_modelUser = 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' ]
然后之前的代码改为:
1 2 3 4 5 6 7 8 9 from .filter import UserFilterclass UserViewSet (mixins.ListModelMixin, mixins.CreateModelMixin, viewsets.GenericViewSet ): queryset = User.objects.all () serializer_class = ModelSerializer filter_backends = (DjangoFilterBackend,) filter_class = UserFilter
过滤用户名的话,其实用SearchFilter
也可以实现,用两行代码就完成了
1 2 filter_backends = (DjangoFilterBackend, SearchFilter) search_fields = ('name' ,)
还有一些更加强大的配置
1 2 3 4 5 6 7 8 9 10 11 12 13 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')
还有一个排序的filter
,例如我们想按照用户的粉丝数量进行排序(升序和降序):
1 2 filter_backends = (DjangoFilterBackend, SearchFilter, OrderingFilter) ordering_fields = ('fans_num' ,)
这样已经完成了,如果想要做更加复杂的过滤,可以查看django-filter
的文档
自定义分页 1 2 3 4 5 6 7 8 9 10 11 12 13 14 from rest_framework.pagination import PageNumberPaginationclass UsersPagination (PageNumberPagination ): page_size = 10 page_size_query_param = 'page_size' page_query_param = 'page' class UserViewSet (mixins.ListModelMixin, mixins.CreateModelMixin, viewsets.GenericViewSet ): pagination_class = UsersPagination
就这样几行代码就搞定了,而且在返回的json
中加了总数,下一页的链接和上一页的链接.
回顾我们之前的代码,在UserViewSet
这个类里面,才写了7行代码,就已经完成了
获取用户列表
create一个用户
分页
搜索
顾虑
排序
这些功能,如果想要获取指定用户的具体信息,直接继承mixins.RetrieveModelMixin
就直接做好了…’’
权限认证 比如有一些API功能,是需要用户登录才能使用可以的 或者比如我要删除我这篇博客,也要验证我是作者才能删除
验证用户是否登录
1 2 3 4 5 6 from rest_framework.permissions import IsAuthenticatedclass XXXViewSet (mixins.CreateModelMixin, mixins.DestroyModelMixin ): permission_classes = (IsAuthenticated,)
验证操作是本人,需要自定义persssion
permissions.py
1 2 3 4 5 6 7 8 9 from rest_framework import permissionsclass 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的用户认证模式 第一步: pip install djangorestframework-jwt
第二步: 在url.py中配置
1 2 3 4 5 6 from rest_framework_jwt.views import obtain_jwt_tokenurlpatterns = [ ... url(r'^api-token-auth/' , obtain_jwt_token), ... ]
第三步: 在需要jwt认证的ViewSet的类里面设置
1 2 3 4 5 from rest_framework_jwt.authentication import JSONWebTokenAuthenticationfrom rest_framework.authentication import SessionAuthenticationclass XXXViewSet (mixins.CreateModelMixin, viewsets.GenericViewSet ): authentication_classes = (JSONWebTokenAuthentication, SessionAuthentication)
加上SessionAuthentication
是为了在网站上调试方便
现在注册登录有两种方式:
用户注册之后跳转到登录页面让其登录
用户注册之后自动帮他登录了
第一种情况的话我们无需再做其他操作,第二种情况我们应该在用户注册之后返回jwt token
的字段给前台,所以要做两步:
因为返回字段是mixins
帮我们做好了,所以我们要重写对应的方法来修改返回字段
需要查看djangorestframework-jwt
的源码找到生成jwt token的方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 from rest_framework_jwt.serializers import jwt_encode_handler, jwt_payload_handlerclass UserViewSet (CreateModelMixin, RetrieveModelMixin,UpdateModelMixin,viewsets.GenericViewSet ): 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 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
属性就可以很方便的实现
1 2 3 4 5 6 7 8 9 10 11 class UserViewSet (CreateModelMixin, RetrieveModelMixin,UpdateModelMixin,viewsets.GenericViewSet ): def get_serializer_class (self ): if self.action == 'create' : return XXXSerializer elif self.action == 'list' : return XXXSerializer return XXXSerializer
一些实用的Serializer fields 比如说我要发布这篇文章,需要上传我(用户)的id才能和这篇文章建立关联,但我们这个可以不用前台来上传
serializer.py
1 2 3 4 5 class XXXSerializer (serializers.ModelSerializer ): user = serializers.HiddenField( default = serializers.CurrentUserDefault() )
还有如果返回的字段逻辑比较复杂,可以用serializer.SerializerMethodField()
来完成,例如:
1 2 3 4 5 6 7 class XXXSerializer (serializers.ModelSerializer ): xxx = serializer.SerializerMethodField() def get_xxx (self, obj ): return
自定义用户认证 1 2 3 4 5 6 7 8 9 10 11 12 ```Python 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
1 2 3 4 5 6 7 8 class UserRegSerializer (serializers.ModelSerializer ): def create (): user = super (UserRegSerializer,self).create(validated_data=validated_data) user.set_password(validated_data["password" ]) user.save() return user
或者也可以用django
的信号量也可以解决
1 2 3 4 5 6 7 8 9 10 11 12 from django.db.models.signals import post_savefrom django.dispatch import receiverfrom django.contrib.auth import get_user_modelUser = 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里面配置
1 2 3 4 5 6 7 8 from django.apps import AppConfigclass UsersConfig (AppConfig ): name = 'users' def ready (self ): import users.signals
大功告成!!!