首页 > 其他分享 >3小时搞定DRF框架 | Django REST framework前后端分离框架实践

3小时搞定DRF框架 | Django REST framework前后端分离框架实践

时间:2024-04-15 19:34:53浏览次数:28  
标签:框架 course request rest Django framework REST import data

DRF(全称Django REST framework)是一个用于构建 Web API 的强力工具集,是一个基于Django的Python Web框架,它为开发人员提供了一套快速开发 RESTful API 的工具,它能够自动化 API 可视化、文档化,实现接口的自动化测试以及自动化的API路由、序列化、视图、验证、分页、版本管理、认证等等功能。DRF简化了API的开发,并提供了一系列的工具来构建高质量的API。

 

#1.1:介绍

目标:使用DRF开发RESTful API接口

内容:序列化、视图集、路由、认证、权限

效果:DRF的多种视图实现课程信息的增删改查

 

#2.1: 从四个方面理解前后端分离

交互形式

 

 

```RESTful API是i前后端约定好的接口规范```

 

代码组织方式

前后端不分离:django模板语法

前后端半分离

前后端分离

开发模式 前后端不分离项目开发流程:

 

 

前后端分离项目开发流程:

 

 

数据接口规范流程

 

 

2.2 深入理解什么是RESTful API

可以学习别人的接口规范学习。

例如:

比较 GitHub 的 REST API 和 GraphQL API - GitHub 文档

 

 

(转)深入理解什么是RESTful API_慕课手记 (imooc.com)

 

 

##2.3 Pycharm搭建项目开发环境

```

Settings.py

# 因为RESTframework是有前端文件的,后面使用时会全部放到这个目录

STATIC_ROOT = os.pardir.join(BASE_DIR, "static")

STATICFILES_DIRS = [

    os.path.join(BASE_DIR, "staticfiles")

]

 ```

manage.py

makemigrations  migrate  createsuperuser

 

 

 

##2.4 framework介绍和安装

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

 

按照文档执行下面步骤:

 

1.安装相应包

```

pip install djangorestframework

pip install markdown       # Markdown support for the browsable API.

pip install django-filter  # Filtering support

 ```

```

在settings.py中修改配置

# Add 'rest_framework' to your INSTALLED_APPS setting.

 

INSTALLED_APPS = [

    ...

    "rest_framework", # 用于开发RESTful API

    'rest_framework.authtoken', # DRF自带的Token认证

]

 

对于rest framework所有相关配置都放在settings的一个字典里面

 

# DRF的全局配置

REST_FRAMEWORK = {

    'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.PageNumberPagination',  # 自带分页类

    'PAGE_SIZE': 50,  # 每页数据量

    'DATETIME_FORMAT': '%Y-%m-%d %H:%M:%S',  # 时间显示格式

    'DEFAULT_RENDER_CLASSES': [

        'rest_framework.renders.JSONRenderer',

        'rest_framework.renders.BrowsableAPIRenderer',

    ],  # 返回response对象所用的类

    'DEFAULT_PARSER_CLASSES': [

        'rest_framework.parsers.JSONParser',

        'rest_framework.parsers.FormParser',

        'rest_framework.parsers.MultiPartParser',

    ],  # 解析器,如何解析request请求中的request.data

    'DEFAULT_PERMISSION_CLASSES': [

        'rest_framework.permissions.IsAuthenticated',

    ],  # 权限相关配置

    'DEFAULT_AUTHENTICATION_CLASSES': [

        'rest_framework.authentication.BasicAuthentication',

        'rest_framework.authentication.SessionAuthentication',

        'rest_framework.authentication.TokenAuthentication',

    ],  # 认证相关配置

    "URL_FIELD_NAME": 'link',

}

`````` 

在urls.py中修改配置

urlpatterns = [

    ...

    path('api-auth/', include('rest_framework.urls')) # DRF的登录退出

]

 ```

DRF的20个模块

序列化、视图、路由、认证、权限、限流、过滤、解析器、渲染器、分页、版本、测试、概要、异常处理、配置、验证、状态码、内容协商、缓存、元数据

##3.1 开发课程信息模型类

models.py

 ```

from django.db import models

 

# Create your models here.

from django.db import models

from django.conf import settings

 

 

class Course(models.Model):

    name = models.CharField(max_length=255, unique=True, help_text='课程名称', verbose_name='名称')

    introduction = models.TextField(help_text='课程介绍', verbose_name='介绍')

    teacher = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE, help_text='课程讲师', verbose_name='讲师')

    price = models.DecimalField(max_digits=6, decimal_places=2, help_text='课程价格')

    created_at = models.DateTimeField(auto_now_add=True, verbose_name='创建时间')

    update_at = models.DateTimeField(auto_now=True, verbose_name='更新时间')

 

    class Meta:

        verbose_name = "课程信息"

        verbose_name_plural = verbose_name

        ordering = ('price',)

 

    def __str__(self):

        return self.name

``` 

将字段注册到后台,方便从admin后台添加数据

