Django 构建RESTful

1、安装环境

$ pip install django==1.11.5

$ pip install djangorestframework==3.6.4

创建应用

$ django-admin startproject restful01

$ cd restful01

$ python manage.py startapp toys

在setting.py中加入toys

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

app.py 中django.apps.AppConfig代表Django application和配置

调试工具

安装curl

安装postman

安装stoplight,用于多语言

安装iCurlHTTP,用于移动设备

2、创建数据模型、序列化和反序列化

toys/models.py中创建模型

class Toy(models.Model):
    created = models.DateTimeField(auto_now_add=True)
    name = models.CharField(max_length=150, blank=False, default='')
    description = models.CharField(max_length=2250, blank=True, default='')
    toy_category = models.CharField(max_length=200, blank=False, default='')
    release_date = models.DateTimeField()
    was_included_in_home = models.BooleanField(default=False)

    class Meta:
        ordering = ('name', )

$ python manage.py makemigrations toys

$ python manage.py migrate

序列化toys/serializers.py

from rest_framework import serializers
from toys.models import Toy

class ToySerializer(serializers.Serializer):
    pk = serializers.IntegerField(read_only=True)
    name = serializers.CharField(max_length=150)
    description = serializers.CharField(max_length=250)
    release_date = serializers.DateTimeField()
    toy_category = serializers.CharField(max_length=200)
    was_included_in_home = serializers.BooleanField(required=False)

    def create(self, validated_data):
        return Toy.objects.create(**validated_data)  # create Toy model

    def update(self, instance, validated_data):
        instance.name = validated_data.get('name', instance.name)
        instance.description = validated_data.get('description', instance.description)
        instance.release_date = validated_data.get('release_date', instance.release_date)
        instance.toy_category = validated_data.get('toy_category', instance.toy_categoryv)
        instance.was_included_in_home = validated_data.get('was_included_in_home', instance.was_included_in_home)
        instance.save()
        return instance
$ python manage.py shell  创建两个实例
from datetime import datetime
from django.utils import timezone
from django.utils.six import BytesIO
from rest_framework.renderers import JSONRenderer
from rest_framework.parsers import JSONParser
from toys.models import Toy
from toys.serializers import ToySerializer

# 创建两个实例
toy_release_date = timezone.make_aware(datetime.now(),
timezone.get_current_timezone())
toy1 = Toy(name='Snoopy talking action figure',
    description='Snoopy speaks five languages',
    release_date=toy_release_date,
    toy_category='Action figures',
    was_included_in_home=False)
toy1.save()
toy2 = Toy(name='Hawaiian Barbie',
    description='Barbie loves Hawaii',
    release_date=toy_release_date, toy_category='Dolls',
    was_included_in_home=True)
toy2.save()

print(toy1.pk)
print(toy1.name)
print(toy1.created)
print(toy1.was_included_in_home)
print(toy2.pk)
print(toy2.name)
print(toy2.created)
print(toy2.was_included_in_home)

序列化

serializer_for_toy1 = ToySerializer(toy1)
print(serializer_for_toy1.data)

serializer_for_toy2 = ToySerializer(toy2)
print(serializer_for_toy2.data)

将序列化数据封装成JSON

json_renderer = JSONRenderer()
toy1_rendered_into_json = json_renderer.render(serializer_for_toy1.data)
toy2_rendered_into_json = json_renderer.render(serializer_for_toy2.data)
print(toy1_rendered_into_json)
print(toy2_rendered_into_json)

反序列化过程,将JSON数据转换成Toy实例

json_string_for_new_toy = '{"name":"Clash Royale play set","description":"6 figures from Clash Royale", "release_date":"2017-10-09T12:10:00.776594Z","toy_category":"Playset","was_included_in_home":false}'
json_bytes_for_new_toy = bytes(json_string_for_new_toy, encoding="UTF-8")
stream_for_new_toy = BytesIO(json_bytes_for_new_toy)
parser = JSONParser()
parsed_new_toy = parser.parse(stream_for_new_toy)
print(parsed_new_toy)

