• Django-Rest-Framework 教程: 4. 验证和权限


    到目前为止, 我们的API并未指明哪些人有权限编辑或删除snippet, 接下来我们要实现:

    • 为snippet增加创建者
    • 特定用户才能创建snippet
    • snippet创建者才能更新或删除该snippet
    • 未授权用户只能查看

    1. 为snippet model增加field

    我们现为snippet model增加两个field, 一个用于储存创建者信息, 另一个则用作储存高亮HTML信息:

        # snippets/models.py
        from django.db import models
        from pygments.lexers import get_all_lexers
        from pygments.styles import get_all_styles
    
        LEXERS = [item for item in get_all_lexers() if item[1]]
        LANGUAGE_CHOICES = sorted([(item[1][0], item[0]) for item in LEXERS])
        STYLE_CHOICES = sorted((item, item) for item in get_all_styles())
    
    
        class Snippet(models.Model):
            created = models.DateTimeField(auto_now_add=True)
            title = models.CharField(max_length=100, blank=True, default='')
            code = models.TextField()
            linenos = models.BooleanField(default=False)
            language = models.CharField(choices=LANGUAGE_CHOICES,
                                        default='python',
                                        max_length=100)
            style = models.CharField(choices=STYLE_CHOICES,
                                     default='friendly',
                                     max_length=100)
            owner = models.ForeignKey('auth.User', related_name='snippets')
            highlighted = models.TextField()
    
            class Meta:
                ordering = ('created',)

    当执行save()时, 我们使用pygments生成高亮后的HTML:

        # snippets/models.py
        from pygments.lexers import get_lexer_by_name
        from pygments.formatters.html import HtmlFormatter
        from pygments import highlight
    
        class Snippet(models.Model):
    
            ...
    
            def save(self, *args, **kwargs):
                """
                使用pygments创建高亮的HTML文本
                """
                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)

    修改完毕之后我们删除原来的数据库, 然后重新创建数据库:

        python ./manage.py syncdb

    你可能需要再创建几个用户, 可以通过以下命令创建:

        python ./manage.py createsuperuser

    2. 为User model增加endpoints

    在serializer.py中增加UserSerializer:

        # snippets/serializers.py
        from django.contrib.auth.models import User
    
        class UserSerializer(serializers.ModelSerializer):
            snippets = serializers.PrimaryKeyRelatedField(many=True)
    
            class Meta:
                model = User
                fields = ('id', 'username', 'snippets')

    由于”snippets”是User的一个反向关系, 因此我们需要用field明确指明.

    我们只需要为user model添加只读的API, 因此使用ListAPIView和RetrieveAPIView即可:

        # snippets/views.py
        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

    最后修改urls.py, 添加users:

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

    3. 关联user和snippet

    如果现在我们通过API创建snippet, 我们无法将创建者将其关联起来. 因为创建者信息并不是以serialized数据传进来的, 而是通过request获取的.

    我们的解决方法是重写SnippetList和SnippetDetail view的pre_save()方法. 该方法允许我们处理request或request URL中的任何信息.

        # snippets/views.py
        class SnippetList(APIView):
    
            ...
    
            def pre_save(self, obj):
                obj.owner = self.request.user
    
    
        class SnippetDetail(APIView):
    
            ...
    
            def pre_save(self, obj):
                obj.owner = self.request.user

    4. 更新 serializer

    现在snippet已经与创建者关联, 接下来我们修改serializer来反映该变化:

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

    ower field十分有趣, source参数控制着使用snippet哪个attribute作为来源填充该field, 并且能使用”.”语法.

    Field类, 相对于其他field类(CharField, BooleanField等), 是只读field. 它能用作呈现序列化的数据, 但不会在凡序列化时被用做更新数据.

    5. 添加权限

    到此为止, snippet已经与user关联了. 接下来我们实现只有授权用户才能创建, 更新和删除snippet.

    Django REST Framework为我们提供了许多permission类. 在此, 我们使用IsAuthenticatedOrReadOnly, 它能保证只有授权用户才有读写权限, 而一般用户只有读得权限:

        # snippets/views.py
        from rest_framework import permissions
        class SnippetList(APIView):
    
            ...
    
            permission_classes = (permissions.IsAuthenticatedOrReadOnly,)
    
    
        class SnippetDetail(APIView):
    
            ...
    
            permission_classes = (permissions.IsAuthenticatedOrReadOnly,)

    6. 增加可浏览的授权API

    在tutorial/urls.py中:

        # 在tutorial/urls.py
    
        urlpatterns = patterns('',
                           ...
        )
    
        urlpatterns += patterns('',
            url(r'^api-auth/', include('rest_framework.urls',
                                       namespace='rest_framework')),
        )

    r’^api-auth/’可以是你能想得到的所有pattern. 到此你可以使用/api-auth/进行登录了. 登录成功之后你才能创建snippet.

    添加好snippet之后, 在浏览/users/ endpoint, 你可以发现每个用户下都有对应的snippet的pk.

    7. 添加对象权限

    接下来, 我们实现只有snippet的创建者才能更新, 编辑或删除snippet, 创建snippets/permissions.py:

        # snippets/permissions.py
        from rest_framework import permissions
    
    
        class IsOwnerOrReadOnly(permissions.BasePermission):
            """
            允许创建者编辑的自定义权限
            """
    
            def has_object_permission(self, request, view, obj):
                # 任何request都有只读权限, 所以总是允许GET, HEAD 或 OPTIONS
                if request.method in permissions.SAFE_METHODS:
                    return True
    
                # 只有snippet的创建者有写的权限
                return obj.owner == request.user

    修改SnippetDetail view:

        # snippets/views.py
        from snippets.permissions import IsOwnerOrReadOnly
    
    
        class SnippetDetail(APIView):
    
            ...
    
            permission_classes = (permissions.IsAuthenticatedOrReadOnly,
                          IsOwnerOrReadOnly,)

    8. 使用API授权

    目前为止, 我们没有设置 authentication classes, 因此 authentication classes 还是默认的SessionAuthentication和BasicAuthentication.

    当我们使用浏览器时, 我们可以通过浏览器登录并使用session授权接下来的request.

    但当我们使用程序调用API时, 我们必须为每次request提供授权信息:

        curl -X POST http://127.0.0.1:8000/snippets/ -d "code=print 789" -u tom:password
    
        {"id": 5, "owner": "tom", "title": "foo", "code": "print 789", "linenos": false, "language": "python", "style": "friendly"}

    原文链接: http://www.weiguda.com/blog/22/

  • 相关阅读:
    项目管理【44】 | 项目干系人管理-识别干系人
    移动端开发基础【15】H5和小程序开发注意事项
    召回率recall,IoU, mPA理解,针对video detection领域
    转:batch normalization, instance normalization, layer normalization, group normalization
    自监督(对比学习)资料
    转:非极大值抑制(Non-Maximum Suppression,NMS)
    转:Zero-shot Learning / One-shot Learning / Few-shot Learning
    转:top1错误率、top5正确率
    转:如何理解Inductive bias?
    台式机更新后没有声音了怎么办,Realtek音频管理器
  • 原文地址:https://www.cnblogs.com/leo23/p/5051915.html
Copyright © 2020-2023  润新知