Loading

Django--restframework

Django--restframework

一 . rest framework框架的认识

  • 它是基于Django的,帮助我们快速开发符合RESTful规范的接口框架

  • 安装 pip

二 . 什么是RESTful风格

三 . 基于Django实现

urls.py

urlpatterns = [
    path('index/', views.TestView.as_view()),  # 函数视图
]

views.py

from django.forms import model_to_dict
from django.http import HttpResponse, JsonResponse
from django.views import View

class TestView(View):
    def get(self, request):
        data = request.GET.get('name')  # View获取前端传递的GET请求name参数
        return HttpResponse(data)

    def post(self, request):
        data = request.POST.get('username')  # View获取前端传递的POST请求username参数
        return HttpResponse(data)

class TestView(View):
     #基于django的序列化 for循环
    def get(self, request):
        queryset = User.objects.all()
        print(queryset)
        list = []
        for i in queryset:
            list.append(model_to_dict(i))  # model对象转换成dict
            # list.append({
            #     "name": i.name,
            #     "age": i.age,
            #     "home": i.home
            # })
            print(list)
        # In order to allow non-dict objects to be serialized set the safe parameter to False.
        # return JsonResponse(list)——》一定要加上:safe=False
        return JsonResponse(list, safe=False)

四 . 基于Django Rest Framework框架实现

DRF框架之视图集APIView:https://www.cnblogs.com/tjw-bk/p/13886536.html

models.py

from django.db import models

# 角色表
class Role(models.Model):
    role_name = models.CharField(max_length=32, verbose_name='角色')

    class Meta:
        db_table = 'tb_role'
        verbose_name = '角色'
        verbose_name_plural = verbose_name
#用户表
class User(models.Model):
    name = models.CharField(max_length=12, verbose_name='姓名')
    age = models.IntegerField(verbose_name='年龄')
    home = models.CharField(max_length=255, null=True, verbose_name='家乡')
    role = models.ManyToManyField(Role)
    class_room = models.ForeignKey(to='ClassRoom', on_delete=models.CASCADE, null=True)

    class Meta:
        db_table = 'tb_user'
        verbose_name = '用户'
        verbose_name_plural = verbose_name

#班级表
class ClassRoom(models.Model):
    classroom = models.CharField(max_length=20, verbose_name='班级')
    address = models.CharField(max_length=32, verbose_name='地址')

    class Meta:
        db_table = 'tb_clasroom'
        verbose_name = '班级'
        verbose_name_plural = verbose_name

urls.py

# 子路由文件
from django.urls import include, path
from user import views
from rest_framework.routers import SimpleRouter, DefaultRouter

# 自动生成路由方法,必须使用视图集
# router=SimpleRouter() #没有根路由 /user/无法识别 生产环境使用
router = DefaultRouter()  # 有根路由 # 开发环境用,有主界面
router.register(r'user2', views.UserModelViewSet)  # 配置路由

urlpatterns = [
    path('user/', views.UserView.as_view()),
    path('',include(router.urls))
]

自定义序列化页面serializers.py

from rest_framework import serializers
from .models import *


class UserSerializers(serializers.ModelSerializer):
    class Meta:
        model = User
        fields = "__all__"
        depth = 1  # 外键序列化

1. 分页

1.1 PageNumberPagination

前端访问网址形式:

GET http://192.168.56.100:8888/user/user2?page=1

可以在子类中定义的属性:

  • page_size_query_param = 'page_size' 前端发送的每页数目关键字名,默认为None
  • max_page_size = 4 前端最多能设置的每页数量
  • page_size = 2 每页数目
  • page_query_param = 'page' 前端发送的页数关键字名,默认为"page"
1.2 可在setting配置全局分页器
  • INSTALLED_APPS = [
        'rest_framework', #需要注册应用
    ]
    
  • REST_FRAMEWORK = {
         # 分页(全局):全局分页器, 例如 省市区的数据自定义分页器, 不需要分页
        'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.PageNumberPagination',
        # 每页返回数量
        'PAGE_SIZE': 10,  # 默认 None
    }
    
1.3 views.py
from rest_framework.viewsets import ModelViewSet
from rest_framework.pagination import PageNumberPagination	#导包 

#自定义分页类
class PageNumberPagination2(PageNumberPagination):
    page_size_query_param = 'page_size'
    max_page_size = 4
    page_size = 2
    page_query_param = 'page'

'''
手写分页
       def get(self, request):
            # 手写页面器
            num = request.GET.get('num', 1)
            num = int(num)

            queryset = Goods.objects.all()
            paginator = Paginator(queryset, 1)

            onepage = paginator.get_page(num)

            resp = {}
            resp['data'] = GoodsSer(onepage, many=True).data
            resp['next_page'] = num + 1
            resp['prev_page'] = num - 1
            resp['page_range'] = [i for i in paginator.page_range]
            return Response(resp)
'''

