身份验证与权限
前面举例项目的API对谁可以编辑或删除项目没有任何限制。而希望有一些更高级的行为,以确保:
- 项目总是与创建者相关联。
- 只有经过身份验证(登录后)的用户才能创建项目。
- 只有项目的创建者才能更新或删除它。
- 未经身份验证(游客)的请求应该具有完全只读访问权限。
modules.py 如下:
方便演示这里直接使用auth应用的User模型
from django.db import models
class Student(models.Model):
"""
学生表
"""
name = models.ForeignKey('auth.User', verbose_name='学生姓名',null=True, help_text='学生姓名', on_delete=models.SET_NULL,
related_name='student')
gender = models.SmallIntegerField('性别', default='1', help_text='性别')
hobby = models.CharField('爱好', max_length=200, null=True, blank=True, help_text='兴趣爱好')
age = models.IntegerField('年龄', null=True, )
create_time = models.DateTimeField('创建时间', auto_now_add=True, help_text='创建时间')
class Meta:
"""
元数据,
"""
db_table = 'student' # 指定当前模型创建的表明,不写默认当前的模型名Student
verbose_name = '学生信息表' # 注释
verbose_name_plural = verbose_name # 指定为复数
def __str__(self):
return f'name:{self.name},hobby:{self.hobby}' # 输出项目名称,方便于调式
class Subject(models.Model):
name = models.CharField(verbose_name='科目名称', max_length=100, null=True)
sutdent = models.ForeignKey(Student, on_delete=models.CASCADE) # 级联删除,删除主表数据时从表数据也会删除
创建用户
创建不同的用户用于测试API
python manage.py createsuperuser
最好将这些用户的表示添加到的API中,因此需要创建用户的序列化器。serializers.py代码如下:
from rest_framework import serializers
from project2s.models import Student, Subject
from django.contrib.auth.models import User
class ProjectSerializers(serializers.ModelSerializer):
name = serializers.ReadOnlyField(source='name.username')
class Meta:
model = Student
fields = ['id', 'name', 'hobby', 'age']
class SubjectSerializers(serializers.ModelSerializer):
class Meta:
model = Subject
fields = '__all__'
class UserSerializers(serializers.ModelSerializer):
student = serializers.PrimaryKeyRelatedField(many=True, queryset=Student.objects.all()) #因为'student'是用户模型的反向关系, 默认情况下,在使用ModelSerializer类时将不包含它,所以需要为它添加一个显式字段。
class Meta:
model = User
fields = ['id', 'username', 'student']
在views.py中添加几个视图。希望只对用户表示使用只读视图,因此使用通用视图ListAPIView和RetrieveAPIView。
from project2s.models import Student, Subject
from django.contrib.auth.models import User
from project2s.serializers import ProjectSerializers, SubjectSerializers, UserSerializers
from rest_framework importgenerics
from rest_framework import permissions
# 类视图
class ListCreateView(generics.ListCreateAPIView):
queryset = Student.objects.all()
serializer_class = ProjectSerializers
permission_classes = [permissions.IsAuthenticatedOrReadOnly] # 添加权限
def perform_create(self, serializer):
serializer.save(name=self.request.user)
class UpdatePutDeleteDetailView(generics.RetrieveUpdateDestroyAPIView):
queryset = Student.objects.all()
serializer_class = ProjectSerializers
permission_classes = [permissions.IsAuthenticatedOrReadOnly]
class ListSubjectView(generics.ListCreateAPIView):
queryset = Subject.objects.all()
serializer_class = SubjectSerializers
class ListUserView(generics.ListCreateAPIView): # 用户只读
queryset = User.objects.all()
serializer_class = UserSerializers
class UserDetailView(generics.RetrieveAPIView): # 用户详情
queryset = User.objects.all()
serializer_class = UserSerializers
urls.py路由配置
from django.urls import path
from project2s import views
from rest_framework.urlpatterns import format_suffix_patterns
app_name = 'project2s'
urlpatterns = [
path('students/', views.ListCreateView.as_view()), # 创建或展示
path('students/<int:pk>/', views.UpdatePutDeleteDetailView.as_view()), # 删除或显示
path('subject/', views.ListSubjectView.as_view()), # 查看
path('user/', views.ListUserView.as_view()), # 查看
path('user/<int:pk>', views.UserDetailView.as_view()) # 查看详情
]
urlpatterns = format_suffix_patterns(urlpatterns)
关联项目与用户
现在可以创建项目,并手动选择对应的用户与项目进行关联。但是在某些场景下这可能不是你想要的,比如谁创建项目就把谁与创建的项目进行关联。
那么用户就不能再作为序列化表示的一部分发送,而是传入请求的一个属性。django会将当前用户对象设置到request.user属性上,它是在中间件AuthenticationMiddleware中完成的。
首先修改项目的序列化器,将name字段修改为只读,并显示用户的用户名。
class ProjectSerializers(serializers.ModelSerializer):
name = serializers.ReadOnlyField(source='name.username')
class Meta:
model = Student
fields = ['id', 'name', 'hobby', 'age']
source参数控制哪个属性用于填充字段,并可以指向序列化实例上的任何属性。它也可以采用上面所示的句点符, 在这种情况下,它将遍历给定的属性, 与Django的模板语言使用的方式类似。
ReadOnlyField始终是只读的,并将用于序列化表示,但当模型实例被反序列化时,将不会用于更新它们。
然后再重写项目视图上的perform_create()方法,修改保存实例保存的方式,处理隐式传入请求或者请求URL中的任何信息,这里我们给项目关联当前用户。
在ListCreateView视图类上,添加以下方法:
class ListCreateView(generics.ListCreateAPIView):
queryset = Student.objects.all()
serializer_class = ProjectSerializers
def perform_create(self, serializer):
serializer.save(name=self.request.user)
添加登录功能
此时创建项目你会发现报错,这是因为,django再未做身份验证的请求的user属性上绑定了一个AnonymousUser对象。我们需要能够以用户身份登录。
通过编辑根urls.py文件中的URLconf,我们可以添加一个login视图来与可浏览的API一起使用。
在文件的末尾,添加一个模式,以包含可浏览API的登录和注销视图。
from django.contrib import admin
from django.urls import path, include
urlpatterns = [
path('admin/', admin.site.urls),
path('', include('project2s.urls')),
path('auth', include('rest_framework.urls')) # 登录功能
]
模式的’auth/'部分实际上可以是你想使用的任何URL。
现在,如果你再次打开浏览器并刷新页面,你会在页面的右上角看到一个“Login”链接。登录之前创建的用户,你将能够再次创建项目。
视图添加权限
现在希望确保只有经过身份验证的用户才能创建、更新和删除项目。REST框架包括许多权限类,我们可以使用它们来限制谁可以访问给定的视图。在这个例子中,我们要找的是IsAuthenticatedOrReadOnly,这将确保经过身份验证的请求获得读写访问权限,而未经身份验证的请求获得只读访问权限。
首先在views模块中添加以下导入
from rest_framework import permissions
然后,将下列属性添加到ListCreateView和UpdatePutDeleteDetailView视图类中。
class ListCreateView(generics.ListCreateAPIView):
queryset = Student.objects.all()
serializer_class = ProjectSerializers
permission_classes = [permissions.IsAuthenticatedOrReadOnly] # 添加权限
def perform_create(self, serializer):
serializer.save(name=self.request.user)
class UpdatePutDeleteDetailView(generics.RetrieveUpdateDestroyAPIView):
queryset = Student.objects.all()
serializer_class = ProjectSerializers
permission_classes = [permissions.IsAuthenticatedOrReadOnly] # 未登录时校验权限
permission_classes = [permissions.IsAuthenticatedOrReadOnly]
现在退出登录,会发现在项目列表页面的POST请求表单不见了
学生详情页的update,put按钮也看不见了
对象级权限
再创建一个用户并登录,会发现可以对前面用户创建的项目进行编辑。
实际上,希望所有项目对任何人都可见,但也要确保只有创建了项目的用户才能更新或删除它。
为此,需要创建一个自定义的权限。
在projects应用程序中,创建一个新文件permissions.py,编写如下代码:
from rest_framework import permissions
class IsStudentOrReadonly(permissions.BasePermission):
"""
自定义权限只允许对象的学生编辑
"""
def has_object_permission(self, request, view, obj):
if request.method in permissions.SAFE_METHODS:
return True
return obj.name == request.user
将它添加到UpdatePutDeleteDetailView视图上
class UpdatePutDeleteDetailView(generics.RetrieveUpdateDestroyAPIView):
queryset = Student.objects.all()
serializer_class = ProjectSerializers
permission_classes = [permissions.IsAuthenticatedOrReadOnly,IsStudentOrReadonly] # 未登录时校验权限
permission_classes = [permissions.IsAuthenticatedOrReadOnly,
IsOwnerOrReadOnly]
刷新后只有当登录用户与当前代码片段的创建用户相同时,DELETE和PUT按钮才会出现在页面上。