【和訳】Django Rest Framework クイックスタート
【和訳】Django Rest Framework チュートリアル1:シリアル化
【和訳】Django Rest Framework チュートリアル2:リクエストとレスポンス
【和訳】Django Rest Framework チュートリアル3:クラスベースのビュー
【和訳】Django Rest Framework チュートリアル4:認証とアクセス許可
【和訳】Django Rest Framework チュートリアル5:リレーションとハイパーリンクされたAPI
【和訳】Django Rest Framework チュートリアル6:ビューセットとルーター
チュートリアル4:認証とアクセス許可
現在、APIには、コードスニペットを編集または削除できるユーザーに関して制限はありません。
以下の制限を設けるために、より高度な動作が必要です。
- コードスニペットは常に作成者に関連付けられている。
- 認証されたユーザーのみがスニペットを作成できる。
- スニペットの作成者のみが更新または削除できる。
- 未認証のリクエストに対しては、読み取り専用(Read-only)のアクセス権のみ許可する。
モデルに情報を追加する
Snippet
モデルクラスにいくつかの変更を加えます。
まず、フィールドを2つ追加しましょう。
追加したフィールドのうち、1つはコードスニペットを作成したユーザーを表すために使用されます。
もう1つのフィールドは、強調表示されたコードのHTML表示を格納するために使用されます。
次の2つのフィールドをmodels.py
のSnippet
モデルに追加します。
owner = models.ForeignKey('auth.User', related_name='snippets', on_delete=models.CASCADE)
highlighted = models.TextField()
また、モデルを保存するときに、pygments
コード強調表示ライブラリを使用して、highlightedフィールドにデータを入力する必要があります。
そのため、追加でモジュールをインポートする必要があります。
from pygments.lexers import get_lexer_by_name
from pygments.formatters.html import HtmlFormatter
from pygments import highlight
これで、モデルクラスに.save()
メソッドを追加できます。
def save(self, *args, **kwargs):
"""
コードスニペットのハイライトされたHTML表示を作成するために、
`pygments`ライブラリを使用する
"""
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)
この作業がすべて完了したら、データベーステーブルを更新する必要があります。
通常、これを行うためにデータベースmigrateを作成しますが、このチュートリアルの目的のために、データベースを削除してやり直してみましょう。
rm -f 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'
はUserモデル上では逆参照の関係にあるので、ModelSerializer
クラスの使用時には、デフォルトではインクルードされません。そのため、私たちはそれを明示的にフィールドを追加する必要があります。
また、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に追加する必要があります。
以下をsnippets/urls.py
のパターンに追加します。
path('users/', views.UserList.as_view()),
path('users/<int:pk>/', 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')
注:内側のMeta
クラスのフィールドのリストにも'owner'
を必ず追加してください。
このフィールドは非常に興味深いことをしています。source
引数は、フィールドを埋めるために、参照先テーブルのどの属性を使用するかをコントロールし、また、シリアル化されたインスタンスのどの属性でも選択できます。
Djangoのテンプレート言語で使用されるのと同様に、上記のようにドット表記を使用することで、source
引数は、指定された属性を辿ることができます('owner.username')。
私たちが追加したフィールドは、型のないReadOnlyField
であり、CharField
、BooleanField
のような他の型のフィールドとは対照的です。ReadOnlyField
は常に読み取り専用であり、シリアライズされた表現のために使用されますが、インスタンスの逆シリアル化された時に、モデルインスタンスを更新するために使用されることはありません。
なお、ここでは、CharField(read_only=True)
を使用することもできます。
ビューに必要な権限を追加する
コードスニペットとユーザーが関連付けられたので、認証されたユーザーのみがコードスニペットを作成、更新、および削除できるようにします。
RESTフレームワークには、特定のビューにアクセスできるユーザーを制限するために使用できるいくつかのアクセス許可クラスが含まれています。
今回、私たちが探し求めているのIsAuthenticatedOrReadOnly
です。
このクラスにより、認証されたリクエストは読み取り/書き込みアクセスを取得し、認証されていないリクエストは読み取り専用アクセスを取得します。
まず、ビューモジュールに次のインポートを追加します
from rest_framework import permissions
その後、SnippetList
とSnippetDetail
ビュークラスの両方に次のプロパティを追加します
permission_classes = [permissions.IsAuthenticatedOrReadOnly]
ブラウザから閲覧可能なAPIへのログインの追加
現時点でブラウザを開いてブラウザから閲覧可能なAPIに移動すると、新しいコードスニペットを作成できなくなっていることがわかります。
新しいコードスニペットを作成するためには、ユーザーとしてログインできるようにしなければなりません。
プロジェクトレベルのurls.py
ファイルでURLconfを編集することにより、ブラウザから閲覧可能なAPIで使用するログインビューを追加できます。
ファイルの先頭で次のライブラリをインポートします。
from django.urls import path, include
また、ファイルの最後に、閲覧可能なAPIのログインビューとログアウトビューを含めるパターンを追加します。
urlpatterns += [
path('api-auth/', include('rest_framework.urls')),
]
'api-auth/'
パターンの一部は、実際には使用したいURLにすることができます。
ここで、ブラウザをもう一度開いてページを更新すると、ページの右上に「ログイン」リンクが表示されます。以前に作成したユーザーの1人としてログインすると、コードスニペットを再度作成できるようになります。
いくつかのコードスニペットを作成したら、「/ users /」エンドポイントに移動します。
表現には、各ユーザーの「スニペット」フィールドに、各ユーザーに関連付けられているスニペットIDのリストが含まれていることに注意してください。
オブジェクトレベルの権限
実際には、すべてのコードスニペットを誰にでも表示できるようにしたいだけでなく、コードスニペットを作成したユーザーだけがコードスニペットを更新または削除できるようにします。
そのためには、カスタム権限を作成する必要があります。
スニペットアプリで、新しいファイルを作成し、 permissions.py
from rest_framework import permissions
class IsOwnerOrReadOnly(permissions.BasePermission):
"""
オブジェクトの編集を、オーナーのみに許可するカスタムパーミッション
"""
def has_object_permission(self, request, view, obj):
# 読み取り権限はあらゆるリクエストに許可される。
# そのため、常にGET、HEAD、OPTIONSリクエストを許可する
if request.method in permissions.SAFE_METHODS:
return True
# 書き込み権限はスニペットのオーナーのみに許可される。
return obj.owner == request.user
SnippetDetail
ビュークラスのpermission_classes
プロパティを編集して、このカスタム権限をスニペットインスタンスのエンドポイントに追加できます。
permission_classes = [permissions.IsAuthenticatedOrReadOnly,
IsOwnerOrReadOnly]
IsOwnerOrReadOnly
クラスもインポートしてください。
from snippets.permissions import IsOwnerOrReadOnly
ここで、ブラウザを再度開くと、コードスニペットを作成したのと同じユーザーとしてログインしている場合にのみ、「DELETE」アクションと「PUT」アクションがスニペットインスタンスエンドポイントに表示されることがわかります。
APIを使用した認証
APIに対して一連の権限を設けたので、スニペットを編集する場合は、APIへのリクエストを認証する必要があります。
私たちはこれまでのところ、認証クラスを何も設定していないので、デフォルトのSessionAuthentication
とBasicAuthentication
が現在適用されている。
Webブラウザーを介してAPIと対話すると、ログインでき、ブラウザーセッションがリクエストに必要な認証を提供します。
プログラムでAPIを操作している場合は、リクエストごとに認証資格を明示的に提供する必要があります。
認証せずにスニペットを作成しようとすると、エラーが発生します。
http POST http://127.0.0.1:8000/snippets/ code="print(123)"
{
"detail": "Authentication credentials were not provided."
}
以前に作成したユーザーの1人のユーザー名とパスワードを含めることで、リクエストを成功させることができます。
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"
}
概要
これで、Web APIに対するかなりきめ細かい権限のセットと、システムのユーザーおよびユーザーが作成したコードスニペットのエンドポイントが得られました。
チュートリアルのパート5では、私たちは強調表示スニペットのためのHTMLエンドポイントを作成することにより、すべてをひとまとめにする方法、および、システム内の関係にハイパーリンクを使用することで、私たちのAPIの凝集度を向上させる方法を見ていきます。
0 件のコメント:
コメントを投稿