django-rest-framework

源码繁琐,多说无益,耐心细读官方文档:数据库

https://www.django-rest-framework.org/django

 

我的总结:json

 

REST是一种软件架构设计风格,不是标准,也不是具体的技术实现,只是提供了一组设计原则和约束条件。api

DRF(Django RestFramework)是一套基于Django开发的、帮助咱们更好的设计符合REST规范的Web应用的一个Django App,因此,本质上,它是一个Django App。浏览器

安装: (肯定Django已经安装)服务器

   >>> pip install djangorestframework

 

 

1 APIView cookie

  首先须要了解django中views.View类及其相关流程,看以下关系图(最好看源码):session

 

  DRF APIView请求流程:架构

 

 

 

  DRF对django视图配置流程图(我的画)app

 

 

2 解析器组件 (用来解析数据的请求的组件)

  Django并不能处理请求协议为application/json编码协议的数据

  注意: DRF解析器会封装到View中的parsers内,在视图函数被调用时,会传入request,经过request.data拿到数据才进行解析 ,即解析器解析是在request对象传入后.

  解析器组件流程图:

 

 

//解析器的使用方式: //1,导入模块 views.py from rest_framwork.views import APIView //2, 继承APIView class BookView(APIView): def get(self, request): pass

    //3, url.py from django.urls import path, include, re_path from classbasedview import views urlpatterns = [ re_path('login/$', views.LoginView.as_view()), ] //4, def post(self, request): origin_data = request.data ... return HttpResponse({})  

 

  试用工具: postman---经过postman来模拟用户请求,再也不须要使用浏览器来发送请求.(直接在官网下载便可)


3 序列化组件 

  序列化组件的使用:

  --get接口设计:

  • 导入序列化组件:from rest_framework import serializers
  • 定义序列化类,继承serializers.Serializer(建议单首创建一个专用的模块用来存放全部的序列化类):class BookSerializer(serializers.Serializer):pass
  • 定义须要返回的字段(字段类型能够与model中的类型不一致,参数也能够调整),字段名称必须与model中的一致
  • 在GET接口逻辑中,获取QuerySet
  • 开始序列化:将QuerySet做业第一个参数传给序列化类,many默认为False,若是返回的数据是一个列表嵌套字典的多个对象集合,须要改成many=True
  • 返回:将序列化对象的data属性返回便可

 

  {{ 实践代码 }}

  --post接口设计

  • url定义:须要为post新增url,由于根据规范,url定位资源,http请求方式定义用户行为
  • 定义post方法:在视图类中定义post方法
  • 开始序列化:经过咱们上面定义的序列化类,建立一个序列化对象,传入参数data=request.data(application/json)数据
  • 校验数据:经过实例对象的is_valid()方法,对请求数据的合法性进行校验
  • 保存数据:调用save()方法,将数据插入数据库
  • 插入数据到多对多关系表:若是有多对多字段,手动插入数据到多对多关系表
  • 返回:将插入的对象返回

  {{ 实践代码 }}

  使数据自动插入并且更加简单:

class BookSerializer(serializers.ModelSerializer): class Meta: model = Book fields = ('title', 'price', 'publish', 'authors', 'author_list', 'publish_name', 'publish_city' ) extra_kwargs = { 'publish': {'write_only': True}, 'authors': {'write_only': True} } publish_name = serializers.CharField(max_length=32, read_only=True, source='publish.name') publish_city = serializers.CharField(max_length=32, read_only=True, source='publish.city') author_list = serializers.SerializerMethodField() def get_author_list(self, book_obj): # 拿到queryset开始循环 [{}, {}, {}, {}]
        authors = list() for author in book_obj.authors.all(): authors.append(author.name) return authors

  步骤以下:

    继承ModelSerializer: 再也不继承Serializer

    添加extra_kwargs类变量: extra_kwargs = { 'publish':{'write_only':True}}

 

4 视图组件

  使用视图组件进行接口逻辑化

   导入mixin

from rest_framework.mixinx import ( ListModelMix, CreateModelMixin, DestroyModelMixin, UpdateModelMixin, RetrieveModelMixin ) from rest_framework.generics import GenericAPIView 

    定义序列化类

