Djangoアプリで投稿作成時にログインユーザー名を自動的に投稿データに格納し、ログイン中のユーザーがその投稿を作成したユーザーと一致する場合のみ投稿を編集できるようにしてみましょう。これもDjangoの機能を使って簡単に実装できます。
目次
モデルの変更とデータベースへの反映
現在作成しているbbsアプリケーションには任意の文字列を入力するためのcontentフィールドとuser_nameフィールドしか存在しません。
投稿者名として、Djangoの管理サイトで利用しているAuthorモデルのフィールドをそのままbbsアプリケーションのArticleモデルに関連付けて使用するよう、モデルを変更し、データベースに反映しましょう。
まずはモデルの変更です。models.py を次のように修正します。
from django.db import models from django.urls import reverse # reverse関数をインポート class Article(models.Model): content = models.CharField(max_length=140) author = models.ForeignKey( 'auth.User', on_delete=models.CASCADE, ) def __str__(self): return self.content # その投稿の詳細へのリンク def get_absolute_url(self): return reverse('bbs:detail', kwargs={'pk': self.pk})
authorカラムを定義し、その内容として外部キーで’auth.User’を関連付けています。
この変更をデータベースに反映しましょう。
まずはマイグレーションファイルを作成します。
Windows PowerShell で django-tutorial ディレクトリに入り、次のコマンドを実行します。
py manage.py makemigrations bbs
マイグレーションファイルが作成されました。
次に、この変更差分をデータベースに反映しましょう。
py manage.py migrate
これでモデルの変更とデータベースへの反映は完了です。
ビューの変更
投稿作成時にログインユーザー名を投稿に格納するようにしましょう。
views.py を次のように修正します。
from django.urls import reverse_lazy from django.views import generic from .models import Article from django.contrib.auth.mixins import LoginRequiredMixin class IndexView(generic.ListView): model = Article class DetailView(generic.DetailView): model = Article class CreateView(LoginRequiredMixin, generic.edit.CreateView): model = Article fields = ['content'] # 項目をcontentに変更 def form_valid(self, form): form.instance.author = self.request.user return super(CreateView, self).form_valid(form) class UpdateView(LoginRequiredMixin, generic.edit.UpdateView): model = Article fields = '__all__' class DeleteView(LoginRequiredMixin, generic.edit.DeleteView): model = Article success_url = reverse_lazy('bbs:index')
表示するフィールドを content だけにして、author(form.instance.author)に現在ログインしているユーザー情報(self.request.user)を代入します。
これで投稿作成時に、ログインユーザーの情報がその投稿の情報としてデータベースに格納されるようになりました。
投稿作成についてはこれでOKです。
テンプレートの変更
変更に合わせてテンプレートも修正しておきましょう。
article_detail_html を次のように修正します。
{% extends "./base.html" %} {% block content %} <h1>{{ article.id }}の個別投稿ページ</h1> <p>{{ article.content }},{{ article.author }}</p> <div> <button onclick='location.href="{% url "bbs:index" %}"'>一覧</button> <!-- ログインユーザーのidがその投稿のものと一致する場合 --> {% if request.user.id == object.author_id %} <button onclick='location.href="{% url "bbs:update" article.pk %}"'>編集</button> <button onclick='location.href="{% url "bbs:delete" article.pk %}"'>削除</button> {% endif %} </div> {% endblock %}
表示する項目名と、「編集」「削除」ボタンを表示する条件を変更しました。
編集フォームのアクセス制御
次に、ログイン中のユーザーがその投稿を作成したユーザーと一致する場合のみ投稿を編集できるように変更しましょう。
views.py を次のように修正します。
from django.urls import reverse_lazy from django.views import generic from .models import Article from django.contrib.auth.mixins import LoginRequiredMixin from django.core.exceptions import PermissionDenied # インポート class IndexView(generic.ListView): model = Article class DetailView(generic.DetailView): model = Article class CreateView(LoginRequiredMixin, generic.edit.CreateView): model = Article fields = ['content'] def form_valid(self, form): form.instance.author = self.request.user return super(CreateView, self).form_valid(form) class UpdateView(LoginRequiredMixin, generic.edit.UpdateView): model = Article fields = ['content'] # 項目をcontentのみに変更 def dispatch(self, request, *args, **kwargs): obj = self.get_object() if obj.author != self.request.user: raise PermissionDenied('編集権限がありません。') return super(UpdateView, self).dispatch(request, *args, **kwargs) class DeleteView(LoginRequiredMixin, generic.edit.DeleteView): model = Article success_url = reverse_lazy('bbs:index')
投稿編集用の UpdateView でも新規投稿フォームと同様、入力項目を content のみに変更。
また、PermissionDenied をインポートして、despatchメソッドをオーバーライド、投稿者名(author)がログインユーザー(request.user)と同じでなかったらアクセスを拒否して、「編集権限がありません」というエラーメッセージを返すようにしています。
動作確認
それでは動作確認です。
Djangoコマンドを使ってサーバーを起動し、ブラウザで確認してみましょう。
http://127.0.0.1:8000/accounts/login にアクセスしてログインしてみましょう。
投稿一覧ページが表示されました。
「新規投稿」をクリックして投稿を作成してみましょう。
新規投稿ページが表示されました。
content だけが入力できるようになっています。
実際に投稿を作成してみましょう。
新しい投稿を登録しました。
ユーザー名は入力していませんが、現在ログインしているユーザー名が登録されているのがわかります。
管理サイトでも確認してみましょう。
Authorフィールドに先ほど投稿を作成した時にログインしていたユーザー名が格納されているのがわかります。
次は編集フォームを確認してみましょう。
先ほどの個別投稿ページにアクセスし、「編集」ボタンをクリックします。
編集ページでも入力できる項目が Content だけになっています。
では次に、別のユーザーで作成した投稿の詳細ページへアクセスしてみましょう。
こちらは admin さんが作成した投稿なので「編集」「削除」ボタンが表示されないようになっています。
編集用のURLを直接叩いて無理矢理編集画面にアクセスしてみましょう。
http://127.0.0.1:8000/bbs/27/update のようにURLの末尾に update を追加します。
403エラー、つまり閲覧禁止を示すエラーメッセージが表示されました。
ログインしているユーザーと、投稿を作成したユーザーが一致していないのでURLを直接叩いて編集しようとしてもできないようアクセス制御が実現できています。
Windows PowerShell のログを確認してみると、「編集権限がありません。」と先ほど設定したエラーメッセージが返されているのがわかります。
きちんとしたアクセス制御が実装できました!
OperationalError at /bbs/
no such column: bbs_article.author_id
このようなエラーが出てしまいます。コードは同じように記載しております。
自分個人のwebサイトを作っている際にカスタムユーザーを作成して、記事とつなげるためにforeignkey を設定するとこういったエラーが出ます。author_id がないですよというエラーが出ているのですが、どのように改善したらよいのか分かりません。
正確にはbbs_articleテーブルにauthor_idというカラムがないよ、というエラーですね。
ご自分でカスタムユーザーを作成されているとのことですので、まずは下記を確認してみてください。
カスタムユーザーまわりについては下記の記事もご参照ください。