予約サイト、マイページ作成①

Python Django

概要

Djangoで、シンプルな予約サイトの作成シリーズの1つです。スタッフが予約を確認したり休暇を入れたりする、マイページを作成していきます。

ログイン・ログアウト機能

マイページを利用するために、ログインとログアウト機能を実装する必要があります。今回はDjango管理サイトへはログインできない、つまりスタッフ権限自体は持たせないようにして、ログインとログアウト機能をピンポイントで実装します。

まず、settings.pyに追記しておきましょう。

# ログインページのURL
LOGIN_URL = 'booking:login'

# ログイン後にリダイレクトされるURL
LOGIN_REDIRECT_URL = 'booking:store_list'

# ログアウト後にリダイレクトされるURL
LOGOUT_REDIRECT_URL = 'booking:store_list'

これらはapp_name:view_nameの形式で書けます。ログイン・ログアウト後はトップページにリダイレクトされるようにしておきます。ログインページはこれから作ります。

では、booking/urls.pyを編集します。

from django.contrib.auth.views import LoginView, LogoutView  # 追加
from django.urls import path
from . import views

app_name = 'booking'

urlpatterns = [
    path('', views.StoreList.as_view(), name='store_list'),
    path('store/<int:pk>/staffs/', views.StaffList.as_view(), name='staff_list'),
    path('staff/<int:pk>/calendar/', views.StaffCalendar.as_view(), name='calendar'),
    path('staff/<int:pk>/calendar/<int:year>/<int:month>/<int:day>/', views.StaffCalendar.as_view(), name='calendar'),
    path('staff/<int:pk>/booking/<int:year>/<int:month>/<int:day>/<int:hour>/', views.Booking.as_view(), name='booking'),

    # 追加
    path('login/', LoginView.as_view(template_name='admin/login.html'), name='login'),
    path('logout/', LogoutView.as_view(), name='logout'),
]

ログインとログアウト機能を実装するための専用のビューがDjangoに用意されているので、それを使いました。ログインページのテンプレートファイルは自分で作るのが面倒だったので、Django管理サイトで使われているあのカッコいいの(admin/login.html)を借りています。

珍しくas_viewの引数に指定するやり方を使っていますが、何か処理を挟みたい場合は、普通のビューみたいに作ってください。

そして、booking/base.htmlを開いて、ログインやログアウトのリンクを設定しておきましょう。

<nav>
    <a href="{% url 'booking:store_list' %}">Home</a>
    {% if user.is_authenticated %}
        <a href="">MyPage</a>
        <a href="{% url 'booking:logout' %}">Logout</a>
    {% else %}
        <a href="{% url 'booking:login' %}">Login</a>
    {% endif %}
</nav>

ログイン、ログアウトの様子

マイページトップ

ログイン・ログアウトも実装できたので、マイページを作成します。

まず、booking/urls.pyです。

urlpatterns = [
    path('', views.StoreList.as_view(), name='store_list'),
    path('store/<int:pk>/staffs/', views.StaffList.as_view(), name='staff_list'),
    path('staff/<int:pk>/calendar/', views.StaffCalendar.as_view(), name='calendar'),
    path('staff/<int:pk>/calendar/<int:year>/<int:month>/<int:day>/', views.StaffCalendar.as_view(), name='calendar'),
    path('staff/<int:pk>/booking/<int:year>/<int:month>/<int:day>/<int:hour>/', views.Booking.as_view(), name='booking'),

    path('login/', LoginView.as_view(template_name='admin/login.html'), name='login'),
    path('logout/', LogoutView.as_view(), name='logout'),
    path('mypage/', views.MyPage.as_view(), name='my_page'),  # 追加
]

次に、ビューを作ります。

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


# 追加
class MyPage(LoginRequiredMixin, generic.TemplateView):
    template_name = 'booking/my_page.html'

    def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs)
        context['staff_list'] = Staff.objects.filter(user=self.request.user).order_by('name')
        context['schedule_list'] = Schedule.objects.filter(staff__user=self.request.user, start__gte=timezone.now()).order_by('name')
        return context