new_toy_serializer = ToySerializer(data=parsed_new_toy)
if new_toy_serializer.is_valid():
    toy3 = new_toy_serializer.save()  # 保存数据库
    print(toy3.name)

3、创建API视图

toys/views.py

from django.shortcuts import render
from django.http import HttpResponse
from django.views.decorators.csrf import csrf_exempt

from rest_framework.renderers import JSONRenderer
from rest_framework.parsers import JSONParser
from rest_framework import status
from toys.models import Toy
from toys.serializers import ToySerializer

class JSONResponse(HttpResponse):
    def __init__(self, data, **kwargs):
        content = JSONRenderer().render(data)
        kwargs['content_type'] = 'application/json'
        super(JSONResponse, self).__init__(content, **kwargs)

@csrf_exempt
def toy_list(request):
    if request.method == 'GET':
        # 获取所有数据
        toys = Toy.objects.all()
        toys_serializer = ToySerializer(toys, many=True) # many表示多个实例
        return JSONResponse(toys_serializer.data)
    elif request.method == 'POST':
        # 添加数据
        toy_data = JSONParser().parse(request)
        toys_serializer = ToySerializer(data=toy_data)
        if toys_serializer.is_valid():
            toys_serializer.save()
            return JSONResponse(toys_serializer.data,
                                status=status.HTTP_201_CREATED)
        return JSONResponse(toys_serializer.errors,
                            status=status.HTTP_400_BAD_REQUEST)

@csrf_exempt
def toy_detail(request, pk):
    try:
        toy = Toy.objects.get(pk=pk)
    except Toy.DoesNotExist:
        return HttpResponse(status=status.HTTP_404_NOT_FOUND)

    if request.method == 'GET':
        toy_serializer = ToySerializer(toy)
        return JSONResponse(toy_serializer.data)
    elif request.method == 'PUT': # 更新数据
        toy_data = JSONParser().parse(request)
        toy_serializer = ToySerializer(toy, data=toy_data)
        if toy_serializer.is_valid():
            toy_serializer.save()
            return JSONResponse(toy_serializer.data)
        return JSONResponse(toy_serializer.errors,
                            status=status.HTTP_400_BAD_REQUEST)
    elif request.method == 'DELETE':
        toy.delete()
        return HttpResponse(status=status.HTTP_204_NO_CONTENT)

为视图函数创建路由URL

toys/urls.py

from django.conf.urls import url
from toys import views
urlpatterns = [
    url(r'^toys/$', views.toy_list),
    url(r'^toys/(?P<pk>[0-9]+$', views.toy_detail),
]

restful01/urls.py

from django.conf.urls import url, include
urlpatterns = [
    url(r'^', include('toys.urls')),
]

启动服务 $ python manage.py runserver

使用postman去测试API

4、使用APIView类创建API

使用APIView能够减少不少代码

rest_framework.serializers.ModelSerializer默认实现create和update。

toys/serializers.py

from rest_framework import serializers
from toys.models import Toy

class ToySerializer(serializers.ModelSerializer):
    class Meta:
        model = Toy
        fields = ('id',
                  'name',
                  'description',
                  'release_date',
                  'toy_category',
                  'was_included_in_home')

重新测试API

在views.py中函数中加入@api_view,处理异常

5、可浏览的API

rest_framework.response.BrowsableAPIRenderer提供text/html的返回内容。

默认使用Bootstrap库

6、使用高级的关系和序列化

将创建复制的API服务,能处理多张表,例如一个drone设备的数据模型

drone model, DroneCategory model, Pilot model, competition model。

scopeURI
DroneCategory/drone-categories/
DroneCategory/drone-categories/{id}
drones/drone/
drones/drone/{id}
pilots/pilots/
pilots/pilots/{id}
competitions/competitions/
competitions/competitions/{id}

 

 

 

 

 

 

 

 

 

resources定义如下:

