Django Rest Framework详解


Django Rest Framework介绍

DRF介绍

Django Rest Framework(简称DRF),是Django框架中实现Restful API的一个插件,可以非常方便的实现接口数据的返回。Django中也可以使用JsonResponse直接返回json格式的数据,但DRF相比直接使用Django返回json数据有以下优点:

  • 可以直接生成API文档;
  • 授权验证策略比较完整,包含OAuth1和OAuth2验证;
  • 支持ORM模型和非ORM数据的序列化;
  • 高度封装了视图,使得返回Json数据更加的高效;

在开发REST API接口时,视图主要要实现三件事:

  • 将请求的数据处理校验;
  • 操作数据库;
  • 将模型类对象转换为响应的数据

将前端发送的数据反序列化为模型类对象,并保存到数据库中;将数据库数据序列化为前端所需要的json格式并返回。

DRF操作最大的方便之处在于对序列化和反序列化的处理:

  • 增:校验请求数据 -> 执行反序列化过程 -> 操作数据库 -> 将新增的对象序列化并返回
  • 删:判断要删除数据是否存在 -> 执行数据库删除操作
  • 改:判断要修改数据是否存在 -> 校验请求数据 -> 执行反序列化过程 -> 操作数据库 -> 将修改的对象序列化并返回
  • 查:查询数据库 -> 将数据序列化并返回

安装

因为本人安装的是python3.11.6django4.2.6所以特地到官网和github上查了一下:

来自官网:https://www.django-rest-framework.org
REST framework requires the following:

Python (3.6, 3.7, 3.8, 3.9, 3.10)
Django (2.2, 3.0, 3.1, 3.2, 4.0, 4.1)
来自github:https://github.com/encode/django-rest-framework
Python 3.6+
Django 4.2, 4.1, 4.0, 3.2, 3.1, 3.0
We highly recommend and only officially support the latest patch release of each Python and Django series.
上面是主页的介绍信息,下面是详情页介绍信息
Django 2.2 is no longer supported. #8662
Django 4.1 compatibility. #8591
pip install djangorestframework

基本使用

在Pycharm中新建Django项目,配置基本环境

pip install djangorestframework
pip install mysqlclient
python manage.py startapp baituan

将drf和新建的app添加到settings.py中的INSTALLED_APPS配置中,并配置好数据库

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'rest_framework',
    'baituan'
]

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.mysql',
        'NAME': 'xxxx',
        'USER': 'root',
        'PASSWORD': 'xxxx',
        'HOST': '127.0.0.1',
        'PORT': '3306'
    }
}

在baituanapp中的models.py设计模型

from django.db import models
from django.contrib.auth.models import User


class Merchant(models.Model):
    """
    商家
    """
    name = models.CharField(max_length=200, verbose_name='商家名称', null=False)
    address = models.CharField(max_length=200, verbose_name='商家', null=False)
    logo = models.CharField(max_length=200, verbose_name='商家logo', null=False)
    notice = models.CharField(max_length=200, verbose_name='商家的公告', null=True, blank=True)
    up_send = models.DecimalField(verbose_name='起送价', default=0, max_digits=6, decimal_places=2)
    lon = models.FloatField(verbose_name='经度')
    lat = models.FloatField(verbose_name='纬度')

    created = models.ForeignKey(User, on_delete=models.SET_NULL, null=True)


class GoodsCategory(models.Model):
    """
    商家商品分类
    """
    name = models.CharField(max_length=20, verbose_name='分类名称')
    merchant = models.ForeignKey(Merchant, on_delete=models.CASCADE, verbose_name='所属商家', related_name='categories')


class Goods(models.Model):
    """
    商品
    """
    name = models.CharField(max_length=200, verbose_name='商品名称')
    picture = models.CharField(max_length=200, verbose_name='商品图片')
    intro = models.CharField(max_length=200)
    price = models.DecimalField(verbose_name='商品价格', max_digits=6, decimal_places=2)  # 最多6位数,2位小数。9999.99
    category = models.ForeignKey(GoodsCategory, on_delete=models.CASCADE, related_name='goods_list')

