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

它重写了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就可以看到相应的路由视图了,(这种需要使用的路由注册的方法)

这样你就会在过滤的界面里看到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

Django RESRframework奇淫技巧的更多相关文章

  1. 优化DP的奇淫技巧

    DP是搞OI不可不学的算法.一些丧心病狂的出题人不满足于裸的DP,一定要加上优化才能A掉. 故下面记录一些优化DP的奇淫技巧. OJ 1326 裸的状态方程很好推. f[i]=max(f[j]+sum ...

  2. 12个实用的 Javascript 奇淫技巧

    这里分享12个实用的 Javascript 奇淫技巧.JavaScript自1995年诞生以来已过去了16个年头,如今全世界无数的网页在依靠她完成各种关键任务,JavaScript曾在Tiobe发布的 ...

  3. NGINX的奇淫技巧 —— 5. NGINX实现金盾防火墙的功能(防CC)

    NGINX的奇淫技巧 —— 5. NGINX实现金盾防火墙的功能(防CC) ARGUS 1月13日 发布 推荐 0 推荐 收藏 2 收藏,1.1k 浏览 文章整理中...... 实现思路 当服务器接收 ...

  4. NGINX的奇淫技巧 —— 3. 不同域名输出不同伺服器标识

    NGINX的奇淫技巧 —— 3. 不同域名输出不同伺服器标识 ARGUS 1月13日 发布 推荐 0 推荐 收藏 6 收藏,707 浏览 大家或许会有这种奇葩的需求...要是同一台主机上, 需要针对不 ...

  5. NGINX的奇淫技巧 —— 6. IF实现数学比较功能 (1)

    NGINX的奇淫技巧 —— 6. IF实现数学比较功能 (1) ARGUS 1月13日 发布 推荐 0 推荐 收藏 3 收藏,839 浏览 nginx的if支持=.!= 逻辑比较, 但不支持if中 & ...

  6. Zepto源码分析(二)奇淫技巧总结

    Zepto源码分析(一)核心代码分析 Zepto源码分析(二)奇淫技巧总结 目录 * 前言 * 短路操作符 * 参数重载(参数个数重载) * 参数重载(参数类型重载) * CSS操作 * 获取属性值的 ...

  7. javascript之奇淫技巧

    最近准备面试,复习一下javascript,整理了一些javascript的奇淫技巧~ //为兼容ie的模拟Object.keys() Object.showkeys = function(obj) ...

  8. Gradle更小、更快构建APP的奇淫技巧

    本文已获得原作者授权同意,翻译以及转载原文链接:Build your Android app Faster and Smaller than ever作者:Jirawatee译文链接:Gradle更小 ...

  9. BZOJ 3192: [JLOI2013]删除物品 奇淫技巧&树状数组

    点我看题 这题十分奇淫技巧...QAQ因为知道是树状数组的题QAQ刚开始以为维护两个数组的树状数组然后模拟从大到小,然后发现不会打QAQ 于是悄悄咪咪翻开题解了. 实际上两个数组可以看做一个数组 如 ...

随机推荐

  1. HTML连载38-内边距属性、外边距属性

    一.内边距属性 1.定义:边框和内容之间的距离就是内边距 2.分开写 padding-top:数字px: padding-left:数字px: padding-bottom:数字px: padding ...

  2. C# HTTP网络常用方法封装

    using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace Regi ...

  3. Java 自定义注解 校验指定字段对应数据库内容重复

    一.前言 在项目中,某些情景下我们需要验证编码是否重复,账号是否重复,身份证号是否重复等... 而像验证这类代码如下: 那么有没有办法可以解决这类似的重复代码量呢? 我们可以通过自定义注解校验的方式去 ...

  4. PopUpWindow 的使用笔记

    最接做需求的时候,碰到了 PopUpWindow,但是也没做过多了解,就是照搬别人的代码改改逻辑.后面视觉看了之后,说让我加一些动画效果,使用起来更加舒服.可是我看别人以前也没有写,于是就开始捣鼓 P ...

  5. select2 分组后的选项无法被选中

    在使用select2组件的过程中发现分组下的选项无法选中,与分组在同一级的选项可以被选中! 1.所用select2版本select2-4.0.32 2.HTML代码: <input id=&qu ...

  6. mysql修改数据库的存储引擎(InnoDB)

    查看当前的存储引擎 show engines; 基本的差别:MyISAM类型不支持事务处理等高级处理,而InnoDB类型支持.MyISAM类型的表强调的是性能,其执行数度比InnoDB类型更快,但是不 ...

  7. setStyleSheet 设置背景图片

    设置背景颜色很简单,大部分教程都对 设置背景图像有一个小坑. 设置背景图像主要有两种情况, 第一种:图片的绝对路径 ``` this->setObjectName("mainWindo ...

  8. C#实现请求唯一性校验支持高并发

    使用场景描述: 网络请求中经常会遇到发送的请求,服务端响应是成功的,但是返回的时候出现网络故障,导致客户端无法接收到请求结果,那么客户端程序可能认为判断为网络故障,而重复发送同一个请求.当然如果接口中 ...

  9. 阿里云服务器CentOS6.9 nexus私服配置

    从nexus官网下载nexus版本. 下载地址如下:https://help.sonatype.com/repomanager2/download 如果要下载3.X版本的,可以把repomanager ...

  10. ng form组件(表单)

    tip: 数据的双向绑定(数据的双向绑定只是针对表单) 实现数据的双向绑定需要在app_module.ts(根模块)中进行注册一些东西 import {FormsModule} from '@angu ...