Class BookSerializer(serializers.ModelSerializer): class Meta: Book fields = () extra_kwargs = {"field_name": {"write_only": True}}

 

    导入序列化类

    from .app_serializers import BookSerializer

  定义视图类

            class BookView(ListModelMix, CreateModelMixin, GenericAPIView): # queryset和serializer_class是固定的写法
                queryset = Book.objects.all() serializer_class = BookSerializer def get(): return self.list() def post(): return self.create() class BookFilterView(RetrieveModelMixin, DestroyModelMixin, UpdateModelMixin, GenericAPIView): queryset = Book.objects.all() serializer_class = BookSerializer def get(): return self.retrieve() def delete(): return self.destroy() def put(): return self.update()

 

   注意: 单条数据操做的url是这样的:re_path(r'books/(?P<pk>\d+)/$, views.BookFilterView.as_view())

 

  使用视图组件的view进行接口逻辑优化

    导入模块  from rest_framework import generics

    写试图类

class BookView(generics.ListCreateAPIView) queryset = Book.objects.all() serializer_class = BookSerializer class BookFilterView(generics.RetrieveUpdateDestroyAPIView): queryset = Book.objects.all() serializer_class = BookSerializer

 

  使用视图组件的viewset进行接口逻辑优化

    导入模块 from rest_framework.viewset import ModelViewSet

    设计url

re_path(r'books/$, views.BookView.as_view({
                    'get': 'list', 'post': 'create' })), re_path(r'books/(?P<pk>\d+)/$', views.BookView.as_view({ 'get': 'retrieve', 'delete': 'destroy', 'put': 'update' }))

 

 

 

    设计视图类

       class BookView(ModelViewSet): queryset = Book.objects.all() serializer_class = BookSerializer

 

 

  • Django程序启动,开始初始化,获取配置信息,获取视图类并加载到内存中,获取url及视图类的对应关系
  • 开始绑定视图类和url的对应关系,执行as_view()方法
  • as_view()方法被执行的时候传递了参数,为字典形式:{ “get”: “retrieve”, “delete”: “destroy”, “put”: “update” }
  • 上一步中执行as_view()方法传递参数的目的是为了完成优化,将delete请求方式从新命名为不一样的函数
  • ViewSetMixin类重写了as_view()方法,也就是在这个地方将几个函数从新绑定,它并无重写dispatch方法
  • 该方法返回视图函数view,注意在这个函数中有一个行 self = cls(**initkwargs), cls是视图类,执行视图函数时self就指向视图函数的实例对象
  • 等待客户端请求
  • 请求到来,开始执行视图函数,注意,调用视图函数时的方式是view(request),而若是url带有参数,调用方式为view(request, xxx=id)的形式
  • 显然,咱们有命名参数(?P\d+),因此此时的调用方式为view(request, pk=id)
  • 视图函数中有一行self.kwargs = kwargs,因此pk已经被视图函数找到了
  • 视图函数返回self.dispatch(),开始执行dispatch方法,注意self是视图类的实例化对象(每一个请求都被封装为一个对象)
  • dispatch开始执行get方法,注意此时的get方法会执行retrieve,觉得已经被重定向了
  • 开始执行retrieve,有一行instance = self.get_object(), 该方法在GenericAPIView中
  • 相当重要的是拿到self.kwargs中的pk关键字,而后从queryset中拿到想要的数据
  • 返回结果

 

 

5 认证组件

  cookie和session两种方式能够保存用户信息,这两种方式不一样的是cookie保存在客户端浏览器中,而session保存在服务器中,他们各有优缺点,配合起来使用,可将重要的敏感的信息存储在session中,而在cookie中能够存储不太敏感的数据。

  token称之为令牌。cookie、session和token都有其应用场景,没有谁好谁坏,不过开发数据接口类的Web应用,目前用token仍是比较多的。

  token认证步骤:

    用户登陆,服务器端获取密码,查询用户表,若是存在该用户且第一次登陆(或者token过时), 生成token,不然返回错误信息

    若是用户不是第一次登陆,且token未过时,更新token值

  建立俩个model,(token能够存储在user表中,建议存储在user表中):

    

from django.db import models # Create your models here.