映射同步到数据库

python manage.py makemigrations
python manage.py migrate

导入准备好的测试数据。

例子:

views.py

from rest_framework import viewsets
from baituan.models import Merchant
from .serializers import MerchantSerializer


# viewsets相当于一些view的集合
class MerchantViewset(viewsets.ModelViewSet):
    queryset = Merchant.objects.all()
    serializer_class = MerchantSerializer

serializers.py

from rest_framework import serializers
from baituan.models import Merchant


class MerchantSerializer(serializers.ModelSerializer):
    class Meta:
        model = Merchant
        fields = "__all__"

urls.py

from rest_framework.routers import DefaultRouter
from .views import MerchantViewset

router = DefaultRouter()
router.register('merchant', MerchantViewset, basename='merchant')
app_name = 'quickstart'

urlpatterns = [] + router.urls

序列化

DRF中的序列化主要是用来将模型序列化成Json格式的对象。同时序列化还具有表单验证、数据储存和更新功能。

使用Django的方式来属性serializer

serializers.py

#!/usr/bin/env python
# -*- coding:utf-8 -*-
# author: laobai
# datetime: 2023/11/9 02:08
from rest_framework import serializers
from baituan.models import Merchant, Goods, GoodsCategory

# serializer(序列化)的作用
# 1. 用来序列化数据,将ORM转化成JSON
# 2. 用来验证表单数据。Form
# 3. 可以创建数据、修改数据等。

# serializer的构造函数的参数:
# 1. instance:需要传递一个ORM对象,或者一个queryset对象,用来将ORM模型序列化成JSON;
# 2. data:把需要验证的数据传递给data,用来验证这些数据是不是符合要求。
# 3. many:如果instance是一个queryset对象,那么就需要设置为True,否则为False
class MerchantSerializer(serializers.Serializer):
    id = serializers.IntegerField(read_only=True)
    name = serializers.CharField(max_length=200, required=True)
    logo = serializers.CharField(max_length=200, required=True)
    address = serializers.CharField(max_length=200, required=True)
    notice = serializers.CharField(max_length=200, required=False)
    up_send = serializers.DecimalField(required=False, max_digits=6, decimal_places=2)
    lon = serializers.FloatField(required=True)
    lat = serializers.FloatField(required=True)

    # DRF的源代码中serializer的update()和create()两个方法没有实现,若要调用这两个方法,需要进行重写实现
    def update(self, instance, validated_data):
        # instance是当前的实例,validated_data是传过来进过上面序列化并验证ok的值。
        # get() 传了新的有效值,就用传的值"name",没有获取到有效值,就重新赋值成原来instance中的值
        instance.name = validated_data.get("name", instance.name)
        instance.logo = validated_data.get("logo", instance.logo)
        instance.address = validated_data.get("address", instance.address)
        instance.notice = validated_data.get("notice", instance.notice)
        instance.up_send = validated_data.get("up_send", instance.up_send)
        instance.lon = validated_data.get("logo", instance.lon)
        instance.lat = validated_data.get("logo", instance.lat)
        instance.save()
        return instance

    def create(self, validated_data):
        return Merchant.objects.create(**validated_data)   # 相当于(name="laobai")

使用DRF的方式来书写serializer,上面大段的代码精简为如下:

serializer.py

from rest_framework import serializers
from baituan.models import Merchant, Goods, GoodsCategory

class MerchantSerializer(serializers.ModelSerializer):
    class Meta:
        model = Merchant
        fields = "__all__"

视图函数的代码如下:views.py

from django.http import JsonResponse
from django.views.decorators.http import require_http_methods
from baituan.models import Merchant, GoodsCategory, Goods
from .serializers import MerchantSerializer, GoodsCategorySerializer