HTTP VerbScopeSemantics
GETDroneCategory获取所有记录
POSTDroneCategory创建一个新记录
PUTDroneCategory更新一个已存在的记录
PATCHDroneCategory更新多个字段
DELETEDroneCategory删除一个记录

 

 

 

 

 

 

 

使用Django中的ORM(Object-Relational Mapping)功能。

$ python manage.py startapp drones

from django.db import models

class DroneCategory(models.Model):
    name = models.CharField(max_length=250)

    class Meta:
        ordering = ('name', )

    def __str__(self):
        return self.name

class Drone(models.Model):
    name = models.CharField(max_length=250)
    drone_category = models.ForeignKey(
        DroneCategory,
        related_name='drones',
        on_delete=models.CASCADE # 删除
    )
    manufacturing_date = models.DateTimeField()
    has_it_competed = models.BooleanField(default=False)
    inserted_timestamp = models.DateTimeField(auto_now_add=True)

    class Meta:
        ordering = ('name', )

    def __str__(self):
        return self.name

class Pilot(models.Model):
    MALE = 'M'
    FEMALE = 'F'
    GENDER_CHOICES = (
        (MALE, 'Male'),
        (FEMALE, 'Female'),
    )
    name = models.CharField(max_length=150, blank=False, default='')
    gender = models.CharField(
        max_length=2,
        choices=GENDER_CHOICES,
        default=MALE,
    )
    races_count = models.IntegerField()
    inserted_timestamp = models.DateTimeField(auto_now_add=True)

    class Meta:
        ordering = ('name', )

    def __str__(self):
        return self.name

class Competition(models.Model):
    pilot = models.ForeignKey(
        Pilot,
        related_name='competitions',
        on_delete=models.CASCADE
    )
    drone = models.ForeignKey(
        Drone,
        on_delete=models.CASCADE
    )
    distance_in_feet = models.IntegerField()
    distance_achievement_date = models.DateTimeField()

    class Meta:
        ordering = ('-distance_in_feet', )

$ python manage.py makemigrations drones

$ python manage.py migrate

序列化drones/serializers.py

rest_framework.serializers.ModelSerializer

rest_framework.serializers.HyperlinkedModelSerializer

from rest_framework import serializers
from drones.models import DroneCategory
from drones.models import Drone
from drones.models import Pilot
from drones.models import Competition
import drones.views

class DroneCategorySerializer(serializers.HyperlinkedModelSerializer):
    # one to many 关系
    drones = serializers.HyperlinkedRelatedField(
        many=True,
        read_only=True,
        view_name='drone-detail'
    )

    class Meta:
        model = DroneCategory
        fields = ('url', 'pk', 'name', 'drones', )

class DroneSerializer(serializers.HyperlinkedModelSerializer):
    # 代表关系唯一,显示类型名称
    drone_category = serializers.SlugRelatedField(
        queryset=DroneCategory.objects.all(),
        slug_field='name')
    class Meta:
        model = Drone
        fields = (
            'url',
            'name',
            'drone_category',
            'manufacturing_date',
            'has_it_competed',
            'inserted_timestamp'
        )

class CompetitionSerializer(serializers.HyperlinkedModelSerializer):
    drone = DroneSerializer()
    class Meta:
        model = Competition
        fields = (
            'url',
            'pk',
            'distance_in_feet',
            'distance_achievement_date',
            'drone'
        )

class PilotSerializer(serializers.HyperlinkedModelSerializer):
    competitions = CompetitionSerializer(
        many=True,
        read_only=True
    )
    gender = serializers.ChoiceField(choices=Pilot.GENDER_CHOICES)
    gender_description = serializers.CharField(
        source='get_gender_display',
        read_only=True
    )
    class Meta:
        model = Pilot
        fields = (
            'url',
            'name',
            'gender',
            'gender_description',
            'races_count',
            'inserted_timestamp',
            'competitions'
        )

