1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

【Django】ユーザが作成したデータのみをDBから抽出・表示させる方法

Posted at

こんにちは。
株式会社クラスアクト インフラストラクチャ事業部の大塚です。

Djangoの勉強の一環でtodo webアプリを今まで作成してきておりました。
個人的にはCRUD(Create・Read・Update・Delete)の実装の仕方について理解できたので非常に良かったと思っています。
今回はこのtodo webアプリの機能を拡張していきます。具体的にはログインしたユーザが登録したtodoのみしか表示されないようにしたいと思います。ログインしたユーザが別の人のtodoを見れないようにするということですね。

その機能を実装するために、以下の手順で現行の状態から更改していきたいと思います。

  1. Modelの修正と反映できているかの確認
  2. ログイン画面を作成する
  3. CreateViewで作成するtodoにUser属性を追加する
  4. 自分が作成したtodoしか表示させないようにする

今までのtodo Webアプリを作成した方法は以下をご覧ください。

Github

ディレクトリ構造

django-ページ17.drawio (1).png

環境構築

Modelの修正と反映できているかの確認

アプリ内のディレクトリにあるmodels.pyを以下のように修正。
userという項目を追加しています。

todoApp/models.py
from django.db import models
from django.contrib.auth.models import User

PRIORITY = (("High","high"), ("Normal","normal"), ("Low","low"))

