NARITO BLOG

Djangoでカレンダーを作るシリーズ

Python, Django, Bootstrap4, まとめ・シリーズ,

概要

Djangoで、カレンダアプリケーションを作成します。

例えば、月間カレンダー

月間カレンダー

週間カレンダー

週間カレンダー

各日のスケジュールも表示される、週間カレンダー

スケジュールつき週間

曜日、日付、スケジュールを1行に収めたり...

週間の1行版

縦にするなども可能です。

週間カレンダー、縦ver

スケジュール付きの月間カレンダー等もできます。

月間カレンダーwithスケジュール

そして、これら全てを詰め込み、スケジュール作成ページもつけたカレンダー(Googleカレンダーのデフォルトページをイメージしました)

Googleカレンダー風

一括作成・更新機能付き月間カレンダー等もあります。

一括作成・更新機能付き月間カレンダー

動作環境

Python: 3.6以上
Django: 2.0以上

初期設定

アプリケーション名をappとして進めていきます。

settings.py

設定ファイルに、appアプリケーションを定義します。

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'app',  # カレンダーアプリ
]

urls.py

from django.contrib import admin
from django.urls import path, include

urlpatterns = [
    path('admin/', admin.site.urls),
    path('', include('app.urls')),
]

models.py

スケジュールを管理するモデルとして、以下のようなものを予め定義しておきます。

import datetime
from django.db import models
from django.utils import timezone


class Schedule(models.Model):
    """スケジュール"""
    summary = models.CharField('概要', max_length=50)
    description = models.TextField('詳細な説明', blank=True)
    start_time = models.TimeField('開始時間', default=datetime.time(7, 0, 0))
    end_time = models.TimeField('終了時間', default=datetime.time(7, 0, 0))
    date = models.DateField('日付')
    created_at = models.DateTimeField('作成日', default=timezone.now)

    def __str__(self):
        return self.summary

mixins.py

mixins.pyというファイルをappディレクトリ内に作ります。ここに作るクラスは、カレンダー関連ビューを作るための部品です。views.pyに定義しても良いのですが、専用のmixins.pyのようなファイルを作っておくほうが管理しやすいですし、一般的です。

手始めに、次のようなMixinを定義しておきましょう。これは各Mixinの基底クラスになります。

import calendar
from collections import deque


class BaseCalendarMixin:
    """カレンダー関連Mixinの、基底クラス"""
    first_weekday = 0  # 0は月曜から、1は火曜から。6なら日曜日からになります。お望みなら、継承したビューで指定してください。
    week_names = ['月', '火', '水', '木', '金', '土', '日']  # これは、月曜日から書くことを想定します。['Mon', 'Tue'...

    def setup(self):
        """内部カレンダーの設定処理

        calendar.Calendarクラスの機能を利用するため、インスタンス化します。
        Calendarクラスのmonthdatescalendarメソッドを利用していますが、デフォルトが月曜日からで、
        火曜日から表示したい(first_weekday=1)、といったケースに対応するためのセットアップ処理です。

        """
        self._calendar = calendar.Calendar(self.first_weekday)

    def get_week_names(self):
        """first_weekday(最初に表示される曜日)にあわせて、week_namesをシフトする"""
        week_names = deque(self.week_names)
        week_names.rotate(-self.first_weekday)  # リスト内の要素を右に1つずつ移動...なんてときは、dequeを使うと中々面白いです
        return week_names

内部的に標準ライブラリ内のcalendarモジュールを利用してカレンダーを作成しています。

デフォルトでは月曜日からカレンダーが始まりますが、火曜日から表示したいというケースも当然あるので、first_weekdayというクラス属性を定義しています。0はデフォルトの月曜日スタート、1なら火曜日...のようになります。

また、['月', '火', '水'...]といった曜日のリストを柔軟に取得できるようにget_week_names()も定義しています。first_weekdayを1にしたならば'['火', '水'...]'といった曜日リストを返してくれますし、week_names属性を上書きすることで、['Mon', 'Tue']のように曜日リストの表記も変更できます。

残りのMixinは、後で作っていきます。

base.html

Bootstrap4のスターターテンプレートを使っていきます。

<!doctype html>
<html lang="ja">
<head>
    <!-- Required meta tags -->
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">

    <!-- Bootstrap CSS -->
    <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.2.1/css/bootstrap.min.css"
          integrity="sha384-GJzZqFGwb1QTTN6wy59ffF1BuGJpLSa9DkKMp0DgiMDm4iYMj70gZWKYbI706tWS" crossorigin="anonymous">

    <title>カレンダー</title>
</head>
<body>

<div class="container" mt-3>
    {% block content %}{% endblock %}
</div>


<!-- Optional JavaScript -->
<!-- jQuery first, then Popper.js, then Bootstrap JS -->
<script src="https://code.jquery.com/jquery-3.3.1.slim.min.js"
        integrity="sha384-q8i/X+965DzO0rT7abK41JStQIAqVgRVzpbzo5smXKp4YfRvH+8abtTE1Pi6jizo"
        crossorigin="anonymous"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.6/umd/popper.min.js"
        integrity="sha384-wHAiFfRlMFy6i5SRaxvfOCifBUQy1xHdJ/yoi7FRNXMRBu5WHdZYu1hA6ZOblgut"
        crossorigin="anonymous"></script>
<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.2.1/js/bootstrap.min.js"
        integrity="sha384-B0UglyR+jN6CkvvICOB2joaf5I4l3gm9GU6Hc1og6Ls7i6U/mkkaduKaBhlAXv9k"
        crossorigin="anonymous"></script>
{% block extrajs %}{% endblock %}
</body>
</html>

後で追加のJavaScriptを少し書くので、{% block extrajs %}{% endblock %}を定義しておきます。

月間カレンダー

まずは月間カレンダーです。次月、前月といった移動も勿論できます。

週間カレンダー

週間カレンダー
その週ごとに表示する機能も、よく見かけます。こちらも、前週・次週へ移動できます。

スケジュール付き週間カレンダー

スケジュール付き週間カレンダー
週間カレンダーは少しさみしいものでした。なので、拡張してスケジュールも表示できるものを作ります。

スケジュール付き月間カレンダー

スケジュールつき月間カレンダー
今度はスケジュール付きの、月間カレンダーを作ります。

スケジュール付き週間カレンダー+月間カレンダー+スケジュール登録フォーム

Djangoで、週間・月間カレンダー
Mixinを定義したおかげで、いくつかの機能を組み合わせるのも簡単です。 さらにgeneric.CreateViewも使い、スケジュール登録フォームもつけてみます。

一括作成・更新機能付き月間カレンダー

Djangoで、一括作成・更新機能付き月間カレンダー フォームセットを使い、月間カレンダーを表示しつつ各日のスケジュール登録・更新ができるようにしています。

完成品(Github)

Githubにソースコードがあります。

うごかしかた。

git clone https://github.com/naritotakizawa/django-simple-calendar
pip install django (pipenv install でも)
python manage.py migrate
python manage.py runserver