class PilotCompetitionSerializer(serializers.ModelSerializer):
    pilot = serializers.SlugRelatedField(
        queryset=Pilot.objects.all(),
        slug_field='name'
    )
    drone = serializers.SlugRelatedField(
        queryset=Drone.objects.all(),
        slug_field='name'
    )
    class Meta:
        model = Competition
        fields = (
            'url',
            'pk',
            'distance_in_feet',
            'distance_achievement_date',
            'pilot',
            'drone'
        )

使用基于类的视图 drones/views.py

from django.shortcuts import render
from rest_framework import generics
from rest_framework.response import Response
from rest_framework.reverse import reverse
from drones.models import DroneCategory
from drones.models import Drone
from drones.models import Pilot
from drones.models import Competition
from drones.serializers import DroneCategorySerializer
from drones.serializers import DroneSerializer
from drones.serializers import PilotSerializer
from drones.serializers import PilotCompetitionSerializer

class DroneCategoryList(generics.ListCreateAPIView):
    queryset = DroneCategory.objects.all()
    serializer_class = DroneCategorySerializer
    name = 'dronecategory-list'

class DroneCategoryDetail(generics.RetrieveUpdateDestroyAPIView):
    queryset = DroneCategory.objects.all()
    serializer_class = DroneCategorySerializer
    name = 'dronecategory-detail'

class DroneList(generics.ListCreateAPIView):
    queryset = Drone.objects.all()
    serializer_class = DroneSerializer
    name = 'drone-list'

class DroneDetail(generics.RetrieveUpdateDestroyAPIView):
    queryset = Drone.objects.all()
    serializer_class = DroneSerializer
    name = 'drone-detail'

class PilotList(generics.ListCreateAPIView):
    queryset = Pilot.objects.all()
    serializer_class = PilotSerializer
    name = 'pilot-list'

class PilotDetail(generics.RetrieveUpdateDestroyAPIView):
    queryset = Pilot.objects.all()
    serializer_class = PilotSerializer
    name = 'pilot-detail'

class CompetitionList(generics.ListCreateAPIView):
    queryset = Competition.objects.all()
    serializer_class = PilotCompetitionSerializer
    name = 'competition-list'

class CompetitionDetail(generics.RetrieveUpdateDestroyAPIView):
    queryset = Competition.objects.all()
    serializer_class = PilotCompetitionSerializer
    name = 'competition-detail'

# API
class ApiRoot(generics.GenericAPIView):
    name = 'api-root'
    def  get(self, request, *args, **kwargs):
        return Response({
            'drone-categories': reverse(DroneCategoryList.name, request=request),
            'drones': reverse(DroneList.name, request=request),
            'pilots': reverse(PilotList.name, request=request),
            'competitions': reverse(CompetitionList.name, request=request)
        })

路由drones/urls.py    访问 http://127.0.0.1:8000/

from django.conf.urls import url
from drones import views
urlpatterns = [
    url(r'^drone-categories/$',
        views.DroneCategoryList.as_view(),
        name=views.DroneCategoryList.name),
    url(r'^drone-categories/(?P<pk>[0-9]+)$',
        views.DroneCategoryDetail.as_view(),
        name=views.DroneCategoryDetail.name),
    url(r'^drones/$',
        views.DroneList.as_view(),
        name=views.DroneList.name),
    url(r'^drones/(?P<pk>[0-9]+)$',
        views.DroneDetail.as_view(),
        name=views.DroneDetail.name),
    url(r'^pilots/$',
        views.PilotList.as_view(),
        name=views.PilotList.name),
    url(r'^pilots/(?P<pk>[0-9]+)$',
        views.PilotDetail.as_view(),
        name=views.PilotDetail.name),
    url(r'^competitions/$',
        views.CompetitionList.as_view(),
        name=views.CompetitionList.name),
    url(r'^competitions/(?P<pk>[0-9]+)$',
        views.CompetitionDetail.as_view(),
        name=views.CompetitionDetail.name),
    url(r'^$',
        views.ApiRoot.as_view(),
        name=views.ApiRoot.name),
]

7、使用约束、过滤、搜索、排序和分页功能

防止重名

分页

