[DRFチュートリアル]4.Authentication & Permissions


コードクリップを変更または削除できるようになりました.
そこで、以下の内容を追加します.
  • コードクリップにcreatorを追加
    snippet
  • を作成できるのは、
  • の認証を受けたユーザーのみです.
    このフラグメントを変更、削除できるのは
  • creatorのみです.
    認証されていないリクエストはread-only
  • です.
    所有者をモデルに追加
    # snippets/models.py
    class Snippet(models.Model):
        """생략"""
        owner = models.ForeignKey('auth.User', related_name='snippets', on_delete=models.CASCADE)
        highlighted = models.TextField()
        """생략"""
    また、モデルの保存時にハイライト表示フィールドを追加
    # 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):
            lexer = get_lexer_by_name(self.language)
            linenos = 'table' if self.linenos else False
            options = {'title': self.title} if self.title else {}
            formatter = HtmlFormatter(style=self.style, linenos=linenos,
                                      full=True, **options)
            self.highlighted = highlight(self.code, lexer, formatter)
            super(Snippet, self).save(*args, **kwargs)
        """생략"""
    通常、DBテーブルを変更するにはcreate移行が必要ですが、このチュートリアルの目的でDBを削除して再生成します.
    rm -f db.sqlite3
    rm -r snippets/migrations
    python manage.py makemigrations snippets
    python manage.py migrate
    
    python manage.py createsuperuser
    ユーザー・モデルの端点の追加
    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']
    クリップはユーザーモデルとは逆の関係にあるため、ModelSerializerクラスを使用しても自動的に含まれません.したがって、フィールドを明示的に追加する必要があります.
    観点もあります.pyを変更します.read-onlyビューのみを使用するにはgenericのListAPIビューとRetrieveAPIビューを使用します.
    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
    最後はsnippets/urlsです.pyを変更します.
    path('users/', views.UserList.as_view()),
    path('users/<int:pk>/', views.UserDetail.as_view()),
    Snapetsとユーザーの接続
    コードクリップがまだ作成されていない場合は、userには関連付けられません.userは自動的にシーケンス化されていませんが、requestの属性として入力されます.
    snippetビューにあります.perform create()メソッドを上書きすることで解決できます.これにより、インスタンスの格納方法を管理し、要求された情報を明確にすることができます.
    SnipetListビュークラスに追加します.
    def perform_create(self, serializer):
        serializer.save(owner=self.request.user)
    Serializerのcreate()メソッドは、検証されたデータとともに追加のownerフィールドを渡します.
    Serializerの変更
    snippetが作成したユーザーに接続されました.これをSnippetSerializerに反映します.Metaクラスにも追加されます.
    owner = serializers.ReadOnlyField(source='owner.username')
    sourceパラメータは、フィールドを塗りつぶすために使用する属性を決定します.点記号を使用します.Template言語に似ています.
    ReadOnlyFieldはCharFieldやBooleanFieldとは異なり、タイプのないクラスです.常にread-onlyであり、シーケンス化に使用されますが、モデルインスタンスを逆シーケンス化するときに変更するには使用されません.ここではCharField(read only=True)で代用することができます.
    Viewsに必要な権限の設定
    SnipetListとSnipetDetailの両方を追加します.
    認証されたプレイヤーは読むことと書くことができます.そうしないと読むしかありません.
    from rest_framework import permissions
    
    permission_classes = [permissions.IsAuthenticatedOrReadOnly]
    ブラウザへのログインの追加
    プロジェクトのルートurls.pyに追加します.
    from django.urls import path, include
    
    urlpatterns += [
        path('api-auth/', include('rest_framework.urls')),
    ]
    オブジェクトレベルの権限の設定
    コードクリップは誰にでも見られるようにしたいのですが、生成したユーザーのみを修正、削除します.
    custom permissionのためにsnippetアプリケーションpermissionに向かいます.pyを生成します.
    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
    視点.pyに追加します.
    from snippets.permissions import IsOwnerOrReadOnly
    
    permission_classes = [permissions.IsAuthenticatedOrReadOnly,
                          IsOwnerOrReadOnly]
    APIによる認証
    以前のようにお願いすると、エラーが発生します.
    http POST http://127.0.0.1:8000/snippets/ code="print(123)"
    
    {
        "detail": "Authentication credentials were not provided."
    }
    以下のようにユーザー名とpasswordを追加すればいいです.
    http -a admin:password123 POST http://127.0.0.1:8000/snippets/ code="print(789)"
    
    {
        "id": 1,
        "owner": "admin",
        "title": "foo",
        "code": "print(789)",
        "linenos": false,
        "language": "python",
        "style": "friendly"
    }