到目前为止,DRF
学习到了一定的程度,可以写很基础的接口了。
不过,离实际应用还差了很远。目前接口的情况:任何人都可以发起请求,实际应用时是不允许的。在传统的django开发中,有@login_required
和@permission_required
两个装饰器分别对身份和权限进行限制,不过在drf
中内置有对应的方法。
所以现在要学习认证与权限。简单来说:
同时,DRF的认证和权限集成到了一起。DRF的认证是基于django的默认User模块,前面的User都是我自己写的,并且和django自带的User没有半毛钱关系,所以,干脆重写了一个app:Author。 即,从这里开始,代码发生了改变。
from django.db import models
from django.contrib.auth.models import User
from user.models import Company
from django.dispatch import receiver
from django.db.models.signals import post_save
"""
One2One的模式:增加数据的时候,只能从默认User模型中选择对象
"""
class UserExtension(models.Model):
user_name = models.OneToOneField(User, on_delete=models.CASCADE)
cn_name = models.CharField(max_length=10)
phone = models.CharField(('手机号码') ,max_length=11)
company = models.ForeignKey(Company, on_delete=models.SET('公司已删除'))
#信号,使用系统自带的user增加数据时,自动为UserExtension新增一个实例
# @receiver(post_save, sender=User)
# def create_userextension(sender, instance, created, **kewargs):
# if created:
# UserExtension.objects.create(username=instance, company_id=1, phone='13800138000')
from django.db.models import fields
from . models import UserExtension
from rest_framework import serializers
from rest_framework.exceptions import ValidationError
from django.contrib.auth.models import User
class UserSerializer(serializers.ModelSerializer):
class Meta:
model = User
fields = ('id', 'username', 'email')
class UserExListCreateSerializer(serializers.ModelSerializer):
user_name = UserSerializer()
class Meta:
model = UserExtension
fields = '__all__'
depth = 1
class UserExDetailPutDeleteSerializer(serializers.ModelSerializer):
user_name = serializers.CharField(read_only=True)
class Meta:
model = UserExtension
fields = '__all__'
from django.db.models.base import Model
from django.shortcuts import render
from .models import UserExtension
from . serializers import UserExListCreateSerializer, UserExDetailPutDeleteSerializer
from rest_framework.viewsets import ModelViewSet
from rest_framework import permissions
#自定义权限
class IsOwnerOrReadOnly(permissions.BasePermission):
def has_object_permission(self, request, view, obj):
print(obj.user_name)
print(request.user)
if request.method in ('GET',): # 判断请求方法未get时,任意用户都可以读
return True
return request.user == obj.user_name # 只有当前用户与obj的user_name相同时才能读写
class UserExViewSet(ModelViewSet):
queryset = UserExtension.objects.all()
# serializer_class = UserExSerializer
permission_classes = (IsOwnerOrReadOnly, )
def get_serializer_class(self):
serializer_class = self.serializer_class
if self.request.method in ('GET', 'POST'):
serializer_class = UserExListCreateSerializer
print(self.request)
return serializer_class
else:
serializer_class = UserExDetailPutDeleteSerializer
return serializer_class
from django.urls import path
from . import views
urlpatterns = [
path('user/', views.UserExViewSet.as_view({'get': 'list', 'post': 'create'})),
path('user/' , views.UserExViewSet.as_view({'get': 'retrieve', 'delete': 'destroy', 'put': 'update'})),
]
DRF自带一些认证与权限的预设方案,如:
更多权限可浏览源码rest_framework/permisions.py
。
这里需要理解一下自定义权限的逻辑。自定义权限要继承permissions.BasePermission
类,BasePermission
里有两个方法需要理解的,分别是has_permission
和 has_object_permission
:
class BasePermission(metaclass=BasePermissionMetaclass):
"""
A base class from which all permission classes should inherit.
"""
def has_permission(self, request, view):
"""
Return `True` if permission is granted, `False` otherwise.
"""
return True
def has_object_permission(self, request, view, obj):
"""
Return `True` if permission is granted, `False` otherwise.
"""
return True
经过测试,得出以下结论:
1、读写多个对象时,需要has_permission
返回True,反之则无权限
2、读写单个对象时,需要has_object_permission
返回True,反之则无权限
上面我的自定义权限的写法为:
#自定义权限
class IsOwnerOrReadOnly(permissions.BasePermission):
def has_object_permission(self, request, view, obj):
print(obj.user_name)
print(request.user)
if request.method in ('GET',): # 判断请求方法未get时,任意用户都可以读
return True
return request.user == obj.user_name # 只有当前用户与obj的user_name相同时才能读写
分析一下:
1、当为get请求时,任意用户(包含匿名用户)均仅读
2、当request.user 和 当前所请求的obj中的user_name 一致时,可以读写。
这就实现了用户只能修改自己创建的数据,同时也可以读他人的数据。有两点需要说明一下:
1、request.user
是django自带的User
,即使用自带的user所创建的用户
2、obj.user_name
是appUserExtension
的,这个字段和django自带的是One2One关系。
python代码是按顺序执行的,意味着,可以同时写几个权限的逻辑,但必须保证每一个逻辑都要有布尔返回值。
本案例中,权限是通过视图中的permision_class
属性来设置的,这个权限仅在该视图中生效,如果某些权限要在全局生效,可以在settings中设置,DRF为我们提供了对应的全局设置方案。
REST_FRAMEWORK = {
'DEFAULT_PERMISSION_CLASSES': (
'rest_framework.permissions.IsAuthenticated',
)
}
如果视图是基于函数的,还可以使用下面的装饰器:
from rest_framework.decorators import api_view, permission_classes
from rest_framework.permissions import IsAuthenticated
@api_view(['GET'])
@permission_classes((IsAuthenticated, ))
def func(request): pass
暂时还不懂,跳过。