LoginRequiredMixinを使ってログイン必須にしておきます。また、自分に紐づくスタッフの一覧と、今より後にある予約の一覧もテンプレートファイルに渡すようにしています。

booking/my_page.htmlとして、次のようにします。

{% extends 'booking/base.html' %}

{% block content %}
    <h1>{{ user.username }}のMyPage</h1>
    <h2>所属店舗</h2>
    <ul>
        {% for staff in staff_list %}
            <li><a href="">{{ staff }}</a></li>
        {% empty %}
            <li>まだ店舗がありません。</li>
        {% endfor %}
    </ul>

    <h2>直近の予約</h2>
    <ul>
        {% for schedule in schedule_list %}
            <li><a href="">{{ schedule }}</a></li>
        {% empty %}
            <li>予約はありません。</li>
        {% endfor %}
    </ul>
{% endblock %}

booking/base.htmlを開いて、マイページへのリンクを追加しておきましょう。

<a href="{% url 'booking:my_page' %}">MyPage</a>

マイページの様子

管理者用のマイページ

現状ではログインユーザーに紐づく情報が表示されます。なので、他人のマイページは見れません。しかし、このマイページは便利なので、スーパーユーザーといった一部のユーザーは他人のマイページを見れるようにしたいと思います。

まず、URL定義を追加しましょう。

urlpatterns = [
    path('', views.StoreList.as_view(), name='store_list'),
    path('store/<int:pk>/staffs/', views.StaffList.as_view(), name='staff_list'),
    path('staff/<int:pk>/calendar/', views.StaffCalendar.as_view(), name='calendar'),
    path('staff/<int:pk>/calendar/<int:year>/<int:month>/<int:day>/', views.StaffCalendar.as_view(), name='calendar'),
    path('staff/<int:pk>/booking/<int:year>/<int:month>/<int:day>/<int:hour>/', views.Booking.as_view(), name='booking'),

    path('login/', LoginView.as_view(template_name='admin/login.html'), name='login'),
    path('logout/', LogoutView.as_view(), name='logout'),
    path('mypage/', views.MyPage.as_view(), name='my_page'),
    path('mypage/<int:pk>/', views.MyPageWithPk.as_view(), name='my_page_with_pk'),  # 追加
]

見たいユーザーのpkをURLに含むようにしておきます。そして、対応するビューも作成しましょう。

# 追加
from django.contrib.auth import get_user_model
from django.contrib.auth.mixins import LoginRequiredMixin, UserPassesTestMixin

# 追加
User = get_user_model()


# 追加
class OnlyUserMixin(UserPassesTestMixin):
    raise_exception = True

    def test_func(self):
        return self.kwargs['pk'] == self.request.user.pk or self.request.user.is_superuser


# 追加
class MyPageWithPk(OnlyUserMixin, generic.TemplateView):
    template_name = 'booking/my_page.html'

    def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs)
        context['user'] = get_object_or_404(User, pk=self.kwargs['pk'])
        context['staff_list'] = Staff.objects.filter(user__pk=self.kwargs['pk']).order_by('name')
        context['schedule_list'] = Schedule.objects.filter(staff__user__pk=self.kwargs['pk'], start__gte=timezone.now()).order_by('name')
        return context

ビュー以外にいくつかのコードがあります。User = get_user_model()は、Userモデルを柔軟に取得するための書き方ですね。OnlyUserMixinですが、UserPassesTestMixinを継承しています。これはビューのアクセス制御をしたい場合に使うものです。test_funcメソッド内にビューにアクセスできる条件を書きます。Trueの場合はアクセス可能です。

今回の処理は、URL内のpkが、ログインユーザーのpkと同じか、又はログインユーザーがスーパーユーザーの場合にアクセスできるようになっています。これで、スーパーユーザー以外は、他人のマイページを見れなくできます。後は、このMixinをMyPageWithPkビューで継承するだけです。

MyPageWithPkビューは、前のビューと違ってrequest.userが使えないことに注意しておきましょう。

Relation Posts

Comment

記事にコメントする

まだコメントはありません。