@require_http_methods(["GET", "POST"])
def merchant(request):
    # get:返回所有商家;
    # post:创建新的商家;
    if request.method == "GET":
        # 查询全部的商家数据
        queryset = Merchant.objects.all()
        # 对商家数据进行验证 (有多条数据,many=True,只有一条数据则用默认的False)
        serializer = MerchantSerializer(instance=queryset, many=True)
        # 返回商家数据json格式(这里serializer.data是列表,所以要设置safe=False)
        return JsonResponse(serializer.data, status=200, safe=False)
    else:
        # 序列化提交的商家数据
        serializer = MerchantSerializer(data=request.POST)
        # 若商家数据是有效的,保存到数据库,并返回Json格式;若是无效的,报错。
        if serializer.is_valid():
            serializer.save()
            return JsonResponse(serializer.data, status=200)
        else:
            return JsonResponse(serializer.errors, status=400)


def category(request):
    if request.method == "GET":
        queryset = GoodsCategory.objects.all()
        serializer = GoodsCategorySerializer(instance=queryset, many=True)
        return JsonResponse(serializer.data, status=200, safe=False)
    else:
        serialize = GoodsCategorySerializer(data=request.POST)
        if serialize.is_valid():
            serialize.save()
            return JsonResponse(serialize.data, status=200)
        else:
            return JsonResponse(serialize.errors, status=400)

用postman发送请求,返回正确的结果:

同样实现商品和商品分类的序列化。

一个商品分类对应着一个商家,同时一个商品分类下可能有多个商品。这样的一个关系在返回的json中体现。

serializer.py

from rest_framework import serializers
from baituan.models import Merchant, Goods, GoodsCategory

# 序列化商家数据
class MerchantSerializer(serializers.ModelSerializer):
    class Meta:
        model = Merchant
        fields = "__all__"

# 序列化商品数据
class GoodsSerializer(serializers.ModelSerializer):
    class Meta:
        model = Goods
        fields = "__all__"

# 序列化商品分类数据,并在返回结果中体现对应的商家数据和商品数据(序列化的嵌套)
class GoodsCategorySerializer(serializers.ModelSerializer):
    # 返回数据时若要输出关联的模型数据,则需要在这里添加序列化数据。(商品分类模型有所属商家外键)
    merchant = MerchantSerializer(read_only=True)
    # 返回商品分类所对应的商品
    goods_list = GoodsSerializer(many=True, read_only=True)
    # 提交新增分类数据时,同样需要关联对应的商家
    merchant_id = serializers.IntegerField(write_only=True)

    class Meta:
        model = GoodsCategory
        fields = "__all__"

    # 提交新增分类时,需验证商家是否存在
    def validate_merchant_id(self, value):
        if not Merchant.objects.filter(pk=value).exists():
            raise serializers.ValidationError("商家不存在!")
        return value

    # 重写创建方法
    def create(self, validated_data):
        merchant_id = validated_data.get('merchant_id')
        merchant = Merchant.objects.get(pk=merchant_id)
        category = GoodsCategory.objects.create(**validated_data, merchant=merchant)
        return category

用postman发送请求,返回正确的结果:

Request和Response对象

在DRF中,可以使用RequestResponse对象来替代Django内置的HttpRequestHttpResponse

Request对象

DRF的Request对象是从HttpRequest中拓展出来的,同时增加了一些其他的属性。其中最核心的用得最多的属性便是request.datarequest.datarequest.POST更加灵活:

  • request.POST:只能处理表单数据,获取通过POST方式上传上来的数据;
  • request.data:可以处理任意的数据。可以获取通过POST、PUT、PATCH等方式上传上来的数据;
  • request.query_params:查询参数,比request.GET用起来更直白。

Response对象

Response可以自动的根据返回的数据类型来决定返回什么样的格式,并且会自动的监听如果是浏览器访问,那么会返回这个路由的信息。

状态码

Restful API中,响应的状态码是很重要的一部分。可以使用DRF提供的status标识状态。

例子:在上面例子中的views.py的requestResponsestatus替换为DRF中的代替方式

# from django.http import JsonResponse
# from django.views.decorators.http import require_http_methods
from baituan.models import Merchant, GoodsCategory, Goods
from .serializers import MerchantSerializer, GoodsCategorySerializer
from rest_framework.response import Response
from rest_framework import status
from rest_framework.decorators import api_view