设置setting.py的方法

REST_FRAMEWORK = {
    'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.LimitOffsetPagination',
    'PAGE_SIZE': 4
}

# curl -iX GET "localhost:8000/drones/?limit=4&offset=0
使用自定义的分页类的方法

通过改变全局配置rest_framework.pagination.LimitOffsetPagination类,这个类中的max_limit,默认为None,即无上限

drones/custompagination.py

from rest_framework.pagination import LimitOffsetPagination
class LimitOffsetPaginationWithUpperBound(LimitOffsetPagination):
    max_limit = 8  # 最大页

setting.py

REST_FRAMEWORK = {
    'DEFAULT_PAGINATION_CLASS': 'drones.custompagination.LimitOffsetPaginationWithUpperBound',
    'PAGE_SIZE': 4
}

配置过滤、搜索等

前面的请求检索整个数据集,并且使用默认的排序。

$ pip install django-filter

将使用以下三个类:

  • rest_framework.filters.OrderingFilter:允许控制排序
  • django_filters.rest_framework.DjangoFilterBackend:允许开发者自定义类视图
  • rest_framework.filters.SearchFilter:提供搜索功能

setting.py

INSTALLED_APPS = [
    ......
    'django_filters',
]
......
REST_FRAMEWORK = {
    'DEFAULT_PAGINATION_CLASS': 'drones.custompagination.LimitOffsetPaginationWithUpperBound',
    'PAGE_SIZE': 4,
    'DEFAULT_FILTER_BACKENDS': (
        'django_filters.rest_framework.DjangoFilterBackend',
        'rest_framework.filters.OrderingFilter',
        'rest_framework.filters.SearchFilter',
    ),
}

在DroneCategoryList, DroneList, PilotList和CompetitionList中加入配置filter_fields, search_fields和ordering_fields。

drones/views.py

from rest_framework import filters
from django_filters import AllValuesFilter, DateTimeFilter, NumberFilter

class DroneCategoryList(generics.ListCreateAPIView):
    queryset = DroneCategory.objects.all()
    serializer_class = DroneCategorySerializer
    name = 'dronecategory-list'
    filter_fields = ('name', )
    search_fields = ('^name', )
    ordering_fields = ('name', )

class DroneList(generics.ListCreateAPIView):
    queryset = Drone.objects.all()
    serializer_class = DroneSerializer
    name = 'drone-list'
    filter_fields = ('name', 'drone_category',
                     'manufacturing_date', 'has_it_competed',)
    search_fields = ('^name',)
    ordering_fields = ('name','manufacturing_date', )

class PilotList(generics.ListCreateAPIView):
    queryset = Pilot.objects.all()
    serializer_class = PilotSerializer
    name = 'pilot-list'
    filter_fields = ('name', 'gender', 'races_count', )
    search_fields = ('^name',)
    ordering_fields = ('name', 'races_count',)

创建一个自定义的CompetitionFiler类

class CompetitionFilter(filters.FilterSet):
    from_achievement_date = DateTimeFilter(
        name='distance_achievement_date', lookup_expr='gte'
    ) # 大于等于某日期
    to_achievement_date = DateTimeFilter(
        name='distance_achievement_date', lookup_expr='lte'
    )
    min_distance_in_feet = NumberFilter(
        name='distance_in_feet', lookup_expr='gte'
    ) # 大于等于某值
    max_distance_in_feet = NumberFilter(
        name='distance_in_feet', lookup_expr='lte'
    )
    drone_name = AllValuesFilter(
        name='drone_name'
    )
    pilot_name = AllValuesFilter(
        name='pilot_name'
    )
    class Meta:
        model = Competition
        fields = (
            'distance_in_feet',
            'from_achievement_date',
            'to_achievement_date',
            'min_distance_in_feet',
            'max_distance_in_feet',
            'drone_name',
            'pilot_name',
        )

