2
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

表示するモデルのデータをユーザごとに切り替える

Last updated at Posted at 2022-09-15
  • ログイン機能を実装しただけではデータベースから表示するデータが全ユーザーで共有されてしまう。
  • 特定のユーザーが登録した情報だけを表示する処理を実装する。

実装手順

  • Userモデルと表示したいモデルの連携
  • フォームにて、ログイン中のユーザ情報を自動保存(表示したいモデルに)
  • ユーザごとに表示するデータを切り替える
  • 独自制限の追加(detail, update, delete)

Userモデルと表示したいモデルの連携

models.py
from django.db import models
from model_utils.models import TimeStampedModel
from accounts.models import CustomUser

class Post(TimeStampedModel):
    # created = models.DateTimeField(auto_now_add=True) 新規作成
    # modified = models.DateTimeField(auto_now=True) 更新
    title = models.CharField(max_length=255)
    body = models.TextField()
    user_name = models.ForeignKey(CustomUser, on_delete=models.CASCADE) # 追加

    def __str__(self):
        return self.title

カラムの追加によるエラー対応

  • マイグレーション後に、モデルにカラムを追加する場合エラーが発生する。
  • エラーの内容:「デフォルト値なしではフィールドの登録ができない」
  • エラー対応:
    • そのままターミナル上でデフォルト値を入力する場合は「1」
    • 一度ターミナルを終了しコード自体に変更を加える場合は「2」
  • 1を入力した場合:追加したカラムに値を入力する
  • 2を入力した場合:コードに戻り、user_name = models.ForeignKey(CustomUser, on_delete=models.CASCADE, null=True) # nullを追加などを追記する
  • マイグレート
$python3 manage.py makemigrations

You are trying to add a non-nullable field 'user' to nippomodel without a default; we can't do that (the database needs something to populate existing rows).
Please select a fix:
 1) Provide a one-off default now (will be set on all existing rows with a null value for this column)
 2) Quit, and let me add a default in models.py
1を入力した場合
Select an option: 1
どの値を入力するかを聞かれるのでここでは、スーパーユーザのidを入力
>>> 1
$ python3 manage.py migrate

これでUserモデル(CustomUser)との連携は完了。次は、フォームを修正し、追加したuser_nameフィールドにログイン中のユーザを自動入力させたいと思う。

フォームにて、ログイン中のユーザ情報を自動保存(表示したいモデルに)

  • CreateViewのform_valid()をオーバーライドする
    ※今回は、form_classを用いず、modelのみを使用して作成したフォームをもとに実装していく
views.py
class Create(LoginRequiredMixin, CreateView):
    template_name = 'report/create.html'
    model = Post # form_classは記述が多くなるのでmodelを使用
    fields = ['title', 'body']

    def form_valid(self, form):
      '''
      フォームの保存(post)時にログインユーザをモデルに保存
      '''
      # ユーザーを投稿者として保存できるようにする
      object = form.save(commit=False) # 入力値をモデルに保存せず保留
      object.user_name = self.request.user # ログインユーザ取得
      object.save() # モデルに保存
      return super().form_valid(form)

    # idパラメータも渡す
    def get_success_url(self):
        return reverse('report:detail', kwargs={'pk': self.object.id})
  • form_valid()は、フォームからの入力値がpostされるタイミングで実行される
  • object = form.save(commit=False):ユーザからの入力値をモデルに保存せずに、一旦所持する。戻り値は、モデルオブジェクト。(CustomUserオブジェクト)
  • self.request.user:ログイン中のユーザ情報を取得する。
  • CustomUserモデルのuser_nameフィールドに対してログイン中のユーザ情報を格納する。
  • object.save():入力値とログイン中のユーザ情報を含んだデータをモデル(Post)に保存する。

ユーザごとに表示するデータを切り替える

  • ListViewのget_queryset()をオーバーライドする
views.py
class Index(LoginRequiredMixin, ListView):
    template_name = 'report/index.html'

    def get_queryset(self):
        current_user = self.request.user.username # ログイン中のユーザ名を取得(CustomUserモデルのusernameレコードの値を取得)
        user_data = CustomUser.objects.get(username=current_user) # QuerySet(条件が一致するレコードを全て取得)
        if user_data:
            queryset = Post.objects.filter(user_name=user_data).all() # QuerySet(一致するレコード全て取得)
            queryset = queryset.order_by("created")
        return queryset
  • self.request.user.username:ログイン中のユーザ名を取得
  • CustomUser.objects.get(username=current_user):CustomUserモデルのusernameカラムにおいて、取得したユーザ名と同じ値のレコードを1件取得する
  • Post.objects.filter(user_name=user_data).all():Postモデルのuser_nameカラムにおいて、上記で取得した1件のレコードと一致するレコードを全て取得する
  • queryset.order_by("created"):作成日時順にレコードを並び替える

※今回は、サインアップ、ログイン・ログアウト機能を実装している前提である。そのため、サインアップし、ログインしなければアプリを利用することができない。つまり、user_data, current_userがFalseになることはない。言い換えれば、queryset = Noneの場合の定義は不要。

独自制限の追加(detail, create, update, delete)

  • このままではdetail, update, delete時において、自分以外のログインユーザに内容が表示されてしまう。
  • 自分しかアクセスできないように、ログイン制限以外の独自制限を作成。
  • 注意点:継承する際は、一番左に持ってくること。ただし、LoginRequiredMixinより後ろ。

独自制限の作成(共有化)

  • 制限を対象のビュークラスで共有するため、UserPassesTestMixinを継承したクラスを作成し、独自のアクセス制限を設定
    • アクセス制限は、test_func()に定義する
  • 制限をかけたいビュークラスに継承する
views.py
from django.contrib.auth.mixins import UserPassesTestMixin
from django.shortcuts import redirect

class OwnerOnly(UserPassesTestMixin):
    # 参照条件
    def test_func(self):
        object = self.get_object()
        return object.user_name == self.request.user
views.py
'''~省略~'''
class Detail(LoginRequiredMixin, OwnerOnly, DetailView):
    # 省略
class Update(LoginRequiredMixin, OwnerOnly, UpdateView):
    # 省略
class Delete(LoginRequiredMixin, OwnerOnly, DeleteView):
    # 省略
2
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?