class UserModelViewSet(ModelViewSet):
    #设置查询集
    queryset = User.objects.all()
	#设置序列化器类
    serializer_class = UserSerializers
	#设置分页器
    pagination_class = PageNumberPagination2 	#使用自定义分页器
	#pagination_class = PageNumberPagination		 #使用全局分页器
     
#http://192.168.56.100:8888/user/user2?page=2
1.4 使用postman测试

2.排序

  • 对于列表数据 rest framework提供了OrderingFilter过滤器来帮助我们快速指明数据按照字段进行排序。
2.1 在setting.py中配置全局排序
  • INSTALLED_APPS = [
        'rest_framework', #需要注册应用
    ]
    
  • REST_FRAMEWORK = {
         #过滤排序(全局):Filtering 过滤排序
        'SEARCH_PARAM': 'search',
        'ORDERING_PARAM': 'ordering',
        'NUM_PROXIES': None,
    }
    
2.2 使用方法:

在类视图中设置filter_backends,使用from rest_framework.filters import OrderingFilter过滤器,REST framework会在请求的查询字符串参数中检查是否包含了ordering参数,如果包含了ordering参数,则按照ordering参数指明的排序字段对数据集合进行排序。

前端可以传递的ordering参数的可选字段值需要在ordering_fields中指明。

示例:

views.py

from rest_framework.viewsets import ModelViewSet
from django_filters.rest_framework import DjangoFilterBackend
from rest_framework.filters import OrderingFilter

class UserModelViewSet(ModelViewSet):
    # queryset = User.objects.all().order_by('id')
    queryset = User.objects.all()
    serializer_class = UserSerializers
    pagination_class = PageNumberPagination

    filter_backends = [OrderingFilter,]
    
    # 排序
    ordering_fields = ('id', 'age', 'class_room') #需要排序的字段

#http://192.168.56.100:8888/user/user2?ordering=-age 使用age字段从大到小排序
#http://192.168.56.100:8888/user/user2?ordering=age  使用age字段从小到大排序
2.3 使用postman测试

3.过滤

对于列表数据可能需要根据字段进行过滤,我们可以通过添加django_filters扩展来增强支持。

pip install django_filters
3.1 在setting.py中配置
  • INSTALLED_APPS = [
        'django_filters',
        'rest_framework',
    ]
    
  • REST_FRAMEWORK = {
         # 过滤器后端
        'DEFAULT_FILTER_BACKENDS': [
            'django_filters.rest_framework.DjangoFilterBackend',
            # 'django_filters.rest_framework.backends.DjangoFilterBackend', 包路径有变化
        ],
    }
    
3.2 views.py
from rest_framework.viewsets import ModelViewSet
from django_filters.rest_framework import DjangoFilterBackend


class UserModelViewSet(ModelViewSet):
    # queryset = User.objects.all().order_by('id')
    queryset = User.objects.all()
    serializer_class = UserSerializers
    pagination_class = PageNumberPagination

    # 过滤
    filter_backends = [OrderingFilter, DjangoFilterBackend]
    filter_fields = ('name', 'age')	#参加过滤的字段

#http://192.168.56.100:8888/user/user2?name=马六
3.3 使用postman测试

4.限流 Throttling

可以对接口访问的频次进行限制,以减轻对服务器的压力

4.1 在setting.py中配置
  • INSTALLED_APPS = [
        'rest_framework',
    ]
    
  • REST_FRAMEWORK = {
             # 3.限流(防爬虫)
        'DEFAULT_THROTTLE_CLASSES': [
            'rest_framework.throttling.AnonRateThrottle',
            'rest_framework.throttling.UserRateThrottle',
        ],
        # 3.1限流策略
        'DEFAULT_THROTTLE_RATES': {
            'user': '1/hour',  # 认证用户每小时100次
            'anon': '3/day',  # 未认证用户每天能访问3次
        },
        'DEFAULT_CONTENT_NEGOTIATION_CLASS': 'rest_framework.negotiation.DefaultContentNegotiation',
        'DEFAULT_METADATA_CLASS': 'rest_framework.metadata.SimpleMetadata',
        'DEFAULT_VERSIONING_CLASS': None,
    }
    

DEFAULT_THROTTLE_RATES 可以使用 second,minute,hour或day来指明周期。

4.2 views.py
from rest_framework.throttling import AnonRateThrottle,UserRateThrottle
from rest_framework.viewsets import ModelViewSet

class UserModelViewSet(ModelViewSet):
    # queryset = User.objects.all().order_by('id')
    queryset = User.objects.all()
    serializer_class = UserSerializers
    
    #限流自定义限流类
    throttle_classes = [AnonRateThrottle]
     
    #超出限定的次数会报错
	# "detail": "Request was throttled. Expected available in 86398 seconds."
	
    
4.3 使用postman测试

5. 序列化