class CompetitionList(generics.ListCreateAPIView):
    queryset = Competition.objects.all()
    serializer_class = PilotCompetitionSerializer
    name = 'competition-list'
    filter_class = CompetitionFilter
    ordering_fields = (
        'distance_in_feet',
        'distance_achievement_date',
    )

http://localhost:8000/drone-categories/?name=copper

8、API权限

使用request对象的user和auth两个属性。

rest_framework提供了三个权限类,在rest_framework.authentication模块中。

  • BasicAuthentication:提供了username和password的HTTP基本权限
  • SessionAuthentication:用于Django的Session权限
  • TokenAuthentication:提供一个简单的token权限,要求HTTP必须有'Token‘字段。

,在生产环境下只提供https服务,所以不应该使用以上简单的http权限管理,我们可以使用一些第三方库

为了学习,我们使用BasicAuthentication

setting.py

REST_FRAMEWORK = {
    'DEFAULT_PAGINATION_CLASS': 'drones.custompagination.LimitOffsetPaginationWithUpperBound',
    'PAGE_SIZE': 4,
    'DEFAULT_FILTER_BACKENDS': (
        'django_filters.rest_framework.DjangoFilterBackend',
        'rest_framework.filters.OrderingFilter',
        'rest_framework.filters.SearchFilter',
    ),
    'DEFAULT_AUTHENTICATION_CLASSES': (
        'rest_framework.authentication.BasicAuthentication',
        'rest_framework.authentication.SessionAuthentication',
    )
}

只有拥有权限的用户才能创建Drone,即PATCH,PUT和DELETE

request.user和request.auth确定请求是否准许。

在drones/models.py中增加owner字段,使用django.contrib.auth.User

class Drone(models.Model):
    name = models.CharField(max_length=250, unique=True)  # 唯一
    drone_category = models.ForeignKey(
        DroneCategory,
        related_name='drones',
        on_delete=models.CASCADE # 删除
    )
    manufacturing_date = models.DateTimeField()
    has_it_competed = models.BooleanField(default=False)
    inserted_timestamp = models.DateTimeField(auto_now_add=True)
    owner = models.ForeignKey(
        'auth.User',
        related_name='drones',
        on_delete=models.CASCADE
    )
    class Meta:
        ordering = ('name', )
    def __str__(self):
        return self.name

drones/serializers.py

from django.contrib.auth.models import User
class UserDroneSerializer(serializers.HyperlinkedModelSerializer):
    class Meta:
        model = Drone
        fields = (
            'url',
            'name'
        )
class UserSerializer(serializers.HyperlinkedModelSerializer):
    drones = UserDroneSerializer(
        many=True,
        read_only=True
    )
    class Meta:
        model = User
        fields = (
            'url',
            'pk',
            'username',
            'drone'
        )

class DroneSerializer(serializers.HyperlinkedModelSerializer):
    # 代表关系唯一,显示类型名称
    drone_category = serializers.SlugRelatedField(
        queryset=DroneCategory.objects.all(),
        slug_field='name')
    owner = serializers.ReadOnlyField(source='owner.username')
    class Meta:
        model = Drone
        fields = (
            'url',
            'name',
            'drone_category',
            'owner',
            'manufacturing_date',
            'has_it_competed',
            'inserted_timestamp'
        )

自定义准许类,基于rest_framework.permissions.BasePermission

drones/custompermission.py

from rest_framework import permissions

class IsCurrentUserOwnerOrReadOnly(permissions.BasePermission):
    def has_object_permission(self, request, view, obj):
        # 判断方法是否安全
        if request.method in permissions.SAFE_METHODS:
            return True
        else:
            return obj.owner == request.user

drones/views.py

from rest_framework import permissions
from drones import custompermission
......
class DroneList(generics.ListCreateAPIView):
    queryset = Drone.objects.all()
    serializer_class = DroneSerializer
    name = 'drone-list'
    filter_fields = ('name', 'drone_category',
                     'manufacturing_date', 'has_it_competed',)
    search_fields = ('^name',)
    ordering_fields = ('name','manufacturing_date', )
    permission_classes = (
        permissions.IsAuthenticatedOrReadOnly,
        custompermission.IsCurrentUserOwnerOrReadOnly,
    )
    # 创建时调用
    def perform_create(self, serializer):
        serializer.save(owner=self.request.user)