class User(models.Model): username = models.CharField(max_length=32) password = models.CharField(max_length=32) user_type_entry = ( (1, 'Delux'), (2, 'SVIP'), (3, "VVIP") ) user_type = models.IntegerField(choices=user_type_entry) address = models.CharField(max_length=32) def __str__(self): return self.username class UserToken(models.Model): user = models.OneToOneField("User", on_delete=models.CASCADE) token = models.CharField(max_length=128)

  由于涉及登陆认证,因此写post方法接口,登陆都是post请求:

  

from django.http import JsonResponse from rest_framework.views import APIView from .models import User, Book, UserToken from .utils import get_token class UserView(APIView): def post(self, request): response = dict() try: username = request.data['username'] password = request.data['password'] user_instance = User.objects.filter( user_name=username, password=password ).first() if user_instance: access_token = get_token.generater_token() UserToken.objects.update_or_create(user=user_instance, defaults={ "token": access_token }) response["status_code"] = 200 response["status_message"] = "登陆成功" response["access_token"] = access_token response["user_role"] = user_instance.get_user_type_display() else: response["status_code"] = 201 response["status_message"] = "登陆失败,用户名或密码错误"
        except Exception as e: response["status_code"] = 202 response["status_message"] = str(e) return JsonResponse(response)

  经过获取随机字符串的方法用来生成token值:

# -*- coding: utf-8 -*-
import uuid def generater_token(): random_str = ''.join(str(uuid.uuid4()).split('-')) return random_str

  DRF认证组件的使用:

    新建一个认证类,包含以后的认证逻辑:

class UserAuth(object): def authenticate_header(self, request): pass

    def authenticate(self, request): user_post_token = request.query_params.get('token') token_object = UserToken.objects.filter(token=user_post_token).first() if token_object: return token_object.user.username, token_object.token else: raise APIException("认证失败")

  实现方式看上去很是简单,到token表里面查看token是否存在,而后根据这个信息,返回对应信息便可,而后,在须要认证经过才能访问的数据接口里面注册认证类便可: 

class BookView(ModelViewSet): authentication_classes = [UserAuth, UserAuth2] queryset = Book.objects.all() serializer_class =  BookSerializer

  多个认证类实现:

    注意:若须要返回数据,请在最后一个认证类中返回,由于在前面返回,self.authentication()方法中会对返回值进行判断,若不为空,认证的过程就会终止.  多个认证类实现方式以下:

class UserAuth2(object): def authenticate(self, request): raise APIException("认证失败") class UserAuth(object): def authenticate_header(self, request): pass

    def authenticate(self, request): user_post_token = request.query_params.get('token') token_object = UserToken.objects.filter(token=user_post_token).first() if token_object: return token_object.user.username, token_object.token else: raise APIException("认证失败") class BookView(ModelViewSet): authentication_classes = [UserAuth, UserAuth2]

  简化authenticate_header方法,以下:(继承BaseAuthentication类便可)

from rest_framework.authentication import BaseAuthentication class UserAuth2(BaseAuthentication): def authenticate(self, request): raise APIException("认证失败") class UserAuth(BaseAuthentication): def authenticate(self, request): user_post_token = request.query_params.get('token') token_object = UserToken.objects.filter(token=user_post_token).first() if token_object: return token_object.user.user_name, token_object.token else: raise APIException("认证失败")

 

  全局认证: 

    实现全部的数据接口都须要认证:  

authentication_classes=api_settings.DEFAULT_AUTHENTICATION_CLASSES

  若是认证类本身没有authentication_classes,就会到settings中去找,经过这个机制,咱们能够将认证类写入到settings文件中便可实现全局认证:

REST_FRAMEWORK = { 'DEFAULT_AUTHENTICATION_CLASSES': ( 'authenticator.utils.authentication.UserAuth', 'authenticator.utils.authentication.UserAuth2', ), }

 

6 权限组件 

  定义权限类: 

class UserPerms(): message = "您没有权限访问该数据"
    def has_permission(self, request, view): if request.user.user_type > 2: return True else: return False

  一样的逻辑,一样的方式,只是执行权限的方法名与执行认证的方法名不同而已,名为has_permission,而且须要将当前的视图类传递给该方法。

  视图类中加入permission_classes变量:

class BookView(ModelViewSet): authentication_classes = [UserAuth] permission_classes = [UserPerms2] queryset = Book.objects.all() serializer_class =  BookSerializer

 

7 频率组件


8 url控制器组件
9 分页器组件
10 响应器组件

相关文章
相关标签/搜索