admin.py

 ```

from django.contrib import admin

from .models import Course

 

 

# Register your models here.

 

@admin.register(Course)

class CourseAdmin(admin.ModelAdmin):

    list_display = ('name', 'introduction', 'teacher', 'price')

    search_fields = list_display

    list_filter = list_display

 ```

##3.2 什么是序列化

序列化也叫序列化器,是指把查询集QuerySet或者模型类实例instance这种django的数据类型转化为json或者xml

把queryset或者instance转化为方便前端可以轻松渲染的json/xml/yaml

所以序列化和反序列化是为了前后端数据交互

深度剖析 Django 的 serializer

 

启动项目,进入admin后台,插入数据

 

 

 

 

在python console中测试django的序列化器

 ```

from course.models import Course

from django.core import serializers

 

serializers.serialize('json', Course.objects.all())

# '[{"model": "course.course", "pk": 1, "fields": {"name": "Django REST framework快速入门", "introduction": "快速入门Django REST framework,学会开发一套自己的Restful API服务,并且自动生成API文档", "teacher": 1, "price": "9.99", "created_at": "2023-07-28T10:11:46.882", "update_at": "2023-07-28T10:11:46.882"}}]'

 

# 如果只需要序列化一部分字段

serializers.serialize('json', Course.objects.all(), fields=("name"))

# '[{"model": "course.course", "pk": 1, "fields": {"name": "Django REST framework快速入门"}}]'

 ```

但django的序列化器还有很多需要完善的地方

1.验证处理:在反序列化的过程中,当前端传递过来的数据放在request.data里时,要转化为模型类实例,然后保存。这时需要对前端提交的数据进行验证。

2.验证器的参数

3.同时序列化多个对象

4.序列化的过程中添加上下文

5.没有对无效的数据异常处理

6.关系型字段的外键字段只返回了id,需要我们自己处理关系型字段

drf的序列化器可以帮我们很好的解决这些问题。

 

##3.3 如何继承ModelSerializer序列化模型类