class DroneDetail(generics.RetrieveUpdateDestroyAPIView):
    queryset = Drone.objects.all()
    serializer_class = DroneSerializer
    name = 'drone-detail'
    permission_classes = (
        permissions.IsAuthenticatedOrReadOnly,
        custompermission.IsCurrentUserOwnerOrReadOnly,
    )

创建超级用户

$ python manage.py createsuperuser

username:copper , ps: ping2019

$ python manage.py makemigrations drones

选择1,输入默认owner为1,即id为1

$ python manage.py migrate

创建普通用户

$ python manage.py shell

>>> from django.contrib.auth.models import User
>>> user = User.objects.create_user('user01', 'user01@example.com', 'user01password')
>>> user.save()

使用postman测试

urls.py

urlpatterns = [
    url(r'^', include('toys.urls')),
    url(r'^', include('drones.urls')),
    url(r'^api-auth', include('rest_framework.urls')),
]

使用基于token的权限管理,用于Pilot数据表

setting.py

INSTALLED_APPS = [
    ......
    'rest_framework.authtoken',
]

$ python manage.py migrate

drones/views.py

from rest_framework.permissions import IsAuthenticated
from rest_framework.authentication import TokenAuthentication
class PilotList(generics.ListCreateAPIView):
    queryset = Pilot.objects.all()
    serializer_class = PilotSerializer
    name = 'pilot-list'
    filter_fields = ('name', 'gender', 'races_count', )
    search_fields = ('^name',)
    ordering_fields = ('name', 'races_count',)
    authentication_classes = (
        TokenAuthentication
    )
    permission_classes = (
        IsAuthenticated
    )

class PilotDetail(generics.RetrieveUpdateDestroyAPIView):
    queryset = Pilot.objects.all()
    serializer_class = PilotSerializer
    name = 'pilot-detail'
    authentication_classes = (
        TokenAuthentication
    )
    permission_classes = (
        IsAuthenticated
    )

$ python manage.py shell

>>> from rest_framework.authtoken.models import Token
>>> from django.contrib.auth.models import User
>>> user = User.objects.get(username="user01")
>>> token = Token.objects.create(user=user)
>>> print(token.key)

9、使用限制规则和版本管理

限流,rest_framework提供了三个类,rest_framework.throttling --> SimpleRateThrottle --> BaseThrottle

setting.py

REST_FRAMEWORK = {
    ......
    'DEFAULT_THROTTLE_CLASSES': (
        'rest_framework.throttling.AnonRateThrottle',
        'rest_framework.throttling.UserRateThrottle',
    ),
    'DEFAULT_THROTTLE_RATES': {
        'anon': '3/hour',
        'user': '10/hour',
        'drones': '20/hour',
        'pilots': '15/hour',
    }
}

配置视图views.py,有两个类

  • throttle_classes:管理限制规则
  • throttle_scope:累积请求数和限制请求率
from rest_framework.throttling import ScopedRateThrottle
class DroneDetail(generics.RetrieveUpdateDestroyAPIView):
    throttle_scope = 'drones'  # setting中drones的20/hour
    throttle_classes = (ScopedRateThrottle, )

class DroneList(generics.ListCreateAPIView):
    throttle_scope = 'drones'
    throttle_classes = (ScopedRateThrottle, )
class PilotDetail(generics.RetrieveUpdateDestroyAPIView):
    throttle_scope = 'pilots'
    throttle_classes = (ScopedRateThrottle,)

class PilotList(generics.ListCreateAPIView):
    throttle_scope = 'pilots'
    throttle_classes = (ScopedRateThrottle,)

API版本v1、v2等

setting.py中添加

'DEFAULT_VERSIONING_CLASS':'rest_framework.versioning.NamespaceVersioning'

10、自动测试

$ pip install pytest

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值