Authentication & Permissions
认证和权限
目前我们的API对于谁可以编辑和删除MyLesson没有任何限制,我们希望添加一些高级的行为使程序具有如下功能:
MyLesson有相应的创建者
只有经过认证的user可以创建MyLesson
只有MyLesson的创建者可以修改和删除MyLesson
没有认证的求情只有可读属性
【1】为model增加信息
改变一些MyLesson的model类,首先添加一对Fields,其中一个用来表示MyLesson的创建者,另外一个存储代码的高亮HTML的表现形式
owner = models.ForeignKey('auth.User',related_name='myLesson',on_delete=models.CASCADE) highlighted = models.TextField()
# 对于django.db.models,参考官方文档:https://docs.djangoproject.com/en/1.9/ref/models/fields/#model-field-types
# 文档的右下角可以选择django的版本
# 本程序使用
# DateTimeField(auto_now_add=True),
# CharField(max_length=100, blank=True,default='')
# TextField()
# BooleanField(default=False)
# CharField(choices=LANGUAGE_CHOICES,default='python',max_length=100)
# ForeignKey('auth.User',related_name='myLesson',on_delete=models.CASCADE)
我们需要确定的是,当model被保存时,我们植入了highlighted field
使用pygments代码高亮库来完成
from pygments.lexers import get_lexer_by_name from pygments.formatters.html import HtmlFormatter from pygments import highlight def save(self, *args, **kwargs): """ Use the `pygments` library to create a highlighted HTML representation of the code snippet. """ lexer = get_lexer_by_name(self.language) linenos = self.linenos and 'table' or False options = self.title and {'title': self.title} or {} formatter = HtmlFormatter(style=self.style, linenos=linenos, full=True, **options) self.highlighted = highlight(self.code, lexer, formatter) super(MyLesson, self).save(*args, **kwargs) # limit the number of instances retained myLesson = MyLesson.objects.all() if len(myLesson) > 100: myLesson[0].delete()
然后更新数据库表单,通常我们会迁移数据库,但是在这里,我们直接删掉重新开始
rm -f db.sqlite3
rm -r myLesson/migrations
python manage.py makemigrations myLesson
python manage.py migrate
然后创建几个用户做测试
python manage.py createsuperuser
【2】为User model增加endpoints
给API添加User的展示页面
在serializer.py中:
from django.contrib.auth.models import User class UserSerializer(serializers.ModelSerializer): myLesson = serializers.PrimaryKeyRelatedField(many=True,queryset=MyLesson.objects.all()) class Meta: model = User fields = ('id','username','myLesson')
因为MyLesson和User是是反向关联(reverse relationship,即多对一),所以不会默认添加在ModelSerializer类中,我们需要添加一个明确的field
在views.py中我们需要一个只读的view对User做展示,所以我们添加ListAPIView和RetrieveAPIView
from django.contrib.auth.models import User from myLesson.serializers import MyLessonSerializer,UserSerializer class UserList(generics.ListAPIView): queryset = User.objects.all() serializer_class = UserSerializer class UserDetail(generics.RetrieveAPIView): queryset = User.objects.all() serializer_class = UserSerializer
最后添加这些views到API中,修改urls.py:
url(r'^users/$',views.UserList.as_view()), url(r'^users/(?P<pk>[0-9]+)/$',views.UserDetail.as_view()),
【3】连接MyLesson和User
目前,User没有被作为序列化展示的一部分,相反,成了request中的一个属性。
我们在MyLesson views中重写一个perform_create()方法,允许我们修改 实例如何保存,处理隐藏在request中的信息
在MyLessonList类中添加:
def perform_create(self,serializer): serializer.save(owner=self.request.user)
在create方法中传递owner field,还有我们要创建的数据
【4】升级serializer
在MyLesson中添加owner属性
class MyLessonSerializer(serializers.ModelSerializer): owner = serializers.ReadOnlyField(source='owner.username') class Meta: model = MyLesson fields = ('id','title','code','linenos','language','style','owner')
ReadOnlyField非常有趣,source参数用来植入一个field,可以用任何Serializer类中的属性,ReadOnlyField可以用CharField(read_only=True)代替
【5】为view添加必要的权限
我们希望认证用户可以更新,删除,和创建MyLesson
Rest framework提供了permission类,使我们可以限制谁可以键入一个给定的视图,使用IsAuthenticatedOrReadOnly,来确使认证用户可以读写,其他用户只能只读
在MyLessonList和MyLessonDetail view类中添加如下属性
from rest_framework import permissions permission_classes = (permissions.IsAuthenticatedOrReadOnly,)
【6】为API添加登陆
在项目级别的urls.py中添加以下pattern,允许登入登出
from django.conf.urls import include url(r'^api-auth/', include('rest_framework.urls',namespace='rest_framework')),
【7】对象级别的权限
只有对象的创建者可以删除和修改:
在MyLesson app中添加permissions.py
from rest_framework import permissions class IsOwnerOrReadOnly(permissions.BasePermission): """ Custom permission to only allow owners of an object to edit it. """ def has_object_permission(self,request,view,obj): #Read permissions are allowed to any request, #so we`ll always allow GET,HEAD or OPTIONS requests. if request.method in permissions.SAFE_METHODS: return True #Write permissions are only allowed to the owner of the snippet return obj.owner == request.user
在views.py中MyLessonDetail中加入
from myLesson.permissions import IsOwnerOrReadOnly permission_classes = (permissions.IsAuthenticatedOrReadOnly,IsOwnerOrReadOnly,)