[Study notes] 3 hours to get the DRF framework | Django REST framework front-end separation framework practice

3 hours to get the DRF framework | Django REST framework front-end separation framework practice

Preface (based on INSCODE AI assistant)

DRF (full name Django REST framework) is a powerful tool set for building Web API. It is a Python Web framework based on Django. It provides developers with a set of tools for rapid development of RESTful API. It can automate API visualization, documentation It realizes automated testing of interfaces and automated API routing, serialization, views, verification, paging, version management, authentication, and other functions. DRF simplifies the development of APIs and provides a series of tools to build high-quality APIs.

1.1: Course Introduction

Learning objective: Use DRF to develop a RESTful API interface
Learning content: Serialization, view set, routing, authentication, authority
Course effect: Various views of DRF realize the addition, deletion, modification and query of course information

2.1: Understand the front-end and back-end separation from four aspects

  • interactive form
    insert image description here

RESTful API is an agreed interface specification between the front and back ends of i

  • Code organization mode
    The front and back ends are not separated: django template syntax
    The front and back ends are semi-separated
    The front and back ends are separated
  • Development mode
    The front and back ends are not separated Project development process:
    insert image description here
    front and rear ends are separated Project development process:
    insert image description here
  • Data interface specification process
    insert image description here

2.2 In-depth understanding of what is RESTful API

You can learn from other people's interface specifications.
For example:
github-API
insert image description here
insert image description here
Deep understanding of what is RESTful API

2.3 Pycharm builds project development environment

  • Settings.py
# 因为RESTframework是有前端文件的,后面使用时会全部放到这个目录
STATIC_ROOT = os.pardir.join(BASE_DIR, "static")
STATICFILES_DIRS = [
    os.path.join(BASE_DIR, "staticfiles")
]
  • manage.py
makemigrations
migrate
createsuperuser

insert image description here

2.4 Framework introduction and installation

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

Perform the following steps according to the documentation:

  • 1. Install the corresponding package
pip install djangorestframework
pip install markdown       # Markdown support for the browsable API.
pip install django-filter  # Filtering support
  • Modify the configuration in settings.py
# Add 'rest_framework' to your INSTALLED_APPS setting.

INSTALLED_APPS = [
    ...
    "rest_framework", # 用于开发RESTful API
    'rest_framework.authtoken', # DRF自带的Token认证
]

All relevant configurations for rest framework are placed in a dictionary of 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',
}
  • Modify the configuration in urls.py
urlpatterns = [
    ...
    path('api-auth/', include('rest_framework.urls')) # DRF的登录退出
]
  • DRF's 20 modules
    serialization, view, routing, authentication, permissions, current limiting, filtering, parser, renderer, paging, version, test, summary, exception handling, configuration, verification, status code, content negotiation, caching, metadata

3.1 Develop course information model classes

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

Register the field to the backend to facilitate adding data from the admin backend
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 What is serialization

Serialization is also called a serializer, which refers to converting the django data type of query set QuerySet or model class instance instance into json or xml, converting
queryset or instance into json/xml/yaml that can be easily rendered by the front end,
so serialization And deserialization is to
deeply analyze Django's serializer for front-end and back-end data interaction

  • Start the project, enter the admin background, and insert data
    insert image description here
    insert image description here

  • Test django's serializer in python console

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快速入门"}}]'

But django's serializer still has a lot to improve
1. Validation processing: In the process of deserialization, when the data passed by the front end is placed in request.data, it must be converted into a model class instance and then saved. At this time, the data submitted by the front end needs to be verified.
2. Parameters of the validator
3. Serialize multiple objects at the same time
4. Add context during serialization
5. No exception handling for invalid data
6. The foreign key field of the relational field only returns the id, which needs to be handled by ourselves
The serializer of relational field drf can help us solve these problems well.

3.3 How to inherit ModelSerializer to serialize model classes