# 请求类型拦截使用DRF中的from rest_framework.decorators import api_view
# request使用DRF中的request (同名,无缝替代)
# HttpResponse/JsonResponse使用DRF中的from rest_framework.response import Response 替换
# status使用from rest_framework import status 替换
# @require_http_methods(["GET", "POST"])
@api_view(['GET', 'POST'])
def merchant(request):
    # get:返回所有商家;
    # post:创建新的商家;
    if request.method == "GET":
        # 查询全部的商家数据
        queryset = Merchant.objects.all()
        # 对商家数据进行验证 (有多条数据,many=True,只有一条数据则用默认的False)
        serializer = MerchantSerializer(instance=queryset, many=True)
        # 返回商家数据json格式(这里serializer.data是列表,所以要设置safe=False)
        # return JsonResponse(serializer.data, status=200, safe=False)
        return Response(serializer.data, status=status.HTTP_200_OK)
    else:
        # 序列化提交的商家数据
        serializer = MerchantSerializer(data=request.POST)
        # 若商家数据是有效的,保存到数据库,并返回Json格式;若是无效的,报错。
        if serializer.is_valid():
            serializer.save()
            # return JsonResponse(serializer.data, status=200)
            return Response(serializer.data, status=status.HTTP_200_OK)
        else:
            # return JsonResponse(serializer.errors, status=400)
            return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)

@api_view(['GET', 'POST'])
def category(request):
    if request.method == "GET":
        queryset = GoodsCategory.objects.all()
        serializer = GoodsCategorySerializer(instance=queryset, many=True)
        # return JsonResponse(serializer.data, status=200, safe=False)
        return Response(serializer.data, status=status.HTTP_200_OK)
    else:
        serializer = GoodsCategorySerializer(data=request.POST)
        if serializer.is_valid():
            serializer.save()
            # return JsonResponse(serializer.data, status=200)
            return Response(serializer.data, status=status.HTTP_200_OK)
        else:
            # return JsonResponse(serializer.errors, status=400)
            return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)

上面举例子都是视图函数,用rest_framework.decorators.api_view装饰器修饰。而后续会经常使用类视图,通过继承rest_framework.views.APIView来实现。

改成通过DRF实现的视图后,可以在浏览器中访问接口地址:http://127.0.0.1:8000/baituan/category/ 可以清晰的查看到接口及接口数据的信息

类视图

APIView

在DRF中,推荐使用类视图,因为类视图可以通过继承的方式把一些重复性的工作抽取出来,而使得代码更加简洁。

通过类视图的形式实现views.py - merchantView

from baituan.models import Merchant, GoodsCategory, Goods
from .serializers import MerchantSerializer, GoodsCategorySerializer
from rest_framework.response import Response
from rest_framework import status
from rest_framework.decorators import api_view
from rest_framework.views import APIView
from django.http import Http404


class MerchantView(APIView):
    """
    检索、更新和删除一个merchant实例对象
    """
    def get_object(self, pk):
        try:
            return Merchant.objects.get(pk=pk)
        except Merchant.DoesNotExist:
            raise Http404

    def get(self, request, pk=None):
        if pk:
            merchant = self.get_object(pk)
            serializer = MerchantSerializer(merchant)
            return Response(serializer.data)
        else:
            queryset = Merchant.objects.all()
            serializer = MerchantSerializer(instance=queryset, many=True)
            return Response(serializer.data)

    def post(self, request):
        # 序列化提交的商家数据
        serializer = MerchantSerializer(data=request.POST)
        # 若商家数据是有效的,保存到数据库,并返回Json格式;若是无效的,报错。
        if serializer.is_valid():
            serializer.save()
            return Response(serializer.data, status=status.HTTP_200_OK)
        return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)

    def put(self, request, pk):
        merchant = self.get_object(pk)
        serializer = MerchantSerializer(merchant, data=request.data)
        if serializer.is_valid():
            serializer.save()
            return Response(serializer.data)
        return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)

    def delete(self, request, pk):
        merchant = self.get_object(pk)
        merchant.delete()
        return Response(status=status.HTTP_204_NO_CONTENT)

