美文网首页
Django_REST Framework -5.验证与授权

Django_REST Framework -5.验证与授权

作者: 古佛青灯度流年 | 来源:发表于2018-06-27 12:15 被阅读13次

    验证与授权

    目前来看,我们的 API 并没有权限上的限制(即任何人都可以编辑或删除我们的 Movies ),这不是我们想要的。所以我们需要在 API 上做些限制以确保:

    • Movies 与 Users 关联起来。
    • 只有授权了的用户才能创建新的 Movies。
    • 只有 Movies 的创建者才可以更新或删除它。
    • 未授权的用户只能进行查看。

    在 models 中增加以下信息

    我们先把之前注释掉的

    director = models.ForeignKey('celebrity', related_name='Movies')
    
    class celebrity(models.Model):
        name = models.CharField(max_length=100, blank=True, default='')
        age = models.IntegerField()
        gender = models.CharField(choices=GENDER_CHOICES, default='male', max_length=20)
    
    

    关联导演类的注释解开,来看看多张表在生成的 api 里的关联性。

    接着在 models.py 中的 Movies 类中加入以下代码来确定 Movies 的创建者:

    owner = models.ForeignKey('auth.User', related_name='Movies')
    

    最后 models.py 代码为:

    #!/usr/bin/python
    # -*- coding: utf-8 -*-
    
    from django.db import models
    
    # 举个栗子
    COUNTRY_CHOICES = (
        ('US', 'US'),
        ('Asia', 'Asia'),
        ('CN', 'CN'),
        ('TW', 'TW'),
    )
    TYPE_CHOICES = (
        ('Drama', 'Drama'),
        ('Thriller', 'Thriller'),
        ('Sci-Fi', 'Sci-Fi'),
        ('Romance', 'Romance'),
        ('Comedy', 'Comedy')
    )
    GENDER_CHOICES = (
        ('male', 'male'),
        ('female', 'female')
    )
    
    class Movies(models.Model):
        title = models.CharField(max_length=100, blank=True, default='')
        year = models.CharField(max_length=20)
        # 在 director 关联了 Movies 类 和 celecrity 类, 在第4章会用到 celebrity 类
        director = models.ForeignKey('celebrity', related_name='movies')
        # 关联 User 类来确定 Movies 的创建者
        owner = models.ForeignKey('auth.User', related_name='movies')
        country = models.CharField(choices=COUNTRY_CHOICES, default='US', max_length=20)
        type = models.CharField(choices=TYPE_CHOICES, default='Romance', max_length=20)
        rating = models.DecimalField(max_digits=3, decimal_places=1)
        created = models.DateTimeField(auto_now_add=True)
    
        class Meta:
            ordering = ('created',)
    
    class celebrity(models.Model):
        name = models.CharField(max_length=100, blank=True, default='')
        age = models.IntegerField()
        gender = models.CharField(choices=GENDER_CHOICES, default='male', max_length=20)
    

    修改完了模型,我们需要更新一下数据表。

    通常来讲,我们会创建一个数据库 migration 来更新数据表,但是为了图省事儿,宝宝我索性删了整张 Movies 表直接重建!

    在数据库中删除 douban_movies 表后在终端中执行以下命令:

    $ python manage.py syncdb
    

    接着我们可能会需要多个 User 来测试 API ,如果之前你没有创建 Django Super User 的话,用以下命令创建:

    $ python manage.py createsuperuser
    

    然后进入 http://127.0.0.1/admin/ 界面,登录并找到 /user/ 表,然后在里面手动创建 user 并赋予权限。

    为新增的模型增加 endpoints

    既然现在我们已经有了 users 模型和 celebrity 模型,那么现在需要做的就是在 serializer.py 中让他们在 API 中展现出来,加入以下代码:

    class UserSerializer(serializers.ModelSerializer):
        movies = serializers.PrimaryKeyRelatedField(many=True, queryset=Movies.objects.all())
    
        class Meta:
            model = User
            fields = ('id', 'username', 'movies')
    
    class DirectorSerializer(serializers.ModelSerializer):
        movies = serializers.PrimaryKeyRelatedField(many=True, queryset=Movies.objects.all())
    
        class Meta:
            model = celebrity
            fields = ('id', 'name', 'age', 'gender', 'movies')
    

    因为我们之前在 models.py 中添加了 owner = models.ForeignKey('auth.User', related_name='movies') 其中 related_name 设置了可以通过 User.movies 来逆向访问到 movies 表。所以在 ModelSerializer 类中我们需要在 fields 中添加一个 movies 来实现逆向访问。同理 DirectorSerializer 类中也进行相应修改。

    接着,我们还需要在 views.py 中添加相应的视图。

    为 User 添加只读 API ,使用 ListAPIViewRetrieveAPIView

    为 Director 添加读写 API ,使用 ListCreateAPIViewRetrieveUpdateDestroyAPIView

    class UserList(generics.ListAPIView):
        queryset = User.objects.all()
        serializer_class = UserSerializer
    
    class UserDetail(generics.RetrieveAPIView):
        queryset = User.objects.all()
        serializer_class = UserSerializer
    
    class DirectorList(generics.ListCreateAPIView):
        queryset = celebrity.objects.all()
        serializer_class = DirectorSerializer
    
    class DirectorDetail(generics.RetrieveUpdateDestroyAPIView):
        queryset = celebrity.objects.all()
        serializer_class = DirectorSerializer
    

    最后,修改 urls.py 把视图关联起来,在 urlpatterns 中加入以下4个 patterns:

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

    把 Movies 和 Director 、 User 关联起来

    现在,如果我们新建一部 movie ,那它和 director 还有 user 是没有关联的,因为 director 和 user 信息是通过 request 接收到的,而不是通过序列器接收的,这意味着,数据库中收到 director 和 user 信息是没有(和 movies 存在)外键关系的。

    而要让他们发生关系 ,我们的做法是在视图中重写 .perform_create() 方法。

    .perform_create() 方法允许我们处理 request 或 requested URL 中的任何信息。

    MoviesListMoviesDetail 中添加以下代码:

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

    这样 create() 方法就能够在接收到 request.data 时将其传回给序列器里的 owner 和 director 了。

    更新序列器

    在视图中重写了 .perform_create() 方法后还需要更新下序列器才能实现他们之间的关联,在 serializer.py 中的 MoviesSerializer 类添加以下代码:

    owner = serializers.ReadOnlyField(source='owner.username')
    director = serializers.CharField(source='celebrity.name')
    

    接着在 class Meta 的 fields 中加入 owner 和 director :

    class Meta:
        model = Movies
        fields = ('id', 'title', 'director', 'year', 'country', 'type', 'rating', 'owner')
    

    source 关键字负责控制在 fields 中展现的数据的源,它可以指向这个序列器实例的任意一个属性。

    对 owner 属性,我们用的是 ReadOnlyField 在确保它始终是只读的,我们也可以用 CharField(read_only=True) 来等效替代,但是我嫌它太长了,其余的 Field 还有诸如 CharFieldBooleanField 等,你可以在 「这里」查到。

    添加权限

    我们希望授权的用户才能新建、更新和删除 movies,所以需要添加权限管理的功能。

    DRF 包含了一系列的 permission 类来实现权限管理,你可以在「这里」 查到。

    在这个栗子中,我们使用 IsAuthenticatedOrReadOnly 来确保授权的请求得到读写的权限,未授权的请求只有只读权限。

    首先,在 views.py 中 import 以下模块:

    from rest_framework import permissions
    

    接着,在 MoviesListMoviesDetail 中加入以下代码:

    permission_classes = (permissions.IsAuthenticatedOrReadOnly,)
    

    添加可浏览的授权 api

    如果你在浏览器中访问我们的 api Web 界面,你会发现我们没法创建新的 movies 了,因为在上一步我们设置了权限管理。

    所以我需要在浏览器中添加用户登录来实现带界面的权限管理。(之所以说带界面是因为可以在终端中直接使用 httpie 来访问 api )

    restapi/urls.py 中加入以下代码:

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

    这样通过在浏览器中访问 Web api 界面就能在右上角发现一个登录按钮,进行登录授权了。

    对象级权限

    之前提到要使 movies 可以被任何人访问,但是只能被创建者编辑,所以需要赋予其游客访问的权限以及创建者编辑权限。

    下面我们新建一个 permissions.py 来详细解决这个权限问题:

    #!/usr/bin/python
    # -*- coding: utf-8 -*-
    from rest_framework import permissions
    
    
    class IsOwnerOrReadOnly(permissions.BasePermission):
        """
        游客访问权限及创建者编辑权限
        """
    
        def has_object_permission(self, request, view, obj):
            # 游客权限
            if request.method in permissions.SAFE_METHODS:
                return True
    
            # 编辑权限
            return obj.owner == request.user
    

    修改 views.pyMoviesDetailpermission_class :

    from douban.permissions import IsOwnerOrReadOnly
    
    permission_classes = (permissions.IsAuthenticatedOrReadOnly,
                          IsOwnerOrReadOnly,)
    

    终于,我们完成了整个 api 授权的过程!

    https://github.com/thehackercat/django-rest-framework-tutorial/blob

    相关文章

      网友评论

          本文标题:Django_REST Framework -5.验证与授权

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