Python learning———serializer (review The serialization method of
building a serializes.py model class under the application is very similar to the writing method of form during django development . First review the form class for the model class

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')

The serialization of the model class is almost the same, except that it inherits from serializers.ModelSerializer
teacher is a foreign key, so if you want to get the name corresponding to the lecturer, you need to serialize the user's name.
The user table associated with the teacher is not a new user when creating a new course, so this field can be set to read-only 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__"

The same method serializes the user model class.
ModelSerializer is a higher level of encapsulation. It inherits from Serializer. To achieve more flexible customization, it can inherit from Serializer.
Here we only serialize model classes, which is relatively simple, so it can be used directly

from django.contrib.auth.models import User

class UserSerializer(serializers.ModelSerializer):

    class Meta:
        model = User
        fields = "__all__"

For the serialization of foreign key fields, you can specify the depth of traversal.
If the table structure has child tables associated with the parent table, and the parent table is associated with another parent table, you can set the depth

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 HyperlinkedModelSerializer with URL

Assuming that when requesting course information, it is hoped that each record of course information can jump to the details page

DRF-related configurations are all written in the REST_FRAMEWORK dictionary in settings.py
Inherited from HyperlinkedModelSerializer
fields Add url
url is the default value, if you want to change it to another name, such as "link", you can set URL_FIELD_NAME in settings.py to use Take effect globally

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's views develop RESTful API interface

Use four different ways to interface with Restful API through DRF

  • functional programming
  • class view
  • Generic class view
  • Viewsets for DRF

in the views.py file

4.1.1 Django's native view-functional programming API interface

The idea is to judge what method this is, and then deal with it

@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

Need to pay attention: post request, need to use decorator to lift csrf restriction

4.1.2 Django's native view-class view programming API interface

The idea of ​​writing CBV is to use corresponding functions to process different request methods

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

The method decorator can be imported, and the decoration method is dispatch, because according to the request response principle of django, when the request comes, it first reaches the dispatch method, and after being processed by the dispatch method, it finds the post method

The above two methods are to directly use the native Django to write the interface, and there are many things that need to be implemented from scratch.
Such as paging, sorting, authentication, permissions, current limiting, etc.
Then there is a view of DRF, which integrates the common functions of these interface development

4.2 DRF decorator api_view - functional programming

4.2.1 Implement the interface of "get all course information or add a new course"

  • New fbv view of drf
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)

Functional programming will use the decorator
partial=True to indicate partial updates

  • new route

In order to facilitate the management of routes under the course app, create a urls.py file under the course app, and then include it in the urls of the project through include
django urls include usage

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'))
]

insert image description here

The api of HyperlinkedModelSerializer is not used first, because HyperlinkedModelSerializer is a highly integrated serialization class, which can be used for programming with drf view set later. So the serializer class uses instead:

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  # 深度
  • return result
    insert image description here
  • test post request
    insert image description here

4.2.2 Implement the interface of "obtaining a course information or updating and deleting a course information"

  • Realize the view
@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)
  • implement routing
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 How to use Postman to test the API interface (I am apipost here)

Here I take apipost as an example

verification method:

  • basic auth authentication: account name, password authentication

  • api key authentication: token authentication
    insert image description here

  • OAuth 2.0, etc.

To add some keys and values ​​to the request header, you can click header
query, which is a parameter on the url
. The request.data parsed in the back-end code is usually placed in raw

  • Post request
    insert image description here
  • Get request
    insert image description here

4.4 View APIView in DRF - class view programming

4.4.1 Implement the interface of "get all course information or add a new course"

  • import APIView
from rest_framework.views import APIView
  • Implement class view
