在DRF框架中提供了众多的通用视图基类与扩展类,以简化视图的编写。
APIView:DRF提供的所有视图的基类,继承View并扩展,具备了身份认证、权限检查、流量控制等功能
E:\workspace\django-project\day2\django_drf>python manage.py startapp myapp
from rest_framework.views import APIView
from rest_framework.response import Response
class UserAPIView(APIView):
def get(self,request):
data = {'result': 'get'}
return Response(data)
def post(self,request):
data = {'result': 'post'}
return Response(data)
def put(self,request):
data = {'result': 'put'}
return Response(data)
def delete(self,request):
data = {'result': 'delete'}
return Response(data)
from django.urls import re_path
from myapp import views
urlpatterns = [
re_path('^users1/$', views.UserAPIView.as_view())
]
DRF传入视图的request对象不再是Django默认的HttpRequest对象,而是基于HttpRequest类扩展后的Request类的对象。
Request对象的数据是自动根据前端发送的数据统一解析数据格式。
DRF提供了一个响应类Reponse,响应的数据会自动转换符合前端的JSON数据格式。
导入:
from rest_framework.response import Response
格式:
Response(data, status=None, template_name=None, headers=None, content_type=None)
使用方法:
return Reponse(data=data, status=status.HTTP_404_NOT_FOUND)
为了方便设置状态码,rest_framework.status模块提供了所有HTTP状态码,以下是一些常用的:
HTTP_200_OK:请求成功
HTTP_301_MOVED_PERMANENTLY:永久重定向
HTTP_302_FOUND:临时重定向
HTTP_304_NOT_MODIFIED:请求的资源未修改
HTTP_403_FORBIDDEN:没有权限访问
HTTP_404_NOT_FOUND:页面没有发现
HTTP_500_INTERNAL_SERVER_ERROR:服务器内部错误
HTTP_502_BAD_GATEWAY:网关错误
HTTP_503_SERVICE_UNAVAILABLE:服务器不可达
HTTP_504_GATEWAY_TIMEOUT:网关超时
from django.db import models
class User(models.Model):
name = models.CharField(max_length=32)
city = models.CharField(max_length=64)
sex = models.CharField(max_length=8)
age = models.IntegerField()
from myapp.models import User
from rest_framework import serializers
class UserSerializer(serializers.ModelSerializer):
class Meta:
model = User
fields = '__all__'
E:\workspace\django-project\day2\django_drf>python manage.py makemigrations
E:\workspace\django-project\day2\django_drf>python manage.py migrate
E:\workspace\django-project\day2\django_drf>python manage.py shell
In [1]: from myapp.models import User
In [2]: User.objects.create(name="张三",city="北京市",sex="男",age=29)
In [3]: User.objects.create(name="李四",city="上海市",sex="女",age=19)
In [4]: User.objects.create(name="阿山",city="广州市",sex="女",age=23)
In [5]: User.objects.create(name="阿妖",city="深圳市",sex="男",age=21)
from myapp.models import User
from rest_framework.generics import GenericAPIView
from .serializers import UserSerializer
from rest_framework.response import Response
class UserGenericAPIViews(GenericAPIView):
queryset = User.objects.all() #指定操作的数据
serializer_class = UserSerializer #指定序列化器
lookup_field = 'id'
def get(self,request, id=None):
if id:
user_obj = self.get_object() #从类方法调用指定数据,不需要传id
user_ser = self.get_serializer(user_obj)
else:
quertset = self.get_queryset() #从类方法调用所有数据
user_ser = self.get_serializer(quertset, many=True) #从类方法调用序列化器
result = {"code": 200, "msg": "success", "data": user_ser.data}
return Response(result)
def post(self,request):
user_ser = self.get_serializer(data=request.data)
user_ser.is_valid(raise_exception=True)
user_ser.save()
result = {"code": 200, "msg": "create success"}
return Response(result)
def put(self,request,id=None):
user_obj = self.get_object()
user_ser = self.get_serializer(instance=user_obj,data=request.data)
user_ser.is_valid(raise_exception=True)
result = {"code": 200, "msg": "update success"}
return Response(result)
def delete(self,request,id=None):
user_obj = self.get_object()
user_obj.delete()
result = {"code": 200, "msg": "delete success"}
return Response(result)
from django.urls import re_path
from myapp import views
urlpatterns = [
re_path('^users1/$', views.UserAPIView.as_view()),
re_path('^users2/$',views.UserGenericAPIViews.as_view()), #获取所有数据和创建
re_path('^users2/(?P<id>\d+)/$',views.UserGenericAPIViews.as_view()), #获取单条数据及其更新,删除
]
http://127.0.0.1:8000/myapp/users2/ #list,post
http://127.0.0.1:8000/myapp/users2/5/ #指定id数据的更新,删除,查看
GenericAPIView已经完成了许多功能,但会有一个问题,获取所有用户列表和单个用户需要分别定义两个视图和URL路由,使用ViewSet可以很好解决这个问题,并且实现了路由自动映射
。
ViewSet视图集不再实现get()、post()等方法,而是实现以下请求方法动作:
list():获取所有数据
retrieve():获取单个数据
create():创建数据
update():更新数据
destory():删除数据
from rest_framework.viewsets import ViewSet
from rest_framework.response import Response
from myapp.models import User
from .serializers import UserSerializer
class UserViewSet(ViewSet):
lookup_field = 'id' #自动注册路由的时候需要用这个
def list(self,request):
user_obj = User.objects.all()
user_ser = UserSerializer(user_obj, many=True)
result = {"code": 200, "msg": "success", "data": user_ser.data}
return Response(result)
def retrieve(self,request,id=None):
user_obj = User.objects.get(id=id)
user_ser = UserSerializer(user_obj)
result = {"code": 200, "msg": "success", "data": user_ser.data}
return Response(result)
def update(self,request,id=None):
user_obj = User.objects.get(id=id)
user_ser = UserSerializer(instance=user_obj,data=request.data)
user_ser.is_valid(raise_exception=True)
user_ser.save()
result = {"code": 200, "msg": "update success"}
return Response(result)
def create(self,request):
user_ser = UserSerializer(data=request.data)
user_ser.is_valid(raise_exception=True)
user_ser.save()
result = {"code": 200, "msg": "create success"}
return Response(result)
def destory(self,request,id=None):
user_obj = User.objects.get(id=id)
user_obj.delete()
result = {"code": 200, "msg": "delete success"}
return Response(result)
from django.urls import re_path
from myapp import views
urlpatterns = [
re_path('^users1/$', views.UserAPIView.as_view()),
re_path('^users2/$',views.UserGenericAPIViews.as_view()), #获取所有数据和创建
re_path('^users2/(?P<id>\d+)/$',views.UserGenericAPIViews.as_view()), #获取单条数据及其更新,删除
re_path('^users3/$',views.UserViewSet.as_view({'get': 'list','post': 'create'})), #获取所有数据和创建
re_path('^users3/(?P<id>\d+)/$',views.UserViewSet.as_view({'get': 'retrieve','put': 'update','delete': 'destory'})), #获取单条数据及其更新,删除
]
http://127.0.0.1:8000/myapp/users3/ #list,post
http://127.0.0.1:8000/myapp/users3/5/ #指定id数据的更新,删除,查看
from django.urls import re_path,include
from myapp import views
from rest_framework.routers import DefaultRouter
router = DefaultRouter()
router.register('user4',views.UserViewSet,'user4')
router.register('user5',views.UserViewSet,'user5') #再次注册
urlpatterns = [
re_path('^users1/$', views.UserAPIView.as_view()),
re_path('^users2/$',views.UserGenericAPIViews.as_view()), #获取所有数据和创建
re_path('^users2/(?P<id>\d+)/$',views.UserGenericAPIViews.as_view()), #获取单条数据及其更新,删除
re_path('^users3/$',views.UserViewSet.as_view({'get': 'list','post': 'create'})), #获取所有数据和创建
re_path('^users3/(?P<id>\d+)/$',views.UserViewSet.as_view({'get': 'retrieve','put': 'update','delete': 'destory'})), #获取单条数据及其更新,删除
re_path('^api/',include(router.urls))
]
http://127.0.0.1:8000/myapp/api/ #查看注册的url列表
http://127.0.0.1:8000/myapp/api/user4/ #list,post
http://127.0.0.1:8000/myapp/api/user4/2/ #指定id数据的更新,删除,查看
由于ModelViewSet有较高的抽象,实现自动增删改查功能。对于增、改在很多场景无法满足需求,这就需要重写对应方法了。
from rest_framework.viewsets import ModelViewSet
class UserModelViewSet(ModelViewSet):
queryset = User.objects.all()
serializer_class = UserSerializer
from django.urls import re_path,include
from myapp import views
from rest_framework.routers import DefaultRouter
router = DefaultRouter()
router.register('user4',views.UserViewSet,'user4')
router.register('user5',views.UserViewSet,'user5')
router.register('user6',views.UserModelViewSet,'user6') #注入ModelViewSet
urlpatterns = [
re_path('^users1/$', views.UserAPIView.as_view()),
re_path('^users2/$',views.UserGenericAPIViews.as_view()), #获取所有数据和创建
re_path('^users2/(?P<id>\d+)/$',views.UserGenericAPIViews.as_view()), #获取单条数据及其更新,删除
re_path('^users3/$',views.UserViewSet.as_view({'get': 'list','post': 'create'})), #获取所有数据和创建
re_path('^users3/(?P<id>\d+)/$',views.UserViewSet.as_view({'get': 'retrieve','put': 'update','delete': 'destory'})), #获取单条数据及其更新,删除
re_path('^api/',include(router.urls))
]
http://127.0.0.1:8000/myapp/api/ #查看注册的url列表
http://127.0.0.1:8000/myapp/api/user6/ #list,create
http://127.0.0.1:8000/myapp/api/user6/ #update, delete, retrieve
HTTP是一个无状态的协议,每次访问都是新的,早期主要用于浏览网页,随着时代发展,像在线购物网站的兴起,就面临着记录哪些人登录系统,哪些人购物车里放了商品。也就是必须每个人区分开,所以就有了用户名来标识,但每次访问页面都要登录,非常麻烦,这就有了会话保持。Cookie+Session就是实现会话保持的技术。
Cookie+Session通常在浏览器作为客户端的情况下比较通用,随着前后端分离开发模式的普及,会涉及到多端(PC、APP、Pad),特别是手机端,支持Cookie不友好,并且Cookie不支持跨域,因此基于这些局限性,Token逐渐主流。
与普通Token一样,都是访问资源的令牌,区别是普通Token服务端验证token信息要查询数据库验证,JWT验证token信息不用查询数据库,只需要在服务端使用密钥效验。与普通Token一样,都是访问资源的令牌,区别是普通Token服务端验证token信息要查询数据库验证,JWT验证token信息不用查询数据库,只需要在服务端使用密钥效验。
目前DRF可任意访问,没有任何限制,是不符合生产环境标准的,因此接下来学习认证实现访问控制。
由于Django默认提供Session存储机制,可直接通过登录内置管理后台进行验证。当登录管理后台后,就有权限访问了。
REST_FRAMEWORK = {
'DEFAULT_AUTHENTICATION_CLASSES': [
'rest_framework.authentication.SessionAuthentication',
],
'DEFAULT_PERMISSION_CLASSES': [
'rest_framework.permissions.IsAuthenticated',
]
}
from rest_framework.viewsets import ModelViewSet
from rest_framework.authentication import SessionAuthentication
from rest_framework.permissions import IsAuthenticated
class UserModelViewSet(ModelViewSet):
queryset = User.objects.all()
serializer_class = UserSerializer
#认证&权限配置
authentication_classes = [SessionAuthentication]
permission_classes = [IsAuthenticated]
REST_FRAMEWORK = {
#认证
'DEFAULT_AUTHENTICATION_CLASSES': [
#'rest_framework.authentication.SessionAuthentication',
'rest_framework.authentication.TokenAuthentication',
],
#权限
'DEFAULT_PERMISSION_CLASSES': [
'rest_framework.permissions.IsAuthenticated',
]
}
E:\workspace\django-project\day2\django_drf>python manage.py makemigrations
E:\workspace\django-project\day2\django_drf>python manage.py migrate
from django.contrib import admin
from django.urls import path,include,re_path
from rest_framework.authtoken import views
urlpatterns = [
path('admin/', admin.site.urls),
path('myapp/',include("myapp.urls")),
re_path('^auth_token/$', views.obtain_auth_token)
]
from rest_framework.authtoken.views import ObtainAuthToken
from rest_framework.response import Response
from rest_framework.authtoken.models import Token
class CustomAuthToken(ObtainAuthToken):
def post(self, request, *args, **kwargs):
serializer = self.get_serializer(data=request.data)
serializer.is_valid(raise_exception=True)
user = serializer.validated_data['user']
token, created = Token.objects.get_or_create(user=user)
return Response({'token': token.key, 'username': user.username})
from django.contrib import admin
from django.urls import path,include,re_path
# from rest_framework.authtoken import views
from myapp.utils.auth_token import CustomAuthToken
urlpatterns = [
path('admin/', admin.site.urls),
path('myapp/',include("myapp.urls")),
re_path('^auth_token/$', CustomAuthToken.as_view())
]
可以对接口访问的频率进行限制,以减轻服务器压力。
应用场景:投票、购买数量等
REST_FRAMEWORK = {
#认证
'DEFAULT_AUTHENTICATION_CLASSES': [
#'rest_framework.authentication.SessionAuthentication',
'rest_framework.authentication.TokenAuthentication',
],
#权限
'DEFAULT_PERMISSION_CLASSES': [
'rest_framework.permissions.IsAuthenticated',
],
#限流
'DEFAULT_THROTTLE_CLASSES': [
'rest_framework.throttling.AnonRateThrottle',
'rest_framework.throttling.UserRateThrottle'
],
'DEFAULT_THROTTLE_RATES': {
'anon': '10/minute',
'user': '10/minute'
}
}
pip3 install django-filter
REST_FRAMEWORK = {
#认证
# 'DEFAULT_AUTHENTICATION_CLASSES': [
# 'rest_framework.authentication.SessionAuthentication',
# 'rest_framework.authentication.TokenAuthentication',
# ],
#权限
# 'DEFAULT_PERMISSION_CLASSES': [
# 'rest_framework.permissions.IsAuthenticated',
# ],
#限流
'DEFAULT_THROTTLE_CLASSES': [
'rest_framework.throttling.AnonRateThrottle',
'rest_framework.throttling.UserRateThrottle'
],
'DEFAULT_THROTTLE_RATES': {
'anon': '10/minute',
'user': '10/minute'
},
#过滤配置
'DEFAULT_FILTER_BACKENDS': ['django_filters.rest_framework.DjangoFilterBackend']
}
from rest_framework.viewsets import ModelViewSet
class UserModelViewSet(ModelViewSet):
queryset = User.objects.all()
serializer_class = UserSerializer
#过滤
filter_fields = ('sex',)
DRF提供过滤器帮助我们快速对字段进行搜索和排序
from rest_framework.viewsets import ModelViewSet
from rest_framework import filters
class UserModelViewSet(ModelViewSet):
queryset = User.objects.all()
serializer_class = UserSerializer
#过滤
filter_fields = ('sex',)
#搜索&排序
filter_backends = [filters.SearchFilter,filters.OrderingFilter]
search_fields = ('name',)
order_fields = ('age',)
分页是数据表格必备的功能,可以在前端实现,也可以在后端实现,为了避免响应数据过大,造成前端压力,一般在后端实现。
REST_FRAMEWORK = {
#限流
'DEFAULT_THROTTLE_CLASSES': [
'rest_framework.throttling.AnonRateThrottle',
'rest_framework.throttling.UserRateThrottle'
],
'DEFAULT_THROTTLE_RATES': {
'anon': '10/minute',
'user': '10/minute'
},
#过滤配置
'DEFAULT_FILTER_BACKENDS': ('django_filters.rest_framework.DjangoFilterBackend',),
#全局分页
'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.PageNumberPagination',
'PAGE_SIZE': 3 #三条数据一页
}
from rest_framework.pagination import PageNumberPagination
from collections import OrderedDict
from rest_framework.response import Response
class CustomPagination(PageNumberPagination):
page_size = 10 #每页显式多少条数据
page_query_param = 'page_num' #查询第几页数据
page_size_query_param = 'page_size' #每页显式多少条
max_page_size = 50 #每一页最大显式50条
def get_paginated_response(self, data):
return Response(OrderedDict([
('total_num', self.page.paginator.count),
('next_page', self.get_next_link()),
('msg', 'success'),
('code',200),
('previous_page', self.get_previous_link()),
('data', data)
]))
REST_FRAMEWORK = {
#限流
'DEFAULT_THROTTLE_CLASSES': [
'rest_framework.throttling.AnonRateThrottle',
'rest_framework.throttling.UserRateThrottle'
],
'DEFAULT_THROTTLE_RATES': {
'anon': '10/minute',
'user': '10/minute'
},
#过滤配置
'DEFAULT_FILTER_BACKENDS': ('django_filters.rest_framework.DjangoFilterBackend',),
# 自定义分页
'DEFAULT_PAGINATION_CLASS': 'myapp.utils.my_pagination.CustomPagination'
}
由于项目开发经验欠缺或着急上线,需求不断改动,项目设计阶段定义的接口已经面目全非,这给前端开发人员参考带来一定困难,如何改善这个问题呢?
pip3 install django-rest-swagger
REST_FRAMEWORK = {
#限流
'DEFAULT_THROTTLE_CLASSES': [
'rest_framework.throttling.AnonRateThrottle',
'rest_framework.throttling.UserRateThrottle'
],
'DEFAULT_THROTTLE_RATES': {
'anon': '10/minute',
'user': '10/minute'
},
#过滤配置
'DEFAULT_FILTER_BACKENDS': ('django_filters.rest_framework.DjangoFilterBackend',),
# 自定义分页
'DEFAULT_PAGINATION_CLASS': 'myapp.utils.my_pagination.CustomPagination',
#接口文档
'DEFAULT_SCHEMA_CLASS': 'rest_framework.schemas.coreapi.AutoSchema',
}
D:\devTools\python3\lib\site-packages\rest_framework_swagger\templates\rest_framework_swagger\index.html
将index.html中的第二行staticfiles改成static即可解决