开始之前,假设你已经有Django和Django REST framework的一些基础了

mixins,ViewSet和routers配合使用

minxis的类有5种

  • CreateModelMixin
  • ListModelMixin
  • RetrieveModelMixin
  • UpdateModelMixin
  • DestroyModelMixin

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

ViewSet也有5种,分别是

  • ViewSetMixin
  • ViewSet
  • GenericViewSet
  • ReadOnlyModelViewSet
  • ModelViewSet

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

它重写了方法as_view,这个能让我们注册url变得更加简单,还有一个方法是initialize_request,这个方法主要是给action属性赋值,这个属性在设置动态serializerpermission的时候有很大的用处!之后会写到

所以写一个APIView就变得很简单,如下:

from rest_framework import mixins
from rest_framework import viewsetsclass XXXViewSet(mixins.ListModelMixin, mixins.CreateModelMixin, viewsets.GenericViewSet):queryset = Model.objects.all()serializer_class = ModelSerializer复制代码

接下来就是配置url了

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

这样就可以把get请求绑定到list的方法上,post请求就绑定到了create方法.不需要再去重写它们了

其实上面配置url的方法还是过于繁琐,这时候就是router登场了,上面的代码改为:

from rest_framework.routers import DefaultRouter
from appname.views import XXXViewSetrouter = 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 REST framework 的过滤功能

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

class XXXViewSet(mixins.ListModelMixin, mixins.CreateModelMixin,viewsets.GenericViewSet):serializer_class = ModelSerializerdef 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列表中

代码实现:

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

然后使用浏览器打开url,

就会发现页面多了一个过滤器,点开之后输入信息就可以使用过滤功能了. 这种方式还是用局限性,比如用户名想用模糊搜索,或者想要查询粉丝数大于100小于200的用户,这种方式是做不到的.这时候可以使用自定义filter来实现!

新建filter.py

import django_filters
from 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 = Userfields = ['name', 'min_fans_num', 'max_fans_num']
复制代码

然后之前的代码改为:

from .filter import UserFilterclass UserViewSet(mixins.ListModelMixin, mixins.CreateModelMixin, viewsets.GenericViewSet):queryset = User.objects.all()serializer_class = ModelSerializerfilter_backends = (DjangoFilterBackend,)filter_class = UserFilter复制代码

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

filter_backends = (DjangoFilterBackend, SearchFilter)
search_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')

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

filter_backends = (DjangoFilterBackend, SearchFilter, OrderingFilter)
ordering_fields = ('fans_num',)
复制代码

这样已经完成了...如果想要做更加复杂的过滤,可以查看django-filter的文档

自定义分页

from rest_framework.pagination import PageNumberPaginationclass 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):# 设置分页的classpagination_class = UsersPagination
复制代码

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

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

  • 获取用户列表
  • create一个用户
  • 分页
  • 搜索
  • 顾虑
  • 排序

这些功能,如果想要获取指定用户的具体信息,直接继承mixins.RetrieveModelMixin就直接做好了...''

权限认证

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

验证用户是否登录

from rest_framework.permissions import IsAuthenticatedclass XXXViewSet(mixins.CreateModelMixin, mixins.DestroyModelMixin):permission_classes = (IsAuthenticated,)复制代码

验证操作是本人,需要自定义persssion

permissions.py

from rest_framework import permissionsclass IsOwnerOrReadOnly(permissions.BasePermission):def has_object_permission(self, request, view, object):if request.method in permissions.SAFE_METHODS:return Truereturn object.user == request.user
复制代码

permission_classes = (IsAuthenticated, IsOwnerOrReadOnly)

使用JWT的用户认证模式

第一步: pip install djangorestframework-jwt

第二步: 在url.py中配置

from rest_framework_jwt.views import obtain_jwt_token
urlpatterns = [...url(r'^api-token-auth/', obtain_jwt_token),...
]
复制代码

第三步: 在需要jwt认证的ViewSet的类里面设置

from rest_framework_jwt.authentication import JSONWebTokenAuthentication
from rest_framework.authentication import SessionAuthenticationclass XXXViewSet(mixins.CreateModelMixin, viewsets.GenericViewSet):authentication_classes = (JSONWebTokenAuthentication, SessionAuthentication)复制代码

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

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

  • 用户注册之后跳转到登录页面让其登录
  • 用户注册之后自动帮他登录了

第一种情况的话我们无需再做其他操作,第二种情况我们应该在用户注册之后返回jwt token的字段给前台,所以要做两步:

  • 因为返回字段是mixins帮我们做好了,所以我们要重写对应的方法来修改返回字段
  • 需要查看djangorestframework-jwt的源码找到生成jwt token的方法
from rest_framework_jwt.serializers import jwt_encode_handler, jwt_payload_handlerclass 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 Tokenpayload = 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的相关操作可以查看文档

返回json中,url没有全路径

这个情况一般在嵌套serializer中出现,解决:

XXXSerializer(context={'request': self.context{'request'}})
复制代码

动态serializers

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

class UserViewSet(CreateModelMixin, RetrieveModelMixin,UpdateModelMixin,viewsets.GenericViewSet):# 这个就不需要了#serializer_class = XXXSerializerdef get_serializer_class(self):if self.action == 'create':return XXXSerializerelif self.action == 'list':return XXXSerializerreturn XXXSerializer
复制代码