"""二、类视图 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)
  • new route
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')
]
  • test request
    insert image description here
    insert image description here

4.4.2 Implement the interface of "details page"

Static method: Whether it is called directly through a class object or an instance object, it is possible to call it. It should be noted that instance attributes and methods cannot be used in static methods. Python static methods, instance methods, and class
methods

  • implement the interface
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)
  • implement routing
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'),
]
  • test request
    insert image description here
    insert image description here
    insert image description here

The class view programming method is almost the same as the functional programming method, and the code is almost the same. The next section explains a more concise way of writing.

4.5 The generic class view GenericAPIView in DRF

import generics

from rest_framework import generics

4.5.1 Realize viewing and requesting new interfaces

The general class view actually mixes the classes corresponding to some common addition, deletion, modification and query operations through mixin.
The name of the attribute here is fixed
Here you need to overload the perform_create method

4.5.2 Implement the interface of course details

  • Implement a generic class view

The following writing is equivalent to class GCourseDetail(generics.RetrieveUpdateDestroyAPIView):
but for the following method, the request method is no longer GET, PUT, PATCH, DELETE, but retrieve, update, destroy, etc., so it is recommended to useclass 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
  • new route
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'),
]
  • test request
    insert image description here

Other things are done in the general class view: total number (count), next page (next), previous page (previous), data (results)
insert image description here
insert image description here

4.6 DRF's viewsets development interface for addition, deletion, modification and query of course information - Viewset

  • import viewsets
from rest_framework import viewsets
  • create viewset
"""四、DRF的视图集"""
class CourseViewSet(viewsets.ModelViewSet):
    queryset = Course.objects.all()
    serializer_class = CourseSerializer
    
    def perform_create(self, serializer):
        serializer.save(teacher=self.request.user)

Only four lines of code are required without permission authentication

4.7 Django URLs and DRF Routers

  • new route

The way of writing is different from the previous
way of writing the routes of the two view sets

    • 1 traditional writing
    # 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 routing routers
  • import
from rest_framework.routers import DefaultRouter
router = DefaultRouter() # 把类实例化
router.register(prefix="viewsets", viewset=views.CourseViewSet) # 把视图集注册到这个路由实例里面

urlpatterns = [
    path("", include(router.urls))
]
  • test interface
    insert image description here

5.1 Introduction to DRF Authentication Methods

Three commonly used authentication methods in DRF:
1.BasicAuthentication: username and password authentication
2.SessionAuthentication: Session authentication
3.TokenAuthentication: Token authentication
insert image description here

You can also implement a custom authentication scheme, or use a third-party package, such as json webtoken (about jwt authentication, which will be explained in the next drf advanced practical course). The difference between authentication and authority: authentication is the identity of
user
login Perform verification
Permission refers to which interfaces a user who has passed the login verification can access; or for a certain interface, what level of data he can get
According to the response process of Django restframework to http request, identity authentication occurs Before permission check and current limit check

Authentication and permissions, the core depends on two data: request.user, request.auth
The authentication mechanism actually relies on the multiple authentication classes
in Django's auth framework
'DEFAULT_AUTHENTICATION_CLASSES': [ 'rest_framework.authentication.BasicAuthentication', 'rest_framework.authentication.SessionAuthentication', 'rest_framework.authentication.TokenAuthentication', ],
, and DRF will authenticate each class in order from top to bottom . And use the first successfully passed authentication class to return the value to set request.user and request.auth

5.1.1 BasicAuthentication

If basicauthentication is set, the headers will have an additional authorization, which is the ciphertext calculated by the base64 library of the user name and password, which is automatically completed by postman. The base64 library is not confidential and can be decrypted. So basicauthentication is generally used for testing work, not for production environment

  • Test, after passing basicauthentication authentication, what request.user and request.auth return
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'>
  • Check what information is in the response header
    Password is correct
    insert image description here
    Password is wrong

If the authentication fails, there will be an additional WWW_Authenticate field in the response header
insert image description here

5.1.2 SessionAuthentication

The default session backend of Django is used. Usually, session authentication can be used when HTTP and ajax requests are used in the front-end js. When using this, a csrf token needs to be provided to the backend.

5.1.3 TokenAuthentication

Is a simple, token-based http authentication method

  • Add INSTALLED_APPS in settings.py'rest_framework.authtoken'

will generate a data table

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',
]
  • The way to generate token

5.1.3.1 Use Django manag.py to generate Token

Can only be used by administrators to test
Manually generate tokens

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

insert image description here

5.1.3.2 Using Django's signal mechanism to generate Token

Purpose:
To automatically generate tokens,
users can request tokens through the interface

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():

You can also directly import django's user model class as the authentication model class: from django.contrib.auth.models import User
Principle: When a User model class creates a new instance and saves it, that is, when a new user is created, because the save method is called to save, the post_save
signal will be transmitted To the receiver, after the receiver accepts, it will execute the function generate_token
When executing the function, instance refers to the newly created user instance, created=True, so a record is generated in the authtoken_token table

How can the user obtain the token generated for the user?

rest_framework has been written for us, we can call it directly

In total route 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'))
]
  • Test - create a new user
    insert image description here
    user01 - mima123456
    insert image description here
    , you can see that there are new records in the token table
    insert image description here
  • Test, get token through interface through postman
    insert image description here
  • Authentication successful
    insert image description here
    request header
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'>

If the authentication is successful, request.user is still an instance of the Django user class
request.auth is an instance of the Token class,
and there is no authentication-related information in the response header

  • Authentication failed
    insert image description here
  • To use a different authentication method for a certain interface

No matter what view programming method is used, there are corresponding setting methods. The following are four different view programming methods, which are explained separately.

    • 1. For functional programming
      , import the decoration class for authentication:
from rest_framework.decorators import api_view, authentication_classes
from rest_framework.authentication import BasicAuthentication, SessionAuthentication, TokenAuthentication

Decorators are ordered

A class decorator that only adds password authentication to the object

@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)
  • test
    insert image description here

Pay attention to the wording, it must beToken xxx


    • 2. Add class attributes directly to class view programmingauthentication_classes
class CourseList(APIView):

    authentication_classes = (BasicAuthentication, SessionAuthentication, TokenAuthentication)

The same is true for general class views and view sets, and they also have class attributes

5.3 DRF permission control

What are the commonly used permission classes? How should permission policies be set? How to customize object level permissions?

The permissions of DRF are all in the permissions module. The process of permission checking is the same as the identity authentication explained above, and the identity authentication information in the request.user and request.auth attributes is also used to determine whether the request is passed in. ,

5.3.1 Global permission classes can be set in settings.py

insert image description here

  • Common permission classes are:

IsAuthenticatedOrReadOnly: Logged-in users can add, delete, modify and query, and non-logged-in users can query
IsAuthenticated: Only logged-in users can perform all operations, non-logged-in users cannot perform all operations, and get does not work. IsAdminUser
: admin users have permission to access the interface, right? Admin looks at the is_staff field of the auth_user table, true means it is an admin level user
AllowAny: Allow all requests

5.3.2 How to set permissions on interface/object level

  • import decorator
from rest_framework.decorators import api_view, authentication_classes, permission_classes
  • Import permission class
在这里插入代码片

5.3.2.1 Set permission for function view

  • Add a decorator to the function view
@api_view(["GET", "POST"])
@authentication_classes((BasicAuthentication, ))  # 对象级别的认证设置,优先于全局设置
@permission_classes((IsAuthenticated, )) # 自定义的权限类
def course_list(request):

5.3.2.2 Setting Permissions for Class Views/General Class Views/View Sets

  • directly into the class attributeauthentication_classes
permission_classes = (IsAuthenticated, )

5.3.3 How to customize object-level permissions?

The implementation can only modify its own information, and other people's information can only be viewed, not modified.

  • Create permission.py under the application and create a custom permission class
    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

Overridden has_object_permissionmethods
Safe methods ("GET", "HEAD", "OPTIONS")can be permissions.SAFE_METHODSreplaced with

insert image description here

  • Import the created custom permission class into the view
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)
  • test interface
    insert image description here
    insert image description here

6.1 How to generate API interface documents

6.1.1 How to generate API interface documents

You can view the format and other information of a single interface by requesting the url. But is there a way to display all interface information, that is, the API online documentation,
so that it can be easily viewed by the front-end, and it is clear at a glance
The following operations must firstpip install coreapi

  • 1. Add to REST_FRAMEWORK of settings.py
REST_FRAMEWORK = {
    
    
    'DEFAULT_SCHEMA_CLASS': 'rest_framework.schemas.AutoSchema',
    ...
    }
  • 2. Add routing in the total urls.py
    first import get_schema_view
    and then create get_schema_view instance
    and then create routing
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),
]
  • test
    insert image description here

It is recommended to download the jsonView plugin in the Chrome App Store

Click GET-corejson
insert image description here

insert image description here

But the document is still not beautiful enough, the next section introduces the general function of using coreapi

6.2 Introduction to the general usage of DRF

How should coreapi be configured and how to use the document.

  • Modify the 'DEFAULT_SCHEMA_CLASS' property of REST_FRAMEWORK
    insert image description here
  • in urls.py
    • 1. Import the module and add a new route
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')),
]
  • test
    insert image description here

In this API document, after the authentication is configured, it needs to be reconfigured after refreshing

7.1 Course Summary

  • Which view development should be used?
    Use functional view to write flexibly, but this is a process-oriented way, and the repetition rate is high.
    Using class view, you can use the characteristics of classes in python, encapsulation, inheritance, polymorphism, and reduce code repetition. General class view is more
    flexible. Low
    The view set is highly customized, and the flexibility is the least.
    Generally, class view programming is the main method.

  • How does DRF work? APIView source code? test? cache? Limiting. . . didn't talk about it

Guess you like

Origin blog.csdn.net/zhangyifeng_1995/article/details/131898576
Recommended