Django REST framework 学习纪要 Tutor

作者: 流月0 | 来源:发表于2017-12-04 20:23 被阅读0次

    目前为止,我们的代码没有限制谁可以编辑和删除代码片段,此节我们需要实现以下功能

    • 代码片段需要与创建者关联
    • 只有通过验证的用户才能创建代码片段
    • 只有创建者才能修改或删除代码片段
    • 没有通过验证的用户拥有只读权限

    给model添加字段

    我们需要添加两个字段,一个用于存储代码片段的创建者信息,一个用于存储代码的高亮信息

        style = models.CharField(choices=STYLE_CHOICES, default='friendly', max_length=100)
        owner = models.ForeignKey('auth.User', related_name='snippets', on_delete=models.CASCADE)
        highlighted = models.TextField()
    

    同时,我们需要在该模型类执行保存操作时,自动填充highlighted字段,使用pygments库。
    首先,导入一些包

    from pygments.lexers import get_lexer_by_name
    from pygments.formatters.html import HtmlFormatter
    from pygments import highlight
    

    然后为Snippet重写父类的save方法

        def save(self, *args, **kwargs):
            """
            use the 'pygments' library to create a highlighted HTML
            representation of 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)
    

    接下来需要迁移数据库,方便起见,删库,然后重新迁移

    (django_rest_framework) [root@localhost tutorial]# rm -f tmp.db db.sqlite3 && \
    > rm -rf snippets/migrations/ && \
    > python manage.py makemigrations snippets && \
    > python manage.py migrate
    Migrations for 'snippets':
      snippets/migrations/0001_initial.py
        - Create model Snippet
    Operations to perform:
      Apply all migrations: admin, auth, contenttypes, sessions, snippets
    Running migrations:
      Applying contenttypes.0001_initial... OK
      Applying auth.0001_initial... OK
      Applying admin.0001_initial... OK
      Applying admin.0002_logentry_remove_auto_add... OK
      Applying contenttypes.0002_remove_content_type_name... OK
      Applying auth.0002_alter_permission_name_max_length... OK
      Applying auth.0003_alter_user_email_max_length... OK
      Applying auth.0004_alter_user_username_opts... OK
      Applying auth.0005_alter_user_last_login_null... OK
      Applying auth.0006_require_contenttypes_0002... OK
      Applying auth.0007_alter_validators_add_error_messages... OK
      Applying auth.0008_alter_user_username_max_length... OK
      Applying sessions.0001_initial... OK
      Applying snippets.0001_initial... OK
    

    为了测试API,我们需要创建一些用户,最快的方式就是通过createsuperuser命令

    (django_rest_framework) [root@localhost tutorial]# python manage.py createsuperuser
    Username (leave blank to use 'root'): song
    Email address: shelmingsong@gmail.com
    Password: 
    Password (again): 
    Superuser created successfully.
    (django_rest_framework) [root@localhost tutorial]# python manage.py createsuperuser
    Username (leave blank to use 'root'): user_1
    Email address: user_1@gmail.com
    Password: 
    Password (again): 
    Superuser created successfully.
    (django_rest_framework) [root@localhost tutorial]# python manage.py createsuperuser
    Username (leave blank to use 'root'): user_2
    Email address: user_2@gmail.com
    Password: 
    Password (again): 
    Superuser created successfully.
    

    为用户模型添加接口

    我们已经创建了三个用户,现在我们需要添加用户相关的接口,修改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')
    

    因为snippetsuser是一种反向的关联,默认不会包含入ModelSerializer类中,所以需要我们手动添加

    我们也需要对views.py进行修改,由于用户页面为只读,所以继承于ListAPIViewRetrieveAPIView

    from django.contrib.auth.models import User
    from snippets.serializers import UserSerializer
    
    
    class UserList(generics.ListAPIView):
        queryset = User.objects.all()
        serializer_class = UserSerializer
    
    
    class UserDetail(generics.RetrieveAPIView):
        queryset = User.objects.all()
        serializer_class = UserSerializer
    

    配置url.py

        url(r'^users/$', views.UserList.as_view()),
        url(r'^users/(?P<pk>[0-9]+)/$', views.UserDetail.as_view()),
    

    关联User和Snippet

    此时我们创建一个代码片段,是无法与用户关联的,因为用户信息是通过request获取的。
    因此我们需要重写snippet的view中perform_create()方法,这个方法允许我们在对象保存前进行相关操作,处理任何有requestrequested URL传递进来的数据

    修改views.py中的SnippetList类,添加perform_create()方法

        def perform_create(self, serializer):
            serializer.save(owner=self.request.user)
    

    如此,新建代码片段时,会添加owner字段,该字段存储了request中的用户信息

    更新serializer

    之前我们在views中的SnippetList类中添加了perform_create方法,保存了owner信息,因而也需要在serializer中的SnippetSerializer类中添加owner信息,同时将owner添加进Meta子类的fields字段中

    class SnippetSerializer(serializers.ModelSerializer):
        owner = serializers.ReadOnlyField(source='owner.username')
    
        class Meta:
            model = Snippet
            fields = ('id', 'title', 'code', 'linenos', 'language', 'style', 'owner')
    

    这里我们使用了ReadOnlyField类型,这个类型是只读的,不能被更新,和Charfield(read_only=True)是一样的效果

    添加权限认证

    我们希望只有登录的用户能够去增加代码片段,未登录则只有查看的权限,此时我们需要用到IsAuthenticatedOrReadOnly

    修改views.py,为snippet的两个类views添加permission_classes字段

    from rest_framework import permissions
    
    
    class SnippetList(generics.ListCreateAPIView):
        permission_classes = (permissions.IsAuthenticatedOrReadOnly, )
    
    
    class SnippetDetail(generics.RetrieveUpdateDestroyAPIView):
        permission_classes = (permissions.IsAuthenticatedOrReadOnly, )
    

    添加登陆接口

    修改项目的urls.py

    urlpatterns = [
        url(r'^api-auth/', include('rest_framework.urls', namespace='rest_framework'))
    ]
    

    r'^api-auth/'可以自定,namespace在Django 1.9 + 的版本中可以省略

    运行Django服务器,访问your.domain/snippets/,点击右上角的登陆按钮,登陆我们之前创建的用户后,就可以创建代码片段了

    创建完几个代码片段后,再访问your.domain/users/时,就可以看到每个用户创建了哪几个代码片段了

    对象级别的权限

    现在用户都可以对所有的snippets进行增删改查,我们要确保只有创建者可以对snippets进行改动或删除。

    snippetsapp中,创建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):
            # allow all user to read
            if request.method in permissions.SAFE_METHODS:
                return True
    
            # only allow owner to edit
            return obj.owner == request.user
    

    views.py中添加权限

    from snippets.permissions import IsOwnerOrReadOnly
    
    class SnippetDetail(generics.RetrieveUpdateDestroyAPIView):
        permission_classes = (permissions.IsAuthenticatedOrReadOnly,
                              IsOwnerOrReadOnly)
    

    此时我们访问your.domain/snippets/1/,若用户未登录或登录用户不是该snippets的创建者,则只有读的权限,页面上表现为没有DELETE(上方中间)和PUT(右下角)按钮

    通过接口进行权限认证

    之前我们是通过浏览器页面进行登录的,而当我们直接使用接口去请求时,如果没有进行登录,而对某个snippet进行修改或是创建一个新的snippet,则会报错

    (django_rest_framework) [root@localhost django_rest_framework]# http POST http://127.0.0.1:80/snippets/ code="hahah"
    HTTP/1.0 403 Forbidden
    Allow: GET, POST, HEAD, OPTIONS
    Content-Length: 58
    Content-Type: application/json
    Date: Tue, 28 Nov 2017 14:56:18 GMT
    Server: WSGIServer/0.2 CPython/3.6.3
    Vary: Accept, Cookie
    X-Frame-Options: SAMEORIGIN
    
    {
        "detail": "Authentication credentials were not provided."
    }
    
    (django_rest_framework) [root@localhost django_rest_framework]# http POST http://127.0.0.1:80/snippets/1/ code="hahah"
    HTTP/1.0 403 Forbidden
    Allow: GET, PUT, PATCH, DELETE, HEAD, OPTIONS
    Content-Length: 58
    Content-Type: application/json
    Date: Tue, 28 Nov 2017 14:56:26 GMT
    Server: WSGIServer/0.2 CPython/3.6.3
    Vary: Accept, Cookie
    X-Frame-Options: SAMEORIGIN
    
    {
        "detail": "Authentication credentials were not provided."
    }
    

    我们在发送请求时,提供用户名和密码,就可以进行操作了

    (django_rest_framework) [root@localhost django_rest_framework]# http -a your_username:your_password POST http://127.0.0.1:80/snippets/ code="hahah"
    HTTP/1.0 201 Created
    Allow: GET, POST, HEAD, OPTIONS
    Content-Length: 104
    Content-Type: application/json
    Date: Tue, 28 Nov 2017 14:58:10 GMT
    Server: WSGIServer/0.2 CPython/3.6.3
    Vary: Accept, Cookie
    X-Frame-Options: SAMEORIGIN
    
    {
        "code": "hahah",
        "id": 4,
        "language": "python",
        "linenos": false,
        "owner": "song",
        "style": "friendly",
        "title": ""
    }
    

    关于

    本人是初学Django REST framework,Django REST framework 学习纪要系列文章是我从官网文档学习后的初步消化成果,如有错误,欢迎指正。

    学习用代码Github仓库:shelmingsong/django_rest_framework

    本文参考的官网文档:Tutorial 4: Authentication & Permissions

    博客更新地址

    相关文章

      网友评论

        本文标题:Django REST framework 学习纪要 Tutor

        本文链接:https://www.haomeiwen.com/subject/pnkfixtx.html