作用:判断用户是否登录,之后登录后的用户才有权限访问接口
自定义认证类继承BaseAuthentication
认证(判断用户是否登录):
from rest_framework.authentication import BaseAuthentication
from rest_framework.exceptions import AuthenticationFailed
from drf.models import Token
class MyBaseAuthentication(BaseAuthentication):
###自定义认证类
def authenticate(self, request):
#在这里完成认证的逻辑
token = request._request.GET.get('token')
#token_obj ->Token
token_obj = Token.objects.filter(token=token).first()
if token_obj:
#说明用户是登录的用户
####### user auth
return (token_obj.user,token)
else:
raise AuthenticationFailed('用户未登录,没有权限访问')
#局部使用
class BooksView(APIView):
#http://127.0.0.1:8000/api/books/?token=xxxxxxxx
# #设置认证类
authentication_classes = [MyBaseAuthentication,]
def get(self,request,*args,**kwargs):
pass
request.user:对应的是认证成功后返回元祖中的第一个参数
request.auth:对应的是认证成功后返回元祖中的第二个参数
#全局使用
#REST_FRAMEWORK:配置Restframework的全局设置
REST_FRAMEWORK = {
'DEFAULT_AUTHENTICATION_CLASSES':['tools.authentication.MyBaseAuthentication']
# 设置匿名用户
'UNAUTHENTICATED_USER':lambda :'匿名用户小偷',
'UNAUTHENTICATED_TOKEN':lambda :'123456skcaslbvkasvcvacaj',
}
#注意:如果设置了全局的认证类,那么想要在某些视图中不进行认证,设置如下:
class BooksView(APIView):
#http://127.0.0.1:8000/api/books/?token=xxxxxxxx
#如果设置了全局的认证,不想让某些视图执行认证
#将authentication_classes设置为空列表
authentication_classes = []
def get(self,request,*args,**kwargs):
pass
class MyBasePermission(BasePermission):
#has_permission必须实现,在这里写权限逻辑
message = '用户没有权限访问该接口'
def has_permission(self, request, view):
#通过这个方法判断用户是否有权限访问接口
if request.user.usertype == 2:
#返回True,表示有权限访问接口
return True
#返回Flase,表示没有权限访问接口
return False
#局部使用(在视图内使用)
class BooksView(APIView):
##设置权限类
permission_classes = [MyBasePermission]
#全局使用
#REST_FRAMEWORK:配置Restframework的全局设置
REST_FRAMEWORK = {
'DEFAULT_PERMISSION_CLASSES':['tools.permissions.MyBasePermission'],
}
#注意:如果设置了全局的权限判断,在某些视图中如果不需要进行权限判断
class BooksView(APIView):
##设置权限类
permission_classes = []
#自定义节流类(方式一:BaseThrottle)
class MyVisitThrottle(BaseThrottle):
def __init__(self):
self.history = None
def allow_request(self, request, view):
#在这里完成节流的逻辑
#获取用户的主机ip(基于ip进行节流)
# ip_addr = request.META.get('REMOTE_ADDR')
ip_addr = self.get_ident(request)
# print(ip_addr)
##基于用户(id或者用户的其他唯一表示)
# ip_addr = request.user.id
#获取用户访问的时间
ctime = time.time()
if ip_addr not in VISIT_HISTORY:
#用户是第一次访问,返回True表示允许用户访问
VISIT_HISTORY[ip_addr] = [ctime]
return True
##获取用户的访问记录
history = VISIT_HISTORY[ip_addr]
self.history = history
##怎么判断用户,是否还有访问次数
## (是否访问达到了最大频率)
#12:04:00
# 12:02:30 12:03:00
while history and history[-1] < ctime-60:
history.pop()
print(len(history))
if len(history) <= 10:
history.insert(0,ctime)
return True
return False
def wait(self):
#提示用户需要等待的时间
"""
Optionally, return a recommended number of seconds to wait before
the next request.
"""
return 60 - (time.time() - self.history[-1])
#自定义节流类(方式二:SimpleRateThrottle)
class MySimpleRateThrottle(SimpleRateThrottle):
scope = 'throttle'
def get_cache_key(self, request, view):
return self.get_ident(request)
#return request.user.id
#需要在settings文件中添加如下参数
'DEFAULT_THROTTLE_RATES':{
'throttle':'10/m'
}
#局部使用
class BooksView(APIView):
###用户每一分钟只能够访问10次 10/60
###设置节流类,限定用户的访问频率
throttle_classes = [MySimpleRateThrottle]
#全局使用
REST_FRAMEWORK = {
'DEFAULT_THROTTLE_CLASSES':['tools.throttle.MySimpleRateThrottle'],
}
#注意:如果设置了全局的频率,在某些视图中如果想用户的请求频率,
class BooksView(APIView):
throttle_classes = []
##源码
#用户未登录每分钟允许访问10次,登录后每分钟允许访问30次
class MySimpleRateThrottle(SimpleRateThrottle):
scope = 'unlogin'
# scope = 'logined'
def get_cache_key(self, request, view):
return self.get_ident(request)
#return request.user.id
def get_rate_with_request(self,request):
#自定义get_rate_with_request:根据request,判断用户是否登录
#重置scope属性,获取登录或者未登录的请求频率
self.scope = 'logined' if request.user else 'unlogin'
return self.THROTTLE_RATES[self.scope]
def allow_request(self, request, view):
#在我们判断用户请求是否超出最大访问频率之前,
#更新num_requests, duration两个字段
self.rate = self.get_rate_with_request(request)
self.num_requests, self.duration = self.parse_rate(self.rate)
return super(MySimpleRateThrottle,self).allow_request(request, view)
'DEFAULT_THROTTLE_RATES':{
'unlogin': '10/m',
'logined': '30/m'
}
from rest_framework.versioning import BaseVersioning,URLPathVersioning
#自定义版本类(方法一)
class MyVersing(BaseVersioning):
def determine_version(self, request, *args, **kwargs):
#通过这个方法获取版本号,并返回
# http://127.0.0.1:8000/?version=v1
version = request._request.GET.get('version')
return version
#局部使用
class BooksView(APIView):
##设置版本类
versioning_class = MyVersing
#方式二:使用URLPathVersioning(推荐)
#局部使用
class BooksView(APIView):
##设置版本类
versioning_class = URLPathVersioning
#注意路由的格式:
urlpatterns = [
url(r'^(?P<version>[v1|v2]+)/users/$', users_list, name='users-list'),
url(r'^(?P<version>[v1|v2]+)/users/(?P<pk>[0-9]+)/$', users_detail, name='users-detail')
]
##方式三QueryParameterVersioning
class BooksView(APIView):
##设置版本类
versioning_class = QueryParameterVersioning
##全局配置版本
REST_FRAMEWORK = {
'DEFAULT_VERSIONING_CLASS':'rest_framework.versioning.URLPathVersioning',
'DEFAULT_VERSION':'v1',
'ALLOWED_VERSIONS':['v1','v3'],
'VERSION_PARAM':'version'
}
name=li&age=23
request._request.POST.get('')
#json数据格式
#如果请求头中Content-Type:application/json
{"name":"li","age":23}
data = json.loads(request.body.decode('utf-8'))
name = data.get('name')
age = data.get('age')
print(name,age)
# 使用DRF解析器
#from rest_framework.parsers import FormParser,JSONParser
#FormParser:Content-Type:application/x-www-form-urlencoded
#JSONParser:Content-Type:application/json
#局部使用解析器
class BooksView(APIView):
###设置解析器
parser_classes = [FormParser,JSONParser]
def post(self,request,*args,**kwargs):
#post请求获取数据
#设置解析器之后取值request.data
print(request.data.get('name'),request.data.get('age'))
return HttpResponse('post请求')
#全局设置
'DEFAULT_PARSER_CLASSES': (
'rest_framework.parsers.JSONParser',
'rest_framework.parsers.FormParser',
'rest_framework.parsers.MultiPartParser'
),
#选择使用哪种解析器
#源码从request.data方法入手,
#最终到达如下代码部分
def select_parser(self, request, parsers):
"""
Given a list of parsers and a media type, return the appropriate
parser to handle the incoming request.
"""
for parser in parsers:
if media_type_matches(parser.media_type, request.content_type):
return parser
return None
class MovieSerializer(serializers.Serializer):
"""电影列表的序列化"""
#使用HyperlinkedIdentityField,反向生成url地址
url = serializers.HyperlinkedIdentityField(
view_name='moviedetail',
#lookup_field:表中的id
lookup_field = 'id',
#lookup_url_kwarg:url地址上拼接的参数
lookup_url_kwarg = 'pk'
)
name = serializers.CharField()
publishtime = serializers.DateTimeField(format='%Y-%m-%d %H:%M')
#返回枚举的文本
country = serializers.CharField(source='get_country_display')
#获取分类的名称
category = serializers.CharField(source='category.name')
category_id = serializers.IntegerField(source='category.id')
#自定义序列化方法
category = serializers.SerializerMethodField()
def get_category(self,row):
#自定义序列化方法
#row:-> 当前序列化的类针对于哪个model的序列化,row就指的是这个model
return 要返回的数据
####方式二继承自serializers.ModelSerializer,是在serializers.Serializer基础上进行了封装,我们使用起来比较简单
class MovieDetailSerializer(serializers.ModelSerializer):
"""电影详情的序列化"""
class Meta:
#指定要序列化的model
model = Movie
#指定要序列化的字段
fields = "__all__" ##序列化所有字段
# fields = ['name','id'] ##序列化部分字段
####请求数据的验证(post请求)
class CategorySerializer(serializers.ModelSerializer):
#自定义错误信息
name = serializers.CharField(
error_messages={
'required':'分类名为必填字段'
}
)
class Meta:
model = Category
fields = '__all__'
#自定义序列化的验证方法
def validate_name(self,value):
if value == '动作片':
raise ValidationError('该分类数据已存在')
return value
####视图中使用序列化验证字段
class CategoryView(APIView):
def post(self,request,*args,**kwargs):
#添加分类数据
result = {
'code':1
}
data = request.data
ser = CategorySerializer(data=data)
if ser.is_valid():
print('验证通过')
print(ser.validated_data)
#验证通过,将数据保存至数据库
ser.save()
result['msg'] = '创建成功'
else:
print('数据验证失败',ser.errors)
result['code'] = 0
result['msg'] = '创建失败'
return Response(result)
######注意:如果序列化的类继承自serializers.Serializer,
#在更新或者添加数据的时候,必须实现update()、create()方法
# 相关实现可以在serializers.ModelSerializer寻找
class CategorySerializer(serializers.Serializer):
"""分类序列化"""
name = serializers.CharField(max_length=10)
info = serializers.CharField(max_length=256)
add_time = serializers.DateTimeField(format='%Y-%m-%d %H:%M')
def create(self, validated_data):
intance = Category.objects.create(**validated_data)
return intance
####方式一:继承自PageNumberPagination
http://www.baidu.com/?page=1&pagesize=20
from rest_framework.pagination import PageNumberPagination
class MyPageNumberPagination(PageNumberPagination):
#每页返回的条数( 默认值)
page_size = 2
#url地址上表示页码的参数
page_query_param = 'page'
#url地址上表示每页返回多少条数据的参数
page_size_query_param = 'pagesize'
#每页最多返回多少条数据
max_page_size = 20
####方式二:继承自LimitOffsetPagination
# http://www.baidu.com/?offset=10&limit=10
from rest_framework.pagination import LimitOffsetPagination
from collections import OrderedDict
class MyLimitOffsetPagination(LimitOffsetPagination):
##每页返回的条数( 默认值)
default_limit = 2
#url地址上表示返回多少条数据的参数
limit_query_param = 'limit'
#url地址上表示偏移量的参数
offset_query_param = 'offset'
#每页最多限定返回多少条数据
max_limit = None
####方式三:继承自CursorPagination
###分页方式三(会对url地址上的数据加密)
from rest_framework.pagination import CursorPagination
class MyCursorPagination(CursorPagination):
#url地址上的分页参数
cursor_query_param = 'cursor'
##每页返回的条数( 默认值)
page_size = 2
#设置排序字段(-id:表示倒叙,id:表示正序)
ordering = 'id'
# url地址上表示每页返回多少条数据的参数
page_size_query_param = 'pagesize'
#限定每页最多返回多少条数据
max_page_size = 20
###注意如果需要使用get_paginated_response返回分页的响应结果,并且需要对结果数据进行自定义
###可以重写get_paginated_response方法
#可以重写get_paginated_response方法,自定义分页返回结果数据
from collections import OrderedDict
def get_paginated_response(self, data):
return Response(OrderedDict([
('code',1),
('count', self.count),
('next', self.get_next_link()),
('previous', self.get_previous_link()),
('results', data)
]))
###在视图中的使用方式
class MovieView(APIView):
def get(self,request,*args,**kwargs):
#get请求获取电影列表
result = {
'code':1
}
pk = kwargs.get('pk')
if not pk:
# pk不存在:获取全部电影列表数据
#从电影表中查询所有数据
queryset = Movie.objects.all()
#先获取当前分页数据
#pg = MyPageNumberPagination()
# pg = MyLimitOffsetPagination()
pg = MyCursorPagination()
page_data = pg.paginate_queryset(
queryset=queryset,
request=request,
view=self
)
#使用MovieSerializer将queryset序列化
ser = MovieSerializer(instance=page_data,many=True,context={'request': request})
# result['count'] = queryset.count()
#返回分页格式的响应结果
return pg.get_paginated_response(ser.data)
作用:处理业务逻辑程,实现数据的增删该查
####最原始(FBV):
def ShoppingCar(request):
if request.method == "GET":
pass
elif request.method == "POST":
pass
....
####最原始:使用的是Django的View视图(CBV)
from django.views import View
class ShoppingCarView(View):
def get(self,request,*args,**kwargs):
pass
def post(self,request,*args,**kwargs):
pass
def delete(self,request,*args,**kwargs):
pass
def put(self,request,*args,**kwargs):
pass
def patch(self,request,*args,**kwargs):
pass
##进化1:使用的是restframework的View视图
class ShoppingCarView(APIView):
def get(self,request,*args,**kwargs):
pass
def post(self,request,*args,**kwargs):
pass
def delete(self,request,*args,**kwargs):
#获取商品的id
result = {
'code':1
}
try:
pk = kwargs.get('pk')
# from rest_framework.viewsets import ModelViewSet
instance = models.ShopingCar.objects.filter(user=request.user.id,good=pk).first()
if instance:
#从表中删除数据
instance.delete()
result['msg'] = '删除成功'
else:
result['code'] = 0
result['msg'] = '删除失败'
except Exception as err:
print(err)
result['code'] = 0
result['msg'] = '请求异常'
return Response(result)
def put(self,request,*args,**kwargs):
#自定义更新数据的方法
result = {
'code':1
}
data = request.data
#先获取需要更新的数据
pk = kwargs.get('pk')
instance = models.ShopingCar.objects.filter(id=pk).first()
if instance:
#默认进行全字段更新
ser = ShopingCarSerializer(instance=instance,data=data)
if ser.is_valid():
ser.save()
result['msg'] = '数据更新成功'
else:
result['code'] = 0
print(ser.errors)
result['msg'] = '数据更新失败'
else:
result['code'] = 0
result['msg'] = '数据更新失败,数据不存在'
return Response(result)
def patch(self,request,*args,**kwargs):
#自定义更新数据的方法
result = {
'code':1
}
data = request.data
#先获取需要更新的数据
pk = kwargs.get('pk')
instance = models.ShopingCar.objects.filter(id=pk).first()
if instance:
#设置partial=True,表示局部更新
ser = ShopingCarSerializer(instance=instance,data=data,partial=True)
if ser.is_valid():
ser.save()
result['msg'] = '数据更新成功'
else:
result['code'] = 0
print(ser.errors)
result['msg'] = '数据更新失败'
else:
result['code'] = 0
result['msg'] = '数据更新失败,数据不存在'
return Response(result)
##进化2:使用restframe视图中的GenericAPIView,
#####只是在ApiView的基础上实现了一些方法和参数,来实现序列化和分页等
from rest_framework.generics import GenericAPIView
class ShoppingCarGenericAPIView(GenericAPIView):
#获取数据库表中的结果集
queryset = models.ShopingCar.objects.all()
#指定要序列化的类
serializer_class = ShopingListCarSerializer
#添加分页,指定分页的类
pagination_class = MyPageNumberPagination
def get(self,request,*args,**kwargs):
car_goods = self.get_queryset()
#获取当前分页的数据
page_data = self.paginate_queryset(car_goods) #=> pg=MyPageNumberPagination() pg..paginate_queryset(car_goods)
#序列化分页数据
ser = self.get_serializer(instance=page_data,many=True) #=>ShopingListCarSerializer(instance=car_goods,many=True)
print(ser.data)
###获取如果做分页了,返回分页的结果集
return self.get_paginated_response(ser.data)
# return Response(ser.data)
####进化3:(注意如果使用如下视图,路由发生了该改变)
#使用GenericViewSet和ListModelMixin,UpdateModelMixin,
#CreateModelMixin,DestroyModelMixin,RetrieveModelMixin
##GenericViewSet:继承自ViewSetMixin, generics.GenericAPIView,一般情况下
##跟ListModelMixin,UpdateModelMixin,CreateModelMixin,DestroyModelMixin,
#GenericViewSet
# RetrieveModelMixin配合使用
# ListModelMixin:get请求,获取列表数据
# UpdateModelMixin:put请求,更新数据
# CreateModelMixin:post请求,创建数据
# DestroyModelMixin:delete请求,删除数据
# RetrieveModelMixin:get请求,获取详情数据
class ShoppingCarGenericAPIView(ListModelMixin,CreateModelMixin,
DestroyModelMixin,UpdateModelMixin,
RetrieveModelMixin,GenericViewSet):
# 获取数据库表中的结果集
queryset = models.ShopingCar.objects.all()
# 指定要序列化的类
serializer_class = ShopingListCarSerializer
# 添加分页,指定分页的类
pagination_class = MyPageNumberPagination
#动态设置序列化的类
def get_serializer_class(self):
if self.action == 'list':
return ShopingListCarSerializer
elif self.action == 'retrieve':
#获取详情数据
return ShopingCarDetailSerializer
return ShopingCarSerializer
def retrieve(self, request, *args, **kwargs):
result = {}
instance = self.get_object()
serializer = self.get_serializer(instance)
result['code'] = 1
result['data'] = serializer.data
return Response(result)
#### 注意路由的变化:
url(r'^api/(?P<version>[v1|v2]+)/shopcar/$', ShoppingCarGenericAPIView.as_view({'get': 'list','post':'create'})),
#destroy
url(r'^api/(?P<version>[v1|v2]+)/shopcar/(?P<pk>\d+)/$', ShoppingCarGenericAPIView.as_view({"get":"retrieve",'delete': 'destroy',"put":"update",'patch':'partial_update'})),
####总结:
1.如果视图部分的逻辑比较复杂,ListModelMixin...都满足不了的时候,建议使用APiView
写视图类
2.如果只是要实现一些简单的增删改查功能,也可以重写xxxxModelMixin中的方法实现自己的视图逻辑
# GenericViewSet和一下类配合使用
# RetrieveModelMixin配合使用
# ListModelMixin:get请求,获取列表数据
# UpdateModelMixin:put请求,更新数据
# CreateModelMixin:post请求,创建数据
# DestroyModelMixin:delete请求,删除数据
# RetrieveModelMixin:get请求,获取详情数据
3.如果要实现全部的增删改查可以直接继承自ModelViewSet
自动路由匹配使用如下
from rest_framework.routers import DefaultRouter
##实例化自动路由对象
router = DefaultRouter()
router.register(r'shopcar',ShoppingCarGenericAPIView,base_name='shopcar')
urlpatterns = [
url(r'^api/(?P<version>[v1|v2]+)/',include(router.urls))
]
##会帮我们生成如下url地址
#^api/(?P<version>[v1|v2]+)/ ^shopcar/$ [name='shopcar-list']
#^api/(?P<version>[v1|v2]+)/ ^shopcar\.(?P<format>[a-z0-9]+)/?$ [name='shopcar-list']
#^api/(?P<version>[v1|v2]+)/ ^shopcar/(?P<pk>[^/.]+)/$ [name='shopcar-detail']
#^api/(?P<version>[v1|v2]+)/ ^shopcar/(?P<pk>[^/.]+)\.(?P<format>[a-z0-9]+)/?$ [name='shopcar-detail']
返回数据,展示的模版
from rest_framework.renderers import JSONRenderer,BrowsableAPIRenderer,AdminRenderer
##使用:
class ShoppingCarGenericAPIView(ModelViewSet):
##设置渲染器
renderer_classes = [JSONRenderer,AdminRenderer]
因篇幅问题不能全部显示,请点此查看更多更全内容
Copyright © 2019- obuygou.com 版权所有 赣ICP备2024042798号-5
违法及侵权请联系:TEL:199 18 7713 E-MAIL:2724546146@qq.com
本站由北京市万商天勤律师事务所王兴未律师提供法律服务