class todoTable(models.Model
    user = models.ForeignKey(User, on_delete=models.CASCADE, default=1)
    title = models.CharField(max_length=100)
    memo = models.TextField()
    priority = models.CharField(
        max_length=50, 
        choices= PRIORITY
        )
    deadline = models.DateField()
    
    def __str__(self):
        return self.title

modelの変更をDBに反映します。

PS C:\Users\ohtsu\Documents\DjangoEnv\django-dev\userDataPJ> python manage.py makemigrations todoApp
Migrations for 'todoApp':
  todoApp\migrations\0004_todotable_user.py
    - Add field user to todotable

PS C:\Users\ohtsu\Documents\DjangoEnv\django-dev\userDataPJ> python manage.py migrate todoApp
Operations to perform:
  Apply all migrations: todoApp
Running migrations:
  Applying todoApp.0004_todotable_user... OK

runserverを実行して、管理画面にアクセスするとUserの項目が追加されていることが確認できました。問題なさそうですね。
2023122401.png
現在はadminユーザしか存在しないため、adminしか選択できないようになっています。
2023122402.png
test1というユーザを作成してみます。ユーザ名とパスワードを入力して保存を押下します。
2023122403.png
完全に知りませんでしたが、ユーザを作成するとこの画面のように、管理画面で一般ユーザ/スタッフユーザ(この画面にログイン出来るけど権限は弱い)/スーパユーザとかの設定も出来るみたいですね。へー知らなかった。。。
2023122404.png
改めてtodoの作成の画面に移動してみると、作成したtest1ユーザがtodoのユーザで選択できるようになりました。便利ですね。

20231224014.png

ログイン画面を作成する

過去に自分で実装したものを参考に作っていきます。

PJフォルダのurls.pyを以下のように修正。
http://localhost:8000/auth/にアクセスしてきた場合、Djangoが用意しているlogin機構を使うようにしました。

userDataPJ/urls.py
from django.contrib import admin
from django.urls import path, include
from .views import homeView

urlpatterns = [
    path("admin/", admin.site.urls),
    path("auth/", include("django.contrib.auth.urls"), name="login"),
    path("todo/", include("todoApp.urls")),
    path("", homeView.as_view(), name="homePage"),
]

次にtemplatesディレクトリ内にregistrationフォルダを作成。
そのフォルダに以下の内容のlogin.htmlファイルを作成しました。

templates/registration/login.html
<form method="post">
    {% csrf_token %}
    {{ form.as_p }}   
    <button type="submit">ログイン</button>

次に、todoの一覧や詳細確認等を表示するページにアクセスするためにはloginをしていないといけないようにする機構を組み立てていきたいと思います。
todoAppのurls.pyを以下のように修正しました。
※今回は全部"login_required"でログインを必須としましたが、views側で処理をさせることも出来そうです。

todoApp/urls.py
from django.urls import path
from .views import todoListView, todoDetailView, todoCreateView, todoDeleteView, todoUpdateView
from django.contrib.auth.decorators import login_required

app_name = "todoApp"

urlpatterns = [
    path("list/", login_required(todoListView.as_view()), name="todoListPage"),
    path("detail/<int:pk>", login_required(todoDetailView.as_view()), name="todoDetailPage"),
    path("create/", login_required(todoCreateView.as_view()), name="todoCreatePage"),
    path("delete/<int:pk>", login_required(todoDeleteView.as_view()), name="todoDeletePage"),
    path("update/<int:pk>", login_required(todoUpdateView.as_view()), name="todoUpdatePage"),
]

ログイン後に表示されるtodoリストを表示するHTMLを以下のように修正。
ログアウト用のボタンをやっつけで付けました。

templates/todoList.html
{% load static %}

<!DOCTYPE html>
    <html lang="ja">
    
        <head>
            <meta charset="utf-8">
            <meta name="viewport", content="width=device-width">
            <title>task一覧</title>
            <link href="{% static 'todoStyleSheet/todoListStyle.css' %}" rel="stylesheet">
        </head>

        <body>
            <div class="header-container">
                <h1 class="title">task一覧</h1>
            </div>
            <div class="btn-div"><button type="button" class="btn" onclick="window.location.href='{% url 'todoApp:todoCreatePage' %}'">todo作成</button></div>
            {% for todo in object_list %}
                <ul>
                <li class="todo-li"><div class="todo-title">{{ todo.title }}</div><br>
                    <div class="todo-flexbox">
                    <button type="button" class="btn" onclick="window.location.href='{% url 'todoApp:todoDetailPage' todo.pk %}'">todo確認</button>
                    <button type="button" class="btn" onclick="window.location.href='{% url 'todoApp:todoUpdatePage' todo.pk %}'">todo更新</button>
                    <button type="button" class="btnDelete" onclick="window.location.href='{% url 'todoApp:todoDeletePage' todo.pk %}'">todo削除</button>
                    </div>
                </li>
                </ul>
            {% endfor %}
            <div class="btn-div"><button type="button" class="btn" onclick="window.location.href='{% url 'homePage' %}'">ホーム画面に戻る</button></div>
            <div class="btn-div"><button type="button" class="btn" onclick="window.location.href='{% url 'logout' %}'">ログアウト</button></div>
        </body>

    </html>

さらにPJフォルダのsettings.pyの最後に以下の3行を追記しております。
それぞれ認証が求められるページにアクセスしたときにリダイレクトされるページ、ログインが成功した時にアクセスされるページ、ログアウト実行時にアクセスされるページとなります。それぞれurls.pyのnameで任意の名前とすることが出来ます。

userDataPJ/settings.py
LOGIN_URL = "login"
LOGIN_REDIRECT_URL = "todoApp:todoListPage"
LOGOUT_REDIRECT_URL = "homePage"

runserverを実行します。Check Taskボタンを押下して、認証画面に遷移されるようになったかを確認していきたいと思います。今まではこのボタンを押下したらすぐにtodoリストが表示され、セキュリティガバガバガバナンス状態でした。
2023122405.png
質素ではありますが、ログイン画面が表示されました。CSSも何も設定していないので仕方ないです。今回はadminユーザでログインを試みます。
2023122406.png
todoの一覧を確認することが出来ました。
2023122407.png

CreateViewで作成するtodoにUser属性を追加する

Modelの修正は完了していますが、ユーザがWebブラウザ経由でtodoを作成しても今のままではadminユーザが作成したものとして登録されてしまいます。
※models.pyのところで"default=1"としてしまっているため。
アプリのディレクトリにあるviews.pyを以下に修正します。この修正を加えることで、Webブラウザに表示されているtodoの作成ページ上ではユーザの入力を求められませんが、ログインユーザのIDをDjangoが自動で取得してDBに入力してくれるようになります。

todoApp/views.py
# todoApp/views.py

from django.shortcuts import render, redirect
from django.views.generic import ListView, DetailView, DeleteView, CreateView, UpdateView
from .models import todoTable
from django.urls import reverse_lazy

class todoListView(ListView):
    template_name = "todoList.html"
    model = todoTable

class todoDetailView(DetailView):
    template_name = "todoDetail.html"
    model = todoTable

class todoDeleteView(DeleteView):
    template_name = "todoDelete.html"
    model = todoTable
    success_url = reverse_lazy("todoApp:todoListPage")

class todoCreateView(CreateView):
    template_name = "todoCreate.html"
    model = todoTable
    fields = ("title", "memo", "priority", "deadline")

    def form_valid(self, form):
        form.instance.user = self.request.user
        return super().form_valid(form)

    def get_success_url(self):
        return reverse_lazy("todoApp:todoListPage")

class todoUpdateView(UpdateView):
    template_name = "todoUpdate.html"
    model = todoTable
    fields = ("title", "memo", "priority", "deadline")

    def get_success_url(self):
        return reverse_lazy("todoApp:todoDetailPage", kwargs={'pk': self.object.pk})

実際に試してみます。
test1ユーザでログインします。
2023122408.png
以下のような内容のタスクを登録します。
2023122409.png
リストに表示されています。表面上は問題なく作成されていそうです。
20231224010.png
管理画面でUserを見てみるとログインしていたtest1ユーザが表示されています。自動でIDを取れていそうですね。
20231224011.png

自分が作成したtodoしか表示させないようにする

改めてアプリのディレクトリにあるviews.pyを修正します。
return todoTable.objects.filter(user=self.request.user)を追加することで、todoTableテーブルからログインしているユーザが作成したtodoだけを表示するようにしています。

todoApp/views.py
# todoApp/views.py

from django.shortcuts import render
from django.views.generic import ListView, DetailView, DeleteView, CreateView, UpdateView
from .models import todoTable
from django.urls import reverse_lazy
from django.contrib.auth.decorators import login_required
from django.utils.decorators import method_decorator

class todoListView(ListView):
    template_name = "todoList.html"
    model = todoTable
    context_object_name = 'user_todos'  

    def get_queryset(self):  
        return todoTable.objects.filter(user=self.request.user)

class todoDetailView(DetailView):
    template_name = "todoDetail.html"
    model = todoTable

class todoDeleteView(DeleteView):
    template_name = "todoDelete.html"
    model = todoTable
    success_url = reverse_lazy("todoApp:todoListPage")

class todoCreateView(CreateView):
    template_name = "todoCreate.html"
    model = todoTable
    fields = ("title", "memo", "priority", "deadline")

    def form_valid(self, form):
        form.instance.user = self.request.user
        return super().form_valid(form)

    def get_success_url(self):
        return reverse_lazy("todoApp:todoListPage")

class todoUpdateView(UpdateView):
    template_name = "todoUpdate.html"
    model = todoTable
    fields = ("title", "memo", "priority", "deadline")

    def get_success_url(self):
        return reverse_lazy("todoApp:todoDetailPage", kwargs={'pk': self.object.pk})

さらにtodoリストを表示するためのHTMLファイルを修正しました。
Pythonのforループを回す際の元々のリストを"user_todos"に変更してます。

templates/todoList.html
{% load static %}

<!DOCTYPE html>
    <html lang="ja">
    
        <head>
            <meta charset="utf-8">
            <meta name="viewport", content="width=device-width">
            <title>task一覧</title>
            <link href="{% static 'todoStyleSheet/todoListStyle.css' %}" rel="stylesheet">
        </head>

        <body>
            <div class="header-container">
                <h1 class="title">task一覧</h1>
            </div>
            <div class="btn-div"><button type="button" class="btn" onclick="window.location.href='{% url 'todoApp:todoCreatePage' %}'">todo作成</button></div>
            {% for todo in user_todos %}
                <ul>
                <li class="todo-li"><div class="todo-title">{{ todo.title }}</div><br>
                    <div class="todo-flexbox">
                    <button type="button" class="btn" onclick="window.location.href='{% url 'todoApp:todoDetailPage' todo.pk %}'">todo確認</button>
                    <button type="button" class="btn" onclick="window.location.href='{% url 'todoApp:todoUpdatePage' todo.pk %}'">todo更新</button>
                    <button type="button" class="btnDelete" onclick="window.location.href='{% url 'todoApp:todoDeletePage' todo.pk %}'">todo削除</button>
                    </div>
                </li>
                </ul>
            {% endfor %}
            <div class="btn-div"><button type="button" class="btn" onclick="window.location.href='{% url 'homePage' %}'">ホーム画面に戻る</button></div>
            <div class="btn-div"><button type="button" class="btn" onclick="window.location.href='{% url 'logout' %}'">ログアウト</button></div>
        </body>

    </html>

動作を確認します。
test1ユーザでログインします。
2023122408.png
test1ユーザで何件かtodoを追加して以下のようにしました。
20231224012.png
test1ユーザからログアウトして、adminユーザでログインをしてみます。
2023122406.png
todoの一覧を確認してみると、test1で追加したtodoが表示されていないことがわかります。それっぽいアプリになってきましたね(UIは除く)
20231224013.png

1
1
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
1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?