目前,咱们的API对谁能够编辑或删除代码段没有任何限制。咱们想要一些更先进的行为,以确保:html
咱们将对咱们的Snippet
模型类进行几回更改。首先,咱们添加几个字段。其中一个字段将用于表示建立代码段的用户。另外一个字段将用于存储代码的突出显示的HTML表示。python
将如下两个字段添加到Snippet
模型中models.py
。sql
owner = models.ForeignKey('auth.User', related_name='snippets', on_delete=models.CASCADE) #获取记录 删除级联 highlighted = models.TextField()
咱们还须要确保在保存模型时,使用pygments
代码突出显示库填充突出显示的字段。数据库
咱们须要一些额外的导入:django
from pygments.lexers import get_lexer_by_name from pygments.formatters.html import HtmlFormatter from pygments import highlight
如今咱们能够.save()
在咱们的模型类中添加一个方法:编程
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(Snippet, self).save(*args, **kwargs)
完成这些工做后,咱们须要更新咱们的数据库表。一般咱们将建立一个数据库迁移,为了作到这一点,但为了本教程的目的,咱们只需删除数据库并从新开始。api
rm -f tmp.db db.sqlite3 rm -r snippets/migrations python manage.py makemigrations snippets python manage.py migrate
您可能还须要建立几个不一样的用户,以用于测试API。执行此操做的最快方法是使用createsuperuser
命令。浏览器
python manage.py createsuperuser
如今咱们有一些用户可使用,咱们最好将这些用户的表示添加到咱们的API中。建立一个新的串行器很容易。在serializers.py
添加:框架
from django.contrib.auth.models import User class UserSerializer(serializers.ModelSerializer): snippets = serializers.PrimaryKeyRelatedField(many=True, queryset=Snippet.objects.all()) class Meta: model = User fields = ('id', 'username', 'snippets')
由于'snippets'
是用户模型的反向关系,因此在使用ModelSerializer
该类时它不会被默认包含,因此咱们须要为它添加一个显式字段。ide
咱们还会添加几个视图views.py
。咱们但愿只使用只读视图为用户表示,因此咱们将使用ListAPIView
和RetrieveAPIView
通用的基于类的意见。
from django.contrib.auth.models import User class UserList(generics.ListAPIView): queryset = User.objects.all() serializer_class = UserSerializer class UserDetail(generics.RetrieveAPIView): queryset = User.objects.all() serializer_class = UserSerializer
确保也导入UserSerializer
类
from snippets.serializers import UserSerializer
最后,咱们须要经过从URL conf引用它们将这些视图添加到API中。将如下内容添加到其中的模式中urls.py
。
url(r'^users/$', views.UserList.as_view()), url(r'^users/(?P<pk>[0-9]+)/$', views.UserDetail.as_view()),
如今,若是咱们建立了一个代码片断,那么就没有办法将建立该代码段的用户与代码段实例进行关联。用户不是做为序列化表示的一部分发送的,而是传入请求的属性。
咱们处理的方式是覆盖.perform_create()
咱们的代码片断视图上的方法,这样咱们能够修改实例保存的管理方式,并处理传入请求或请求的URL中隐含的任何信息。
在SnippetList
视图类中,添加如下方法:
def perform_create(self, serializer): serializer.save(owner=self.request.user)
create()
咱们的串行器的方法如今将被传递一个附加'owner'
字段,以及请求中验证的数据。
如今,这些片断与建立它们的用户相关联,咱们更新咱们SnippetSerializer
来反映这一点。将如下字段添加到序列化器定义中serializers.py
:
owner = serializers.ReadOnlyField(source='owner.username')
注意:确保您还添加'owner',
到内部Meta
类的字段列表。
这个领域正在作一些颇有趣的事情。的source
哪一个属性参数控制用于填充的字段,而且能够在对串行化实例的任何属性点。它也能够采用上面显示的点划线,在这种状况下,它将以与Django的模板语言同样的方式遍历给定的属性。
咱们添加了字段是类型化ReadOnlyField
类,相对于其余类型的字段,如CharField
,BooleanField
等...类型化ReadOnlyField
始终是只读的,而且将用于序列化表示形式,但不会被用于更新模型他们被反序列化的实例。咱们也能够CharField(read_only=True)
在这里使用。
如今,代码片断与用户相关联,咱们但愿确保只有通过身份验证的用户才能建立,更新和删除代码段。
REST框架包括许多权限类,咱们可使用它来限制哪些人能够访问给定的视图。在这种状况下,咱们正在寻找的是IsAuthenticatedOrReadOnly
,这将确保通过身份验证的请求得到读写访问权限,未经身份验证的请求将得到只读访问权限。
首先在视图模块中添加如下导入
from rest_framework import permissions
接着,下面的属性添加到都在SnippetList
和SnippetDetail
视图类。
permission_classes = (permissions.IsAuthenticatedOrReadOnly,)
若是您打开浏览器并浏览到可浏览的API,那么您将发现没法再建立新的代码段。为了作到这一点,咱们须要可以以用户身份登陆。
咱们能够经过编辑项目级urls.py
文件中的URLconf来添加可浏览API使用的登陆视图。
在文件顶部添加如下导入:
from django.conf.urls import include
并且,在文件末尾添加一个模式以包括可浏览的API的登陆和注销视图。
urlpatterns += [ url(r'^api-auth/', include('rest_framework.urls', namespace='rest_framework')), ]
r'^api-auth/'
模式的一部分实际上能够是您想要使用的任何URL。惟一的限制是包含的URL必须使用'rest_framework'
命名空间。在Django 1.9+中,REST框架将设置命名空间,所以您能够将其删除。
如今,若是再次打开浏览器并刷新页面,您将在页面右上方看到一个“登陆”连接。若是您以您以前建立的用户身份登陆,则能够再次建立代码段。
建立了一些代码片断后,导航到“/ users /”端点,并注意到每一个用户的“片断”字段中,该表示包含与每一个用户相关联的代码段的列表。
咱们但愿全部的代码段均可以被任何人看到,但也要确保只有建立代码段的用户才能更新或删除它。
为此,咱们将须要建立一个自定义权限。
在片断应用中,建立一个新文件, 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
如今,咱们能够经过编辑视图类中的permission_classes
属性将该自定义权限添加到咱们的代码段实例端点SnippetDetail
:
permission_classes = (permissions.IsAuthenticatedOrReadOnly, IsOwnerOrReadOnly,)
确保也导入IsOwnerOrReadOnly
类。
from snippets.permissions import IsOwnerOrReadOnly
如今,若是再次打开浏览器,您会发现若是您以与建立代码段相同的用户身份登陆,“DELETE”和“PUT”操做只会显示在代码段实例端点上。
由于咱们如今有一组API的权限,若是咱们要编辑任何代码片断,咱们须要验证咱们的请求。咱们尚未设置任何身份验证类,因此默认值如今被应用,哪些是SessionAuthentication
和BasicAuthentication
。
当咱们经过Web浏览器与API进行交互时,咱们能够登陆,而后浏览器会话将为请求提供所需的身份验证。
若是咱们以编程方式与API交互,咱们须要在每一个请求上显式提供身份验证凭据。
若是咱们尝试建立一个没有验证的代码段,咱们会获得一个错误:
http POST http://127.0.0.1:8000/snippets/ code="print 123" { "detail": "Authentication credentials were not provided." }
咱们能够经过包括咱们以前建立的一个用户的用户名和密码来成功申请。
http -a tom:password123 POST http://127.0.0.1:8000/snippets/ code="print 789" { "id": 1, "owner": "tom", "title": "foo", "code": "print 789", "linenos": false, "language": "python", "style": "friendly" }
咱们如今已经在咱们的Web API上得到了至关精细的权限,并为系统的用户和他们建立的代码段提供了终点。
接下来咱们将介绍如何经过为突出显示的片断建立一个HTML端点来将全部内容结合在一块儿,并经过使用系统内部关系的超连接来提升API的凝聚力。