Mixins

mixins意思是混入、组件的意思。(这里类似热插拔)在DRF中,针对获取列表、检索、创建等操作,都有相应的mixin

通过mixins实现上面的类视图

from baituan.models import Merchant
from .serializers import MerchantSerializer
from rest_framework import mixins
from rest_framework import generics


class MerchantView(
    generics.GenericAPIView,
    mixins.ListModelMixin,  # 查询列表
    mixins.RetrieveModelMixin,  # 查询某条记录
    mixins.CreateModelMixin,  # 创建
    mixins.UpdateModelMixin,  # 更新
    mixins.DestroyModelMixin  # 删除
):
    queryset = Merchant.objects.all()
    serializer_class = MerchantSerializer

    def get(self, request, pk=None):
        if pk:
            # 返回具体的记录
            return self.retrieve(request)
        else:
            # 返回列表
            return self.list(request)

    def post(self, reqeust):
        return self.create(reqeust)

    def put(self, request, pk):
        return self.update(request, pk)

    def delete(self, request, pk):
        return self.destroy(request, pk)

GenericAPIView

上面使用mixins可以非常方便的实现一些CURD操作,不过DRF又进一步的进行了封装,放在generics下。

Generic类视图:

  • generics.ListAPIView:实现获取列表数据,实现get方法
  • generics.CreateAPIView:实现创建数据,实现post方法
  • generics.UpdateAPIView:实现更新数据,实现put方法
  • generics.DestroyAPIView:实现删除数据,实现delete方法
  • generics.RetrieveAPIView:实现检索数据
  • generics.ListCreateAPIView:实现获取列表和创建数据
  • generics.RetrieveUpdateAPIView:实现检索和更新数据
  • generics.RetrieveDestroyAPIView:实现检索和删除数据
  • generics.RetrieveUpdateDestroyAPIView:实现检索、更新及删除数据

List和Retrieve都占用了get方法,也就是在实现类的时候,只能继承这两个中的一个,另外一个需要新建一个类单独实现。

实现上面例子的views.py可以通过GenericsAPI精简为如下:

from baituan.models import Merchant
from .serializers import MerchantSerializer
from rest_framework import generics


class MerchantView(
    generics.CreateAPIView,  # 继承了对应的类,即表示下方代码已经实现了对应的方法CreateAPIView对应的是POST
    generics.UpdateAPIView,
    generics.DestroyAPIView,
    generics.RetrieveAPIView,
    # generics.ListAPIView
):
    serializer_class = MerchantSerializer
    queryset = Merchant.objects.all()


class MerchantListView(generics.ListAPIView):
    serializer_class = MerchantSerializer
    queryset = Merchant.objects.all()

其中:

  • queryset是用来控制视图返回给前端的数据。若没有逻辑可以直接写在视图的属性中。若逻辑比较复杂,那就需要重写get_queryset,重写后,再要获取queryset就调用get_queryset
  • serializer_class用来验证和序列化数据的,也是可以通过直接设置这个属性,也可以通过重写get_serializer_class来实现。

对应的urls.py

from django.urls import path
from .views import MerchantView

urlpatterns = [
    # 列表:/merchant/     get
    # 新增:/merchant/     post
    # 详情:/merchant/[pk]/    get
    # 修改:/merchant/[pk]/    put
    # 删除:/merchant/[pk]/    delete
    path("merchant/", MerchantView.as_view()),
    path("merchant/<int:pk>/", MerchantView.as_view()),
]

在进行retrieve时,是按照默认的主键PK进行检索的,若要根据其他条件检索,可以用lookup_field来设定。比如lookup_field="name"就是更改了默认的检索条件为按名字检索,对应的urls中的设置也要修改merchant/<str:name>/

