Wagtailで超簡単なイベント予約サイトを作る①

こんにちは。

Wagtailで超簡単なイベント予約サイトを以下流れでご紹介していきたいと思います。

流れ
  1. イベント一覧とイベント詳細ページを作成する
  2. 予約ページを作成する
  3. 予約登録処理
  4. 予約完了メール送信
  5. 予約確認とキャンセル処理を作成する

ちなみにログイン機能を使うので、以下を実施している事が前提で進めます。

Wagtailのサイトにユーザ認証機能を追加する ①設定編

2021.01.13

アプリケーション作成

こんな感じのイベント一覧ページと

こんな感じのイベント詳細ページを作っていきます。

まずは、eventというアプリケーションを作成します。

python manage.py startapp event

base.pyにINSTALLED_APPSにeventを追加します。

INSTALLED_APPS = [
    ...
    'event',
    ...

Homeのサブページとしてhome/models.pyに入れます。

class HomePage(Page):
    subpage_types = [
        ...
        "event.EventListingPage",
        ...
    ]

イベント一覧ページ作成

event/models.pyに以下を追加します。

class EventListingPage(Page):

    parent_page_types = ["home.HomePage"]
    template = "event/event_listing_page.html"

    custom_title = models.CharField(
        max_length=100,
        blank=False,
        null=False,
    )

    explanation = models.CharField(
        max_length=1000,
        blank=True,
        null=True,
    )

    banner_background_image = models.ForeignKey(
        'wagtailimages.Image',
        blank=False,
        null=True,
        related_name='+',
        help_text='The banner background image',
        on_delete=models.SET_NULL,
    )

    content_panels = Page.content_panels + [
        FieldPanel("custom_title"),
        FieldPanel("explanation"),
        ImageChooserPanel("banner_background_image"),
    ]

    def get_context(self, request, *args, **kwargs):
        context = super().get_context(request)

        posts = EventDetailPage.objects.live().public().order_by('-creation_date')
        context["events"] = posts

        return context

イベント詳細ページ作成

event/models.pyに以下を追加します。用途に合わせて色々追加してください。

class EventDetailPage(Page):

    # 画像
    image = models.ForeignKey(
        "wagtailimages.Image",
        verbose_name="画像",
        blank=False,
        null=True,
        related_name="+",
        on_delete=models.SET_NULL,
    )

    # 説明
    explanation = RichTextField(
        verbose_name="イベント説明",
        blank=True,
        null=True,
    )

    # 場所 郵便番号
    zip_code = models.CharField(
        verbose_name="郵便番号",
        max_length=8,
        blank=True,
        null=True,
    )

    # 場所 住所
    address1 = models.CharField(
        verbose_name="住所1",
        max_length=256,
        blank=True,
        null=True,
    )

    address2 = models.CharField(
        verbose_name="住所2",
        max_length=256,
        blank=True,
        null=True,
    )

    address3 = models.CharField(
        verbose_name="住所3",
        max_length=256,
        blank=True,
        null=True,
    )

    # 電話番号
    phone = models.CharField(
        verbose_name="Phone",
        max_length=256,
        blank=True,
        null=True,
    )

    # 開催日
    creation_date = models.DateTimeField(
        verbose_name="開催日",
        blank=True,
        null=True,
    )

    # 予約開始日
    date_from = models.DateTimeField(
        verbose_name="予約開始日",
        blank=True,
        null=True,
    )

    # 予約終了日
    date_to = models.DateTimeField(
        verbose_name="予約終了日",
        blank=True,
        null=True,
    )

    # 定員
    capacity = models.DecimalField(
        max_digits=10,
        decimal_places=0,
        verbose_name="定員",
        blank=True, null=True,
    )

    # 料金
    price = models.DecimalField(
        max_digits=10,
        decimal_places=0,
        verbose_name="料金",
        blank=True,
        null=True,
    )

    # 利用可能
    available = models.BooleanField(
        default=True,
        verbose_name="利用可能",
        blank=True,
        null=True,
    )

    content_panels = Page.content_panels + [
        FieldPanel("title"),
        ImageChooserPanel("image"),
        FieldPanel("explanation", classname="full"),
        FieldPanel("zip_code"),
        FieldPanel("address1"),
        FieldPanel("address2"),
        FieldPanel("address3"),
        FieldPanel("phone"),
        FieldPanel("creation_date"),
        FieldPanel("date_from"),
        FieldPanel("date_to"),
        FieldPanel("capacity"),
        FieldPanel("price"),
        FieldPanel("available"),
    ]

    def get_context(self, request, *args, **kwargs):
        context = super().get_context(request, *args, **kwargs)
        return context

イベント一覧のTemplateの用意

event_listing_page.htmlという名前でファイル作成して、以下を追加します。

{% extends "base.html" %}

{% load wagtailcore_tags wagtailimages_tags %}

{% block banner %}
    {% image self.banner_background_image fill-1600x400 as bg_img %}

    <div class="jumbotron jumbotron-fluid text-light d-flex sub-banner sub-banner-img" style="background: url({{ bg_img.url }});">
        <class class="container">
            <div class="row">
                <div class="col-8">
                    <h1 class="display-4 sub-banner-title" >{{ self.title }}</h1>
                </div>
            </div>
        </class>
    </div>

    <div class="jumbotron jumbotron-fluid sub-banner-section sub-banner-bg sub-banner-bg-plant" >
        <div class="container">
            <div class="row"></div>
                <div class="col-10 offset-1 col-sm-8 offset-sm-2">
                    <div class="d-block mx-auto">
                        <h2 class="display-6 sub-banner-ctitle text-center" >{{ self.custom_title }}</h2>                    
                        {% if self.explanation %}
                            <p class="lead text-center">{{ self.explanation }}</p>
                        {% endif %}
                    </div>
                </div>
            </div>       
        </div>
    </div>

{% endblock banner %}

{% block content %}

    <div class="container">
        <div class="row my-5">

            {% if events.count %}
                {% for event in events.all %}

                    <div class="card mb-3">
                        <div class="row no-gutters">
                            <div class="col-md-3">
                                {% image event.image fill-300x300 as event_img %}
                                <a href="{% pageurl event %}">
                                    <img class=" card-img" src="{{ event_img.url }}" alt="{{ event_img.alt }}">
                                </a>
                            </div>
                            <div class="col-md-9">
                                <div class="card-body">
                                    <h4 class="card-title">
                                        <a href="{% pageurl event %}">
                                            {{ event }}
                                        </a>
                                    </h4>
                                    <p class="card-text">
                                        {{ event.explanation | richtext | truncatechars_html:100 }}
                                    </p>
                                    <p class="card-text font-weight-bold">
                                        開催日時:{{ event.creation_date|date:"y/n/j H:i" }}
                                    </p>
                                    <p class="card-text font-weight-bold">
                                        料金:{{ event.price }}<small class="text-muted">円</small>
                                    </p>
                                </div>
                            </div>
                        </div>
                    </div>
                {% endfor %}
            {% else %}
                <p>イベントはありません。</p>
            {% endif %}

        </div>
    </div>
{% endblock %}

イベント詳細のTemplateの用意

event_detail_page.htmlという名前でファイル作成して、以下を追加します。「action=“{% url “event:bookconfirm” page.id %}”」の部分は次回に処理を入れていきます。

{% extends "base.html" %}

{% load wagtailcore_tags wagtailimages_tags %}
{% load custom_template_filters %}

{% block content %}
    <div class="container">
        <div class="row justify-content-md-center">
            <h3 class="m-4">{{ page.title }}</h3>
        </div>        

        <div class="row">
            <div class="col-lg-8 col-md-10 mx-auto">
                {% image page.image max-1000x500 as img %}
                <img class="img-fluid" src="{{ img.url }}" >    
            </div>
        </div>
        <div class="row">
            <div class="col-lg-8 col-md-10 mx-auto">
                {% if page.explanation %}
                    {{ page.explanation|richtext }}
                {% endif %}
            </div>
        </div>

        <div class="row">
            <div class="col-lg-8 col-md-10 mx-auto">
                <h4 class="m-4">予約詳細</h4>
            </div>
        </div>    

        <div class="row">
            <div class="col-lg-8 col-md-10 mx-auto">
                <!-- Table -->
                <div class="col-12">
                    <table class="table table-hover table-striped">
                        <tbody>
                            <tr>
                                <th scope="row">日時</th>
                                <td>
                                    {% if page.creation_date %}
                                        {{ page.creation_date|date:"y/n/j H:i" }}
                                    {% endif %}
                                </td>
                            </tr>
                            <tr>
                                <th scope="row">場所</th>
                                <td>
                                    {% if page.zip_code %}
                                        〒{{ page.zip_code }}
                                    {% endif %}
                                    {% if page.address1 %}
                                        {{ page.address1 }}
                                    {% endif %}
                                    {% if page.address2 %}
                                        {{ page.address2 }}
                                    {% endif %}
                                    {% if page.address3 %}
                                        {{ page.address3 }}
                                    {% endif %}
                                </td>
                            </tr>
                            <tr>
                                <th scope="row">定員</th>
                                <td>
                                    {% if page.capacity %}
                                        {{ page.capacity }}名
                                    {% endif %}   
                                </td>
                            </tr>
                            <tr>
                                <th scope="row">参加費</th>
                                <td>
                                    {% if page.price %}
                                        {{ page.price }}円
                                    {% endif %}  
                                </tr>
                            </tr>
                        </tbody>
                    </table> 
                </div>
            </div>
        </div>
 
        <div class="row"> 
            <div class="col-lg-8 col-md-10 mx-auto">
                <form 
                    action="{% url "event:bookconfirm" page.id %}"
                    method="post"
                    class="form-inline">

                    {% csrf_token %}
        
                    {% if user.is_authenticated %}
                        <div class="form-group">         
                            <input type="submit" value="予約に進む" class="btn btn-primary m-4" >
                        </div>
                    {% else %}
                        <div class="form-group">         
                            <input type="" value="予約に進む" class="disabled btn btn-primary m-4" >
                        </div>
                        ご予約頂くにはログインが必要です。
                        <a href="{% url 'account_login' %}">ログイン</a> <a href="{% url 'account_signup' %}">新規登録</a>
                    {% endif %}
                </form>
            </div>
        </div>
    </div>
{% endblock %}

Adminページからイベント登録

あとはAdminページから登録したら終わりです。

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です

日本語が含まれない投稿は無視されますのでご注意ください。(スパム対策)