详情看我之前的博客:https://www.cnblogs.com/tjw-bk/p/13805532.html

6.认证

认证即需要知道是谁在访问服务器,需要有一个合法身份。认证的方式可以有很多种,例如session+cookie、token等,这里以token为例。如果请求中没有token,我们认为这是未登录状态,有些接口要求必须登录才能访问,如果未登录,我们可以一些处理(比如重定向登录页、返回错误信息等)

  • 局部认证:

url.py

from django.conf.urls import url, include
from DemoApp.views import TestView
  
urlpatterns = [
    url(r'^test/', TestView.as_view()),
]

功能代码:utils/auth.py

from rest_framework.authentication import BaseAuthentication
from rest_framework.exceptions import AuthenticationFailed,ValidationError
from DemoApp.models import User

class MyAuth(BaseAuthentication):
    def authenticate(self, request):
        "认证逻辑"
        # 做认证,判断是否登陆。首先拿到token,在数据库里去判断
        token = request.query_params.get("token","")
        if not token:
            raise AuthenticationFailed("没有token")
        try:
            user = User.objects.filter(token=token).first()
            if not user:
                pass
                raise AuthenticationFailed("无效的token")
        except Exception as e:
            msg = "无效的token" if hasattr(e, "messages") else "非法的token"
            raise AuthenticationFailed(msg)

        return (user, token) # 返回值是一个元素,分别对应视图中request的user,auth

views.py

class TestView(APIView):
    authentication_classes = [MyAuth, ]  # 局部认证

    def get(self, request, *args, **kwargs):
        "通过认证后进入views类进行业务处理"
        print(request.user)
        print(request.auth)
        pass

在上述代码中,实现了TestView视图类的认证功能。如果认证成功,向视图类中的request对象中返回requset.user和request.auth;如果认证失败,触发异常,不会进入视图处理,直接返回前端认证失败的结果。在实际的业务中这样并不合适,我们可以将其替代成重定向操作。

  • 全局认证
  • 将视图类中的authentication_classes属性删除
  • settings.py配置文件中 RESTFRAMEWORK 添加配置
6.1 在setting.py中配置
  • INSTALLED_APPS = [
        'rest_framework',
    ]
    
  • REST_FRAMEWORK = {
         # 1.认证器(全局)
        'DEFAULT_AUTHENTICATION_CLASSES': [
            'rest_framework_jwt.authentication.JSONWebTokenAuthentication',  # 在DRF中配置JWT认证
            'rest_framework.authentication.SessionAuthentication',  # 使用session时的认证器
            'rest_framework.authentication.BasicAuthentication'  # 提交表单时的认证器
        ],
    }
    

如果在全局认证下,有些接口不想加上认证,可以在这个类的属性 authentication_classes = [ ] 即可。

除了上述自己实现的认证类,REST Framework为我们了四种认证类:(直接在视图类中使用即可)

from rest_framework.authentication import BaseAuthentication,SessionAuthentication  ......
1.BasicAuthentication       基于用户名密码
2.SessionAuthentication     基于session
3.TokenAuthentication       基于token,与示例中类似
4.RemoteUserAuthentication  
6.2 views.py
from rest_framework.authentication import BasicAuthentication, SessionAuthentication
from rest_framework_jwt.authentication import JSONWebTokenAuthentication

class CourseViewSet(viewsets.ModelViewSet):
    # 1.认证:自定义认证类,自定义会覆盖全局配置
    authentication_classes = (BasicAuthentication, SessionAuthentication, JSONWebTokenAuthentication)
     

7.权限

根据不同权限用户对于api的访问频次,其他限制等等

7.1 在setting.py中配置
  • INSTALLED_APPS = [
        'rest_framework',
    ]
    
  • REST_FRAMEWORK = {
        # 2.权限配置(全局): 顺序靠上的严格
        'DEFAULT_PERMISSION_CLASSES': [
            # 'rest_framework.permissions.IsAdminUser',  # 管理员可以访问
            'rest_framework.permissions.IsAuthenticated',  # 认证用户可以访问
            # 'rest_framework.permissions.IsAuthenticatedOrReadOnly',  # 认证用户可以访问, 否则只能读取
            'rest_framework.permissions.AllowAny',  # 所有用户都可以访问
        ],
    }
    
7.2 views.py
from rest_framework.permissions import AllowAny, IsAuthenticated

class CourseTagViewSet(viewsets.ModelViewSet):
    queryset = CourseTag.objects.all()
    serializer_class = CourseTagSer

    # 2.权限:自定义权限类
    # permission_classes = (AllowAny,)
    permission_classes = (IsAuthenticated,)
    
7.3 使用postman测试

8.版本

参考博客:https://openapi.alipay.com/gateway.do?
https://www.jb51.net/article/166249.htm

posted @ 2020-11-10 09:51  就学45分钟  阅读(347)  评论(0编辑  收藏  举报