在遇到复杂逻辑时,可以重写以下方法来实现:

  • get_queryset(self):用于动态的返回一个queryset对象;
  • get_object(self):用于在数据检索的时候,返回一条数据;
  • perform_create(self, serializer):保存对象的时候调用;
  • perform_update(self, serializer):更新对象的时候调用;
  • perform_destroy(self, serializer):删除对象的时候调用。

ViewSet

ViewSet视图集,相当于视图的一个集合。在视图集中,不再有getpost,取而代之的是listcreate

DRF通过使用ViewSet可以进一步简化代码,这回主要简化的是urls路由。

views.py

from baituan.models import Merchant
from .serializers import MerchantSerializer
from rest_framework import viewsets
from rest_framework.decorators import action
from rest_framework.response import Response


class MerchantViewSet(viewsets.ModelViewSet):
    queryset = Merchant.objects.all()
    serializer_class = MerchantSerializer

    # 上面两行代码实现了list,retrieve,update,destroy的操作
    # 下面自定义增加查询商家名称中包含"肯德基"的商铺
    @action(['GET'], detail=False, url_path='kfc')  # 设置自定义路由(第一个参数是请求方法,第二个参数detail是否使用主键查询,第三个参数路由url)
    def kaifengcai(self, request):
        queryset = self.get_queryset()
        result = queryset.filter(name__contains="肯德基")
        serializer_class = self.get_serializer_class()
        serializer = serializer_class(result, many=True)
        return Response(serializer.data)

urls.py

from .views import MerchantViewSet
from rest_framework.routers import DefaultRouter

router = DefaultRouter()
router.register("merchant", MerchantViewSet, basename="merchant")
urlpatterns = [] + router.urls

自定义路由的url:http://127.0.0.1:8000/baituan/merchant/kfc/

以上,是类视图的主要方式:APIViewMixinsGenericAPIViewViewSet,在之后的项目开发中,基本以ViewSet的实现方式为主。

权限认证

认证

认证可以简单的理解为登录和访问需要登录的接口的认证。只要认证通过了,那么在request对象(DRF的request对象)上便有两个属性:request.userrequest.auth。前者就是django中的User对象,后者根据不同的认证机制有不同的对象。DRF内置了几个认证的模块。

  • rest_framework.authentication.BasicAuthentication:基本的授权,每次都要在Header中把用户名和密码传给服务器,因此不是很安全,不能在生产环境中使用。
  • rest_framework.authentication.SessionAuthentication:基于django的session机制实现的。如果前端部分是网页,用这个是可以的。若前端是ios或android的app,可以用,但不太方便。
  • rest_framework.authentication.TokenAuthentication:基于token的认证机制。只要登录完成后便会返回一个token,以后请求一些需要登录的api,就通过传递这个token就可以了,并且这个token是存储在服务器的数据库中。但这个token方式有一个缺点,就是没有自动的过期机制,一旦登录完成,这个token是永久有效的。这样不安全。

JSON Web Token认证机制

JSON Web Token简称JWT。在前后端分离的项目(包括APP类型的项目)中,推荐使用JWTJWT是在成功后,把用户的相关信息及过期时间进行加密,然后生成一个token返回给客户端,客户端拿到后可以存储起来,以后每次请求的时候都携带这个token,服务器在接收到需要登录的API请求是,对这个token进行解密,然后获取过期时间和用户信息,如果过期了或用户信息不对,那么都会认证失败。

使用JWT需要先安装pyjwt

pip install pyjwt

在上面的例子基础上,在classview目录下新建authentications.py

import time
import jwt
from django.conf import settings
from rest_framework.authentication import BaseAuthentication, get_authorization_header
from rest_framework import exceptions
from django.contrib.auth import get_user_model

User = get_user_model()


# 生成JWT Token
def generate_jwt(user):
    timestamp = int(time.time()) + 60 * 60 * 24 * 7  # 过期时间为一周
    # jwt.encode返回的是bytes数据类型,需要decode解码成str类型
    return jwt.encode({"userid": user.pk, "exp": timestamp}, settings.SECRET_KEY).decode("utf-8")