Python学习————序列化器(复习

在应用下建一个serializes.py

模型类的序列化的写法和django开发时form的写法非常类似

先回顾给模型类建form表单类

 ```

from django import forms

from rest_framework import serializers  # 导入序列化器

 

from .models import Course

 

 

class CourseForm(forms.ModelForm):

    class Meta:

        model = Course

        fields = ('name', 'introduction', 'teacher', 'price')

 ```

模型类序列化的写法几乎一样,只是继承自serializers.ModelSerializer

teacher是外键,所以想得到讲师对应的名字,需要把用户的名称序列化出来。

teacher关联到的用户表,并不是新建课程时新建一个用户,所以该字段可以设为只读ReadOnlyField

 ```

class CourseSerializer(serializers.ModelSerializer):

    teacher = serializers.ReadOnlyField(source='teacher.username') # 外键字段、只读

   

    class Meta:

        model = Course  # 写法和上面的CourseForm类似

        # exclude = ('id', ) # 注意元组中只有1个元素时不能写成("id")

        # fields = ('name', 'introduction', 'teacher', 'price')

        fields = "__all__"

 ```

同样方法序列化用户模型类

ModelSerializer是更高一级的封装,它继承自Serializer,要实现比较灵活的自定义,可以继承自Serializer

我们这里只序列化模型类,比较简单,所以可以直接用

 ```

from django.contrib.auth.models import User

 

class UserSerializer(serializers.ModelSerializer):

 

    class Meta:

        model = User

        fields = "__all__"

 ```

对外键字段的序列化,可以指定遍历的深度

如果表结构有子表关联到父表,父表又关联到另外的父表,就可以设置深度

 ```

class CourseSerializer(serializers.ModelSerializer):

    teacher = serializers.ReadOnlyField(source='teacher.username')  # 外键字段、只读

 

    class Meta:

        model = Course  # 写法和上面的CourseForm类似

        # exclude = ('id', ) # 注意元组中只有1个元素时不能写成("id")

        # fields = ('name', 'introduction', 'teacher', 'price')

        fields = "__all__"

        depth = 2  # 深度

 ```

3.4 带URL的HyperlinkedModelSerializer

假设请求课程信息时,希望每条课程信息的记录,都能跳转到详情页

 

DRF相关的配置,都写在settings.py中的REST_FRAMEWORK的字典中

继承自HyperlinkedModelSerializer

fields里加上url

url是默认值,如果要改成其他名称,如“link”,可在settings.py中设置URL_FIELD_NAME使全局生效

 

settings.py

 ```

# DRF的全局配置

REST_FRAMEWORK = {

    'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.PageNumberPagination',  # 自带分页类

    'PAGE_SIZE': 50,  # 每页数据量

    'DATETIME_FORMAT': '%Y-%m-%d %H:%M:%S',  # 时间显示格式

    'DEFAULT_RENDER_CLASSES': [

        'restframework.renders.JSONRenderer',

        'restframework.renders.BrowsableAPIRenderer',

    ],  # 返回response对象所用的类

    'DEFAULT_PARSER_CLASSES': [

        'rest_framework.parsers.JSONparser',

        'rest_framework.parsers.Formparser',

        'rest_framework.parsers.MultiPartparser',

    ],  # 解析器,如何解析request请求中的request.data

    'DEFAULT_PERMISSION_CLASSES': [

        'rest_framework.permissions.IsAuthenticated',

    ],  # 权限相关配置

    'DEFAULT_AUTHENTICATION_CLASSES': [

        'rest_framework.authentication.BasicAuthentication',

        'rest_framework.authentication.SessionAuthentication',

        'rest_framework.authentication.TokenAuthentication',

    ],  # 认证相关配置

    "URL_FIELD_NAME": 'link',

}

 ```

serializers.py

 ```

class CourseSerializer(serializers.HyperlinkedModelSerializer):

    teacher = serializers.ReadOnlyField(source='teacher.username')  # 外键字段、只读

 

    class Meta:

        model = Course  # 写法和上面的CourseForm类似

        # url是默认值,可在settings.py中设置URL_FIELD_NAME使全局生效

        # fields = ('id', 'url', 'name', 'introduction', 'teacher', 'price', 'created_at', 'update_at')

        fields = ('id', 'link', 'name', 'introduction', 'teacher', 'price', 'created_at', 'update_at')

 ```

##4.1 Django的views开发RESTful API接口

通过DRF使用四种不同的方式来Restful API接口

 

函数式编程

类视图

通用类视图

DRF的视图集

在views.py文件中

 

###4.1.1 Django原生的view-函数式编程编写API接口

思路就是判断这个是什么方法,然后进行处理

 ```

@csrf_exempt

def course_list(request):

    course_dict = {

        'name': '课程名称',

        'introduction': '课程介绍',

        'price': 0.11

    }

    if request.method == "GET":

        # return HttpResponse(json.dumps(course_dict), content_type='application/json')

        return JsonResponse(course_dict) # 这两种写法是等价的

    if request.method == "POST":

        course = json.loads(request.body.decode('utf-8'))

        return JsonResponse(course, safe=False) # 如果解析的数据不是字典类型,是字符串的话,需要使用safe=false

 ```

需要注意:post请求,需要使用装饰器来解除csrf限制

 

###4.1.2 Django原生的view-类视图编程编写API接口

CBV的编写思路是对不同的请求方法用对应的函数处理

 ```

import json

 

from django.http import JsonResponse, HttpResponse

from django.views import View

from django.views.decorators.csrf import csrf_exempt

from django.utils.decorators import method_decorator

 

# Django CBV 编写API接口

@method_decorator(csrf_exempt, name='dispatch')

class CourseList(View):

    def get(self, request):

        return JsonResponse(course_dict)

 

    @csrf_exempt

    def post(self, request):

        course = json.loads(request.body.decode('utf-8'))

        return JsonResponse(course, safe=False)  # 如果解析的数据不是字典类型,是字符串的话,需要使用safe=false

 ```

可以导入方法装饰器,装饰的方法是是dispatch,因为根据django的请求响应原理,当request请求来的时候,它首先到达dispatch方法,通过dispatch方法处理后,再找到post方法

 

上面的两种方式,是直接使用原生的Django写接口,有很多东西都需要自己从零开始实现。

比如分页、排序、认证、权限、限流等等

于是就有了DRF的视图,集成了这些接口开发常用的功能

 

##4.2 DRF的装饰器api_view - 函数式编程

###4.2.1 实现“获取所有课程信息或新增一个课程”的接口

新建drf的fbv视图

```

from rest_framework.decorators import api_view

from rest_framework.response import Response # django使用的是JsonResponse或HttpResponse,而drf直接封装好了,就是Response

from rest_framework import status

from .models import Course

from .serializers import CourseSerializer

 

"""一、函数式编程 Function Based View"""

@api_view(["GET", "POST"])

def course_list(request):

    """

    获取所有课程信息或新增一个课程

    :param request:

    :return:

    """

    if request.method == "GET":

        s = CourseSerializer(instance=Course.objects.all(), many=True) # 序列化多个对象,所以需要many=True

        # 序列化后的数据可以通过s.data获得

        return Response(data=s.data, status=status.HTTP_200_OK)

    elif request.method == "POST":

        s = CourseSerializer(data=request.data, partial=True) # 反序列化, partial=True表示部分更新

        if s.is_valid():

            s.save(teacher=request.user) # 讲师是只读属性

            return Response(data=s.data, status=status.HTTP_201_CREATED)

        return Response(s.erros, status=status.HTTP_400_BAD_REQUEST)

 ```

函数式编程会用到装饰器

partial=True表示部分更新

 

新建路由

为了方便管理course这个app下的路由,在course这个应用下创建一个urls.py文件,然后通过include包含到项目的urls中

django urls include用法

 

course/urls.py

 ```

from django.urls import path

from course import views

 

urlpatterns = [

    # Function Based View

    path("fbv/list/", views.course_list, name="fbv-list")

]

 ```

drf_tutorial/urls.py

 ```

urlpatterns = [

    path('api-auth/', include('rest_framework.urls')), # DRF的登录退出

    path("admin/", admin.site.urls),

    path('course/', include('course.urls'))

]

 ```

 

 

先不用HyperlinkedModelSerializer的api了,因为HyperlinkedModelSerializer是高度集成的序列化类,后面使用drf的视图集来编程可以用。所以序列化类改用:

 ```

class CourseSerializer(serializers.ModelSerializer):

    teacher = serializers.ReadOnlyField(source='teacher.username')  # 外键字段、只读

 

    class Meta:

        model = Course  # 写法和上面的CourseForm类似

        # exclude = ('id', ) # 注意元组中只有1个元素时不能写成("id")

        # fields = ('name', 'introduction', 'teacher', 'price')

        fields = "__all__"

        depth = 2  # 深度

 ```

返回结果

 

 

测试post请求

 

 

###4.2.2 实现“获取一个课程信息或更新删除某个课程信息”的接口

实现视图```

@api_view(["GET", "PUT", "DELETE"])

def course_detail(request, pk): # 通过url传递pk

    """

    获取、更新、删除一个课程

    :param request:

    :param pk:

    :return:

    """

    try:

        course = Course.objects.get(pk=pk)

    except Course.DoesNotExist:

        return Response(data={"msg": "没有此课程信息"}, status=status.HTTP_404_NOT_FOUND)

    else:

        if request.method == "GET":

            s = CourseSerializer(instance=course) # 序列化一个对象不需要many=True

            return Response(data=s.data, status=status.HTTP_200_OK)

 

        elif request.method == "PUT": # PUT方法表示更新,部分写法和POST方法类似

            s = CourseSerializer(instance=course, data=request.data) #instance是指要序列化哪个实例,data表示数据哪里来的

            # 表示把data的数据,反序列化之后,保存或者更新到cuorse对象里

            if s.is_valid():

                s.save() # 不需要teacher字段

                return Response(s.data, status=status.HTTP_200_OK)

 

        elif request.method == "DELETE":

            course.delete()

            return Response(status=status.HTTP_204_NO_CONTENT)

 ```

实现路由```

urlpatterns = [

    # Function Based View

    path("fbv/list/", views.course_list, name="fbv-list")

    path("fbv/detail/<int:pk>", views.course_detail, name="fbv-detail")

]

 ```

4.3 如何使用Postman测试API接口(我这里是apipost)

我这里以apipost为例

 

认证方式:

 

basic auth认证:账户名、密码认证

 

api key认证:token认证

 

 

 

OAuth 2.0等

 

要在请求头加一些key,value,可以点header

query是url带上的参数

后端代码里解析的request.data,一般是放在raw里面

 

Post请求

 

 

Get请求

 

 

4.4 DRF中的视图APIView - 类视图编程

4.4.1 实现“获取所有课程信息或新增一个课程”的接口

导入APIView

from rest_framework.views import APIView

 

实现类视图```

"""二、类视图 Class Based View"""

class CourseList(APIView):

    def get(self, request):

        """

        :param request:

        :return:

        """

        queryset = Course.objects.all()

        s = CourseSerializer(instance=queryset, many=True)

        return Response(s.data, status=status.HTTP_200_OK)

 

    def post(self, request):

        """

        :param request:

        :return:

        """

        s = CourseSerializer(data=request.data)

        if s.is_valid():

            s.save(teacher=self.request.user)

            print(type(request.data), type(s.data))

            # type(request.data): <class 'dict'>

            # type(s.data): <class 'rest_framework.utils.serializer_helpers.ReturnDict'>

            return Response(data=s.data, status=status.HTTP_201_CREATED)

        return Response(s.errors, status=status.HTTP_400_BAD_REQUEST)

 ```

新建路由```

urlpatterns = [

    # Function Based View

    path("fbv/list/", views.course_list, name="fbv-list"),

    path("fbv/detail/<int:pk>/", views.course_detail, name="fbv-detail"),

 

    # Class Based View

    path("cbv/list/", views.CourseList.as_view(), name='cbv-list')

]

 ```

测试请求

 

 

4.4.2 实现“详情页”的接口

静态方法:无论是通过类对象直接调用还是实例对象进行调用都是可以的,需要注意的是在静态方法中无法使用实例属性和方法

python静态方法、实例方法、类方法使用

 

实现接口```

class CourseDetail(APIView):

    @staticmethod  # 静态方法

    def get_object(pk):

        """

        :param pk:

        :return:

        """

        try:

            return Course.objects.get(pk=pk)

        except Course.DoesNotExist:

            return

 

    def get(self, request, pk):

        """

        :param request:

        :param pk:

        :return:

        """

        obj = self.get_object(pk=pk)

        if not obj:

            return Response(data={"msg": "没有此课程信息"}, status=status.HTTP_404_NOT_FOUND)

        s = CourseSerializer(instance=obj)

        return Response(s.data, status=status.HTTP_200_OK)

 

    def put(self, request, pk):

        obj = self.get_object(pk=pk)

        if not obj:

            return Response(data={"msg": "没有此课程信息"}, status=status.HTTP_404_NOT_FOUND)

        s = CourseSerializer(instance=obj, data=request.data)

        if s.is_valid():

            s.save()

            return Response(data=s.data, status=status.HTTP_200_OK)

        return Response(s.errors, status=status.HTTP_400_BAD_REQUEST)

 

    def delete(self, request, pk):

        """

        :param request:

        :param pk:

        :return:

        """

        obj = self.get_object(pk=pk)

        if not obj:

            return Response(data={"msg": "没有此课程信息"}, status=status.HTTP_404_NOT_FOUND)

        obj.delete()

        return Response(status=status.HTTP_204_NO_CONTENT)

 

 ```

实现路由

urlpatterns = [

    # Function Based View

    path("fbv/list/", views.course_list, name="fbv-list"),

    path("fbv/detail/<int:pk>/", views.course_detail, name="fbv-detail"),

 

    # Class Based View

    path("cbv/list/", views.CourseList.as_view(), name='cbv-list'),

    path("cbv/detail/<int:pk>/", views.CourseDetail.as_view(), name='cbv-detail'),

]

 

测试请求

 

 

 

类视图编程方法和函数式编程的编写方法,代码几乎一样。下一小节讲解更加简洁的编写方法。

 

4.5 DRF中的通用类视图GenericAPIView

导入generics

 

from rest_framework import generics

1

4.5.1 实现查看和请求新增的接口

通用类视图,实际上是把一些常见的增删改查操作对应的类通过mixin把他混合在一起。

这里属性的名称是固定的

这里需要重载perform_create方法

 

4.5.2 实现课程详情的接口

实现通用类视图

下面的写法等价于class GCourseDetail(generics.RetrieveUpdateDestroyAPIView):

不过对于下面的方法,请求的方法不再是GET、PUT、PATCH、DELETE,而是retrieve,update,destory等,所以还是建议用class GCourseDetail(generics.RetrieveUpdateDestroyAPIView):

 ```

from rest_framework import mixins

from rest_framework.generics import GenericAPIView

 

 

class GCourseDetail(generics.mixins.RetrieveModelMixin,

                                   mixins.UpdateModelMixin,

                                   mixins.DestroyModelMixin,

                                   GenericAPIView):

    queryset = Course.objects.all()

    serializer_class = CourseSerializer

 ```

新增路由```

urlpatterns = [

    # Function Based View

    path("fbv/list/", views.course_list, name="fbv-list"),

    path("fbv/detail/<int:pk>/", views.course_detail, name="fbv-detail"),

 

    # Class Based View

    path("cbv/list/", views.CourseList.as_view(), name='cbv-list'),

    path("cbv/detail/<int:pk>/", views.CourseDetail.as_view(), name='cbv-detail'),

 

    # Generic Class Based View

    path("gcbv/list/", views.GCourseList.as_view(), name='gcbv-list'),

    path("gcbv/detail/<int:pk>/", views.GCourseDetail.as_view(), name='gcbv-detail'),

]

 ```

测试请求

 

通用类视图里面还做好了其他事情:总个数(count)、下一页(next)、上一页(previous)、数据(results)

 

 

 

4.6 DRF的viewsets开发课程信息的增删改查接口 - 视图集

导入viewsets

from rest_framework import viewsets

 

创建视图集

"""四、DRF的视图集"""

class CourseViewSet(viewsets.ModelViewSet):

    queryset = Course.objects.all()

    serializer_class = CourseSerializer

   

    def perform_create(self, serializer):

        serializer.save(teacher=self.request.user)

 

不做权限认证只需要四行代码

 

4.7 Django的URLs与DRF的Routers

新增路由

写法和前面不一样

两种视图集的路由写法

 

1 传统写法

    # DRF viewsets

    path("viewsets/", views.CourseViewSet.as_view(

        {"get": "list", "post": "create"}

    ), name="viewsets-list"),

    path("viewsets/<int:pk>/", views.CourseViewSet.as_view(

        {"get": "retrieve", "put": "update", "patch": "partial_update", "delete": "destory"}

    ), name="viewsets-detail")

 

2.DRF的路由routers

导入

from rest_framework.routers import DefaultRouter

router = DefaultRouter() # 把类实例化

router.register(prefix="viewsets", viewset=views.CourseViewSet) # 把视图集注册到这个路由实例里面

 

urlpatterns = [

    path("", include(router.urls))

]

 

测试接口

 

5.1 DRF认证方式介绍

DRF常用的三种认证方式:

1.BasicAuthentication:用户名密码认证

2.SessionAuthentication:Session认证

3.TokenAuthentication:Token认证

 

 

也可以实现自定义的认证方案,或者使用第三方的包,比如json webtoken(关于jwt的认证,会在接下来的drf进阶实战课程讲解)

认证和权限的区别:

认证是对用户登录的身份进行校验

权限指的是一个登录验证通过的用户,他能够访问哪些接口;或者对于某一个接口,他能够拿到什么级别的数据

根据Django restframework对http request的响应流程来看,身份认证是发生在权限检查和限流检查之前的

 

认证和权限,核心依赖两个数据:request.user, request.auth

认证机制实际上依赖的是Django的auth框架

'DEFAULT_AUTHENTICATION_CLASSES': [ 'rest_framework.authentication.BasicAuthentication', 'rest_framework.authentication.SessionAuthentication', 'rest_framework.authentication.TokenAuthentication', ],

中的多个认证类,DRF会按顺序从上往下对每个类进行认证。并使用第一个成功通过的认证类来返回值,来设置request.user和request.auth

 

5.1.1 BasicAuthentication

设置basicauthentication,则headers会多一个authorization,它是用户名密码经过base64库计算出来的密文,这是postman自动完成的。base64库不具有机密性,是可以解密的。所以basicauthentication一般用于测试工作,不用于生产环境

 

测试,通过basicauthentication认证后,request.user和request.auth返回什么

print(self.request.user, self.request.auth)

print(type(self.request.user), type(self.request.auth))

# admin None

# <class 'django.contrib.auth.models.User'> <class 'NoneType'>

 

查看响应头有什么信息

密码正确

 

密码错误

认证失败的话会在响应头里面多一个WWW_Authenticate字段

 

 

5.1.2 SessionAuthentication

使用的是Django默认的会话后端,通常在前端js里使用http,ajax请求时可以使用session认证,使用这个时,需要给后端提供csrf令牌的。

 

5.1.3 TokenAuthentication

是一种简单的,基于令牌的http认证方式

 

在settings.py的INSTALLED_APPS加上'rest_framework.authtoken'

会生成一张数据表

 

INSTALLED_APPS = [

    "django.contrib.admin",

    "django.contrib.auth",

    "django.contrib.contenttypes",

    "django.contrib.sessions",

    "django.contrib.messages",

    "django.contrib.staticfiles",

    "rest_framework",  # 用于开发RESTful API

    'rest_framework.authtoken',  # DRF自带的Token认证

    'course.apps.CourseConfig',

]

 

生成token的方式

5.1.3.1 使用Django manag.py生成Token

只能供管理员测试

手动生成token

 

python manage.py drf_create_token admin #给admin建一个token

 

 

 

5.1.3.2 使用Django的信号机制生成Token

目的:

自动生成token

用户可以通过接口请求到token

 

view.py中

 

from django.db.models.signals import post_save # 保存之后,即用户已经建好之后的信号

from django.conf import settings

from django.dispatch import receiver # 接收信号

from rest_framework.authtoken.models import Token

 

@receiver(post_save, sender=settings.AUTH_USER_MODEL)

def generate_token():

 

 

也可以直接导入django的user模型类作为认证模型类:from django.contrib.auth.models import User

原理:当User模型类新建一个实例并把并保存的时候,即新建一个用户的时候,因为保存要调用save方法

保存之后,post_save信号会传递给receiver,receiver接受之后,就会执行函数generate_token

执行函数时,instance是指新建的用户实例,created=True,于是在authtoken_token表中生成一条记录

 

给用户生成的token,用户如何获取到呢?

 

rest_framework已经帮我们写好了,我们直接调用即可

 

在总的路由urls.py中

 

from django.contrib import admin

from django.urls import path, include

from rest_framework.authtoken import views

 

urlpatterns = [

    path("api-token-auth/", views.obtain_auth_token), # 获取token的接口

    path('api-auth/', include('rest_framework.urls')), # DRF的登录退出

    path("admin/", admin.site.urls),

    path('course/', include('course.urls'))

]

 

测试-新建用户

 

user01 - mima123456

 

可以看到token表中有新增记录

 

测试,通过postman,通过接口获取token

 

认证成功

 

请求头

print(self.request.user, self.request.auth)

print(type(self.request.user), type(self.request.auth))

# user01 e1e4eb90e389feb76c8bb71ad73e3b3c63e8090c

# <class 'django.contrib.auth.models.User'> <class 'rest_framework.authtoken.models.Token'>

 

认证成功的话,request.user依旧是django的user类实例

request.auth是Token类实例

响应头里面没有和认证有关的信息

 

认证失败

 

要对某一个接口,去使用不同的认证的方式

不论使用什么视图编程方式,都有对应的设置方法。下面按照四种不同的视图编程方式,分别讲解。

 

1.对函数式编程

导入对于认证的装饰类:

from rest_framework.decorators import api_view, authentication_classes

from rest_framework.authentication import BasicAuthentication, SessionAuthentication, TokenAuthentication

1

2

装饰器是有顺序的

 

对象上只添加密码认证的类装饰器

 

@api_view(["GET", "POST"])

@authentication_classes((BasicAuthentication, )) # 对象级别的认证设置,优先于全局设置

def course_list(request):

    """

    获取所有课程信息或新增一个课程

    :param request:

    :return:

    """

    if request.method == "GET":

        s = CourseSerializer(instance=Course.objects.all(), many=True)  # 序列化多个对象,所以需要many=True

        # 序列化后的数据可以通过s.data获得

        return Response(data=s.data, status=status.HTTP_200_OK)

    elif request.method == "POST":

        s = CourseSerializer(data=request.data, partial=True)  # 反序列化, partial=True表示部分更新

        if s.is_valid():

            s.save(teacher=request.user)  # 讲师是只读属性

            return Response(data=s.data, status=status.HTTP_201_CREATED)

        return Response(s.erros, status=status.HTTP_400_BAD_REQUEST)

 

测试

 

注意写法,必须是Token xxx

 

2.对类视图编程

直接加入类属性authentication_classes

class CourseList(APIView):

 

    authentication_classes = (BasicAuthentication, SessionAuthentication, TokenAuthentication)

 

通用类视图、视图集也是一样的,也都有类属性

 

5.3 DRF的权限控制

常用的权限类有哪些?应该如何设置权限策略?如何自定义对象级别权限?

 

DRF的权限都是在permissions模块里面,权限检查的过程和前面讲解的身份认证一样,也是会使用request.user和request.auth属性中的身份认证信息来决定请求是否传入。、

 

5.3.1 在settings.py中可以设置全局的权限类

 

 

常用权限类有:

IsAuthenticatedOrReadOnly: 登录的用户可以增删改查,不登陆的用户可以查询

IsAuthenticated:只有登录的用户可以进行所有操作,不登陆的用户不能进行所有操作,get也不行

IsAdminUser:admin用户有权限访问接口,是不是admin是看auth_user表的is_staff字段,true表示是admin级别用户

AllowAny:允许所有请求

 

5.3.2 如何在接口上/对象级别设置权限

导入装饰器

from rest_framework.decorators import api_view, authentication_classes, permission_classes

1

导入权限类

在这里插入代码片

1

5.3.2.1 给函数视图设置权限

给函数视图加上装饰器

@api_view(["GET", "POST"])

@authentication_classes((BasicAuthentication, ))  # 对象级别的认证设置,优先于全局设置

@permission_classes((IsAuthenticated, )) # 自定义的权限类

def course_list(request):

 

5.3.2.2 给类视图/通用类视图/视图集设置权限

直接加入类属性authentication_classes

permission_classes = (IsAuthenticated, )

1

5.3.3 如何自定义对象级别权限?

实现只能修改自己的信息,别人的信息只可以查看,不能修改。

 

在应用下面建立permission.py,并创建自定义的权限类

permission.py

class IsOwnerReadOnly(permissions.BasePermission):

    """

    自定义权限:只有对象的所有者可以增删改查

    """

 

    def has_object_permission(self, request, view, obj):  # 重写BasePermission的方法

        """

        所有的request请求都有读权限,因此一律允许GET/HEAD/OPTIONS方法

        如果用户是自己,可以修改

        :param request:

        :param view:

        :param obj:

        :return: bool

        """

        if request.method in permissions.SAFE_METHODS:

            return True

        # 对象的所有者才有写权限

        return obj.teacher == request.user

重写has_object_permission方法

安全的方法("GET", "HEAD", "OPTIONS")可以用permissions.SAFE_METHODS代替

 

 

 

将创好的自定义权限类导入视图

from .permission import IsOwnerReadOnly

 

class GCourseList(generics.ListCreateAPIView):

    queryset = Course.objects.all()

    serializer_class = CourseSerializer

    permission_classes = (IsAuthenticated, )

 

    def perform_create(self, serializer):

        serializer.save(teacher=self.request.user)

 

 

class GCourseDetail(generics.RetrieveUpdateDestroyAPIView):

    queryset = Course.objects.all()

    serializer_class = CourseSerializer

    permission_classes = (IsAuthenticated, IsOwnerReadOnly)

测试接口

 

 

6.1 如何生成API接口文档

6.1.1 如何生成API接口文档

通过请求url可以查看单个接口的格式等信息。但是有没有方法展示所有接口信息,即API在线文档

这样能方便前端查看,一目了然

下面操作必须要先pip install coreapi

 

1.在settings.py的REST_FRAMEWORK中加入

REST_FRAMEWORK = {

    'DEFAULT_SCHEMA_CLASS': 'rest_framework.schemas.AutoSchema',

    ...

    }

2.在总的urls.py中添加路由

先导入get_schema_view

再创建get_schema_view实例

再创建路由

from rest_framework.schemas import get_schema_view

 

schema_view = get_schema_view(title="DRF API文档", description="xxx")

 

urlpatterns = [

    path("api-token-auth/", views.obtain_auth_token), # 获取token的接口

    path('api-auth/', include('rest_framework.urls')), # DRF的登录退出

    path("admin/", admin.site.urls),

    path('course/', include('course.urls')),

    path('schema/', schema_view),

]

测试

 

建议在Chrome应用商店下载jsonView插件

 

点击GET-corejson

 

 

 

 

但是文档还是不够美观,下一节介绍使用coreapi的概要功能

 

6.2 DRF的概要使用方法介绍

coreapi应该如何配置,以及文档的使用方法。

 

修改REST_FRAMEWORK的’DEFAULT_SCHEMA_CLASS’属性

 

在urls.py中

1.导入模块,新增路由

from rest_framework.documentation import include_docs_urls

 

urlpatterns = [

    path("api-token-auth/", views.obtain_auth_token),  # 获取token的接口

    path('api-auth/', include('rest_framework.urls')),  # DRF的登录退出

    path("admin/", admin.site.urls),

    path('docs/', include_docs_urls(title='DRF API文档', description='Django REST framework快速入门')),

    path('course/', include('course.urls')),

]

测试

 

在这个API文档中,认证配置好之后,刷新之后需要重新配置

 

7.1 课程总结

应该使用哪种视图开发?

使用函数式视图编写灵活,但是这是面向过程的方式,重复率高

使用类视图,可以用到python中类的特性,封装、继承、多态,减少代码重复率

通用类视图,灵活度就比较低了

视图集高度定制化,灵活度最低

一般以类视图编程为主。

 

DRF的运行机制?APIView源码?测试?缓存?限流。。。都没有讲到

                       

原文链接:https://blog.csdn.net/zhangyifeng_1995/article/details/131898576

标签:框架,course,request,rest,Django,framework,REST,import,data
From: https://www.cnblogs.com/dreammooncy/p/18136737

相关文章

  • Pytorch分类模型的训练框架
    Pytorch分类模型的训练框架PhotoDataset数据集是自己定义的数据集,数据集存放方式为:----image文件夹--------0文件夹--------------img1.jpg--------------img2.jpg--------1文件夹--------------img1.jpg--------------img2.jpg....如果是cpu训练的话,就把代码中的.cu......
  • flask框架基础(1)
    flask基础一.开发模式flask是b/s(浏览器开发)开发模式二.flask七行代码fromflaskimportFlaskapp=Flask(_name_)@app.route("/")defindex():retun"打开此网页"if_name_=='_name':app.run()三.flask核心1.werkzeug负责后端2.jinja2负责前端......
  • 十款优质企业级Java微服务开源项目(开源框架,用于学习、毕设、公司项目,减少开发工作!)
     Java微服务开源项目前言一、pig二、zheng三、SpringBlade四、SOP五、matecloud六、mall七、jeecg-boot八、Cloud-Platform九、microservices-platform十、RuoYi-Cloud 前言这篇文章为大家推荐几款优质的Java开源项目框架,可以用于学习,毕业设计,公司项目......
  • Java微服务框架一览
    Java微服务框架一览微服务在开发领域的应用越来越广泛,因为开发人员致力于创建更大、更复杂的应用程序,而这些应用程序作为微小服务的组合能够更好地得以开发和管理。这些微小的服务可以组合在一起工作,并实现更大、应用更广泛的功能。现在出现了很多的工具来满足使用逐段法而不......
  • ABP -Vnext框架一步一步入门落地教程——使用ABP -Vnext创建一个WEBAPI接口(二)
    人生需要指引,而复制是成功最快的方式,兄弟们让我们发车吧————代码大牛ljy开发主题:何谓开发应用服务端在官方开发教程这一段的内容叫做开发应用服务端,作为现在前后端分离的开发模式来说,一个应用就分为前端页面框架和后端API,页面框架调用WEBAPI实现业务就完事了。所以咱们今天......
  • RAG 工具和框架介绍: Haystack、 LangChain 和 LlamaIndex
     Haystack、LangChain和LlamaIndex,以及这些工具是如何让我们轻松地构建RAG应用程序的? 我们将重点关注以下内容:HaystackLangChainLlamaIndex增强LLM那么,为什么会有这些工具存在呢?如你所知,ChatGPT和其他LLM是在某个时间点之前的一组数据上进行训练的。更重要的是,它......
  • XAML UI 框架横向对比(Avalonia/Uno Platform/.NET MAUI)
    XAML框架横向对比多年来,基于XAML的UI框架有了很大的发展。下面的图表很好地证明了这个观点。XAMLUI框架的三大巨头:AvaloniaUI、UnoPlatform和.NETMAUI都支持跨平台的应用。事实上,除了AvaloniaUI,对跨平台XAML的需求是它们发展的主要动力。如果微软早一点介入,在几......
  • HTML 高级用法 iframe框架
    HTML中,<iframe>标签允许一个HTML文档被嵌入到另一个HTML文档中。这种技术可以用于多种用途,比如嵌入第三方内容(例如YouTube视频或Google地图)、实现网页中的多窗口布局,或者加载异步内容而不影响主页面的加载时间。<iframe>的高级用法可以增强网站的功能性和用户体验,但也需要注意使......
  • 有关ORM框架的新观点
    1、对于1:11:NN:N的场景,视图的观点是不恰当的,因为有外键关系在,在EF或者typeorm,prisma中可以使用A.B.C结合include方式进行懒加载方式的获取值,尤其在多对多场景,分页变得复杂,视图不可取2、注意懒加载,并能看到SQL的执行方式。实在不行,就写SQL,子查询,也不能用视图。3、视图存在的......
  • RestTemplate进行https请求时适配信任证书
    转载请注明出处:1.http协议请求使用RestTemplate进行http协议的请求时,不需要考虑证书验证相关问题,以下为使用RestTemplate直接使用的代码示例:importorg.springframework.web.client.RestTemplate;importorg.springframework.http.ResponseEntity;importorg.spring......