画像を利用した本の投稿

はじめに

前回の続きで、画像を利用することができるようになったので、それを利用してユーザーが本の投稿をできるようにします。


ログイン状態の判定をする

ログインしている状態でないと本の投稿やデータの閲覧ができないようにします。
まずbook/views.pyに以下を追加します。

from django.contrib.auth.mixins import LoginRequiredMixin # 追加

class ListBookView(LoginRequiredMixin, ListView): # 追加
    template_name = 'book/book_list.html'
    model = Book
    paginate_by = ITEM_PER_PAGE

追加した継承のLoginRequiredMixinは一番左に記述しましょう。
ログインしていない状態ではaccounts/login/に遷移させることができます。

また、他のclassの継承にもLoginRequiredMixinを追記しておきましょう。


自分で投稿したデータのみ編集可能にする

まずはbook/models.pyに以下を追加します。

class Book(models.Model):
    title = models.CharField(max_length=100)
    text = models.TextField()
    thumbnail = models.ImageField(null=True, blank=True)
    category = models.CharField(
        max_length=100,
        choices = CATEGORY
        )
    user = models.ForeignKey('auth.User', on_delete=models.CASCADE) # 追加

    def __str__(self):
        return self.title

サーバーを立ち上げ、127.0.0.1:8000/admin/にアクセスします。それから適当なユーザーを選択します。

この赤い部分がユーザーのidとなっています。これを確認すれば、以下のコマンドを実行します。

python3 manage.py makemigrations
python3 manage.py migrate

再度選択しろと言われたら今度は1を選択します。


ではここからログインしているユーザーしか編集できないようにするための実装をしていきます。まずはbook/views.pyに以下の記述を追加します。

from django.core.exceptions import PermissionDenied # 追加

class UpdateBookView(LoginRequiredMixin, UpdateView):
    template_name = 'book/book_update.html'
    model = Book
    fields = {'title', 'text', 'category', 'thumbnail'}
 success_url = reverse_lazy('list-book')

    def get_object(self, queryset=None): # 追加
        obj = super().get_object(queryset) # 追加

        if obj.user != self.request.user: # 追加
            raise PermissionDenied # 追加

        return obj # 追加

追加した if obj.user != self.request.userについて見ていきます。obj.userはUpdateBookViewで呼び出された書籍の登録をしたユーザーを表しており、self.request.userは現在ログイン中のユーザーを表しています。もし一致していなければraise PermissionDeniedの処理を行うといったものです。
ここでraiseは例外を出すときに利用します。また、PermissionDeniedはDjangoが用意している例外です。

サーバーを立ち上げて登録したユーザーと別ユーザーで編集してみましょう。
以下のように例外がスローされました。

また、書籍登録したユーザーが編集を行ってデータを更新した際のページ遷移先を設定します。book/views.pyを以下のように編集します。

class UpdateBookView(LoginRequiredMixin, UpdateView):
    template_name = 'book/book_update.html'
    model = Book
    fields = {'title', 'text', 'category', 'thumbnail'}
 success_url = reverse_lazy('list-book') # 削除

    def get_object(self, queryset=None):
        obj = super().get_object(queryset)

        if obj.user != self.request.user:
            raise PermissionDenied

        return obj

    def get_success_url(self): # 追加
        return reverse('detail-book', kwargs={'pk': self.object.id}) # 追加

これで編集についてはできたので、削除についても以下のようにしましょう。

class DeleteBookView(LoginRequiredMixin, DeleteView):
    template_name = 'book/book_confirm_delete.html'
    model = Book
    success_url = reverse_lazy('list-book')

    def get_object(self, queryset=None):
        obj = super().get_object(queryset)

        if obj.user != self.request.user:
            raise PermissionDenied

        return obj

また、Bookモデルにuserを追加したので、formにユーザーの情報を追加するため、book/views.pyに以下を追加します。

class CreateBookView(LoginRequiredMixin, CreateView):
    template_name = 'book/book_create.html'
    model = Book
    fields = {'title', 'text', 'category', 'thumbnail'}
    success_url = reverse_lazy('list-book')

    def form_valid(self, form): # 追加
        form.instance.user = self.request.user # 追加

        return super().form_valid(form) # 追加

これで本を投稿したユーザーしか編集することができないようにすることができました。


おわりに

今回は本を投稿したユーザーのみが編集、削除できる仕組みを実装しました。また、登録した人と別のユーザーが編集しようとすると例外を発生させることも行いました。
次回はトップページを新しい順、評価の高い順に並び替えること、ページネーションをつけて見やすいトップページにしていきます。