# 解码JWT Token
class JWTAuthentication(BaseAuthentication):
    """
    Authorization: JWT Token
    """
    keyword = "JWT"
    model = None

    def authenticate(self, request):
        auth = get_authorization_header(request).split()

        if not auth or auth[0].lower() != self.keyword.lower().encode():
            return None

        if len(auth) == 1:
            msg = "Authorization 不可用!"
            raise exceptions.AuthenticationFailed(msg)

        elif len(auth) > 2:
            msg = "Authorization 不可用!"
            raise exceptions.AuthenticationFailed(msg)

        try:
            jwt_token = auth[1]
            jwt_info = jwt.decode(jwt_token, settings.SECRET_KEY)
            userid = jwt_info.get("userid")
            try:
                user = User.objects.get(pk=userid)
                return (user, jwt_token)
            except:
                msg = "用户不存在!"
                raise exceptions.AuthenticationFailed(msg)
        except jwt.ExpiredSignatureError:
            msg = "Token已经过期!"
            raise exceptions.AuthenticationFailed(msg)

在views.py中增加对jwt token生成和使用

from baituan.models import Merchant
from .serializers import MerchantSerializer
from rest_framework import viewsets
from rest_framework.decorators import action
from rest_framework.response import Response

from .authentications import generate_jwt, JWTAuthentication
from django.contrib.auth import get_user_model
from rest_framework.decorators import api_view

User = get_user_model()


class MerchantViewSet(viewsets.ModelViewSet):
    queryset = Merchant.objects.all()
    serializer_class = MerchantSerializer

    # 用来验证用户是否已经成功登录
    authentication_classes = [JWTAuthentication]


# 模拟生成user表中第一个用户的token
@api_view(["GET"])
def token_view(request):
    token = generate_jwt(User.objects.first())
    return Response({"token": token})

权限

通常在系统中,不同的API拥有不同的访问权限。即需要用到权限来进行API的管理。DRF自带了4种权限:

  • permissions.AllowAny:允许所有人访问;
  • permissions.IsAuthenticated:登录的用户即可访问(request.user and request.user.is_authenticated
  • permissions.IsAdminUser:管理员权限
  • permissions.IsAuthenticatedOrReadOnly:是登录用户,并且这个API是只能读(GETOPTIONSHEAD

在上面代码的基础上增加权限控制

from rest_framework.permissions import IsAuthenticated, AllowAny, IsAdminUser, IsAuthenticatedOrReadOnly

class MerchantViewSet(viewsets.ModelViewSet):
    queryset = Merchant.objects.all()
    serializer_class = MerchantSerializer

    # 用来验证用户是否已经成功登录
    authentication_classes = [JWTAuthentication]
    # 用来根据用户的权限来限制访问
    permission_classes = [IsAuthenticated]

自定义权限

当DRF自带的权限无法满足需要时,可以自定义权限。自定义权限要遵循两个条件:

  • 继承permissions.BasePermission
  • 实现has_permission(self, request, view) 或者是has_object_permission(self, request,view, obj)。第一个方法用来管理整个视图的访问权限,第二个方法用来管理某个对象的访问权限(譬如用户只能修改自己的用户信息)。

permissions.py定义权限

from rest_framework import permissions
class IsOwnerOrReadOnly(permissions.BasePermission):
    def has_object_permission(self, request, view, obj):
        if request.method in permissions.SAFE_METHODS:
            return True
        return obj.owner == request.user

权限的使用有两种方式,第一种是在settings.REST_FRAMEWORK.DEFAULT_PERMISSION_CLASSES设置,即全局设定。第二种是在具体的视图函数中通过permission_classes来设置

settings.py

REST_FRAMEWORK = {
    'DEFAULT_PERMISSION_CLASSES': [
        'rest_framework.permissions.IsAuthenticated',
    ]
}

views.py

class MerchantView(APIView):
    permission_classes = [IsAuthenticated]
    def get(self, request, format=None):
        content = {
            'status': 'request was permitted'
        }
        return Response(content)

文章作者: 老百
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 老百 !
  目录