一些实用的Serializer fields

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

serializer.py

class XXXSerializer(serializers.ModelSerializer):# user默认是当前登录的useruser = 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 ModelBackendclass 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 userexcept 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.passwordinstance.set_password(password)instance.save()复制代码

然后还要app.py里面配置

from django.apps import AppConfigclass UsersConfig(AppConfig):name = 'users'def ready(self):import users.signals
复制代码

总结:

人生苦短,我用Python!

转载于:https://juejin.im/post/5aa4f072f265da237a4ca386

Django REST framework的一些奇巧淫技(干货!!!)相关推荐

  1. 算法很美第一章 位运算的奇巧淫技

    第一章 位运算的奇巧淫技 1.2 题解:如何找数组中唯一成对的那个数 知识点: A^A=0 A^0=A(一个数与0进行按位异或,结果是他自己) 对一个序列进行按位异或,可以消除掉序列中的重复项,比如: ...

  2. iOS 开发的一些奇巧淫技

    iOS开发的一些奇巧淫技1&2 CGfloat和float的区别? 现在上架的app都要求支持64位系统,那么CGFloat和float的区别就在这里.command+左键点击CGFloat. ...

  3. 【奇巧淫技】python 助你每天早上八点自动发送天气预报邮件到QQ邮箱

    此博客仅为我业余记录文章所用,发布到此,仅供网友阅读参考,如有侵权,请通知我,我会删掉. 补充 有不少杠精小婊贝留言说本文章没有用,因为天气预报直接打开手机就可以收到了,为何要多此一举发送到邮箱呢!! ...

  4. 位运算概览与奇巧淫技

    文章目录 一.位运算概述 二.位运算概览 异或 二进制数的原码.反码.补码 左移 << 右移 >> 无符号右移 >>> 三.位运算的使用奇巧淫技 判断奇偶数 ...

  5. 蓝桥杯算法竞赛系列第一章——位运算的奇巧淫技及其实战

    遇见蓝桥遇见你,不负代码不负卿! 第二章"递归"已将更新咯,欢迎铁汁们点评!蓝桥杯算法竞赛系列第二章--深入理解重难点之递归(上)_安然无虞的博客-CSDN博客 目录 一.位运算符 ...

  6. iOS开发的一些奇巧淫技2

    能不能只用一个pan手势来代替UISwipegesture的各个方向? 1 - (void)pan:(UIPanGestureRecognizer *)sender 2 { 3 4 typedef N ...

  7. iOS 【奇巧淫技】获取webView内容高度

    针对获取webView高度问题之前写过一个方案--通过监听WebView的scrollView的变化来实时更新高度 附上链接: iOS[终极方案]精准获取webView内容高度,自适应高度 本文是给出 ...

  8. jane street market prediction 冠军方案 奇巧淫技与topline链接整理(3/3)

    目录 前言 将income 作为loss(优化目标) 读取数据,定义优化目标 模型训练与微调 提交结果 多目标学习 Topline整理 AE+MLP (rank10) Current 17th sol ...

  9. 贪心的奇巧淫技-Join the Strings

    题目大意:已知n个长度不同的字符串,求字典序最小的方案. 分析:直接贪心是不对的,因为按字典序排序的话会出现这种情况:bt,btb,此时直接贪心就成了btbtb,明显btbbt更优.~~所以按(a+b ...

最新文章

  1. cannot import caffe
  2. 一种嵌入式系统的内存分配方案
  3. RSS 没有死亡 而是无所不在
  4. Java 基础知识总结(下)-王者笔记《收藏版》
  5. 利用linux curl爬取网站数据
  6. 可运行的C语言程序的拓展名,可运行的c语言程序的扩展名为什么?
  7. 嵌入式开发与C++开发的区别是什么?
  8. vs的离线下载.iso文件_Windows10官方纯净系统下载
  9. mysql5.7未生成初始密码.mysql_secert文件,登陆数据库
  10. FragmentPagerAdapter FragmentPagerAdapter .
  11. GitHub官网操作指南
  12. linux设计论文题目,计算机linux本科毕业论文题目
  13. FaceBook流程到底应该如何?
  14. Java核心编程总结(六、常用API与集合)
  15. c语言两个矩形相交部分坐标,C++判断矩形相交的方法
  16. ffmpeg 如何剔除掉视频中的水印和马赛克
  17. BSC Web3生态深度研究
  18. 城市微光,大抵如此--爱摸鱼的美工(15)
  19. FFmpeg简单使用:视频编码 ---- YUV转H264
  20. 第4关 注册配置中心实现

热门文章

  1. 正式迁移博客到osc
  2. PostgreSQL中的数据库实例、模式、用户(角色)、表空间
  3. CC1100E被不同频段的频率干扰问题
  4. wince中的hook(钩子)用法
  5. BUCK/BOOST电路原理分析
  6. 通过Python的__slots__节省9GB内存
  7. ASP.NET Web API 过滤器创建、执行过程(二)
  8. igbinary vs serialize vs json_encode
  9. Linq延迟求值现象
  10. php 面向对象学习