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

Twitterでシェア FaceBookでシェア はてなブックマークでシェア

Python - Django
2018年11月22日21:14に更新(約21日前)
2018年10月7日20:14に作成(約67日前)

旧ブログ移行記事です。

概要

Djangoでカレンダーを作るシリーズの1つです。 スケジュール付きの、週間カレンダーを作成していきます。

以下のようなものが作れます。 スケジュール付き週間カレンダー

スケジュールが表示されると、少し見た目がよくなりますね。

scalendar/views.py

WeekCalendarMixinをベースに、新しいMixinを定義します。

import calendar
from collections import deque
import datetime
from .models import Schedule
...
...
class WeekWithScheduleMixin(WeekCalendarMixin):
    """スケジュール付きの、週間カレンダーを提供するMixin"""
    model = Schedule
    date_field = 'date'
    order_field = 'start_time'

    def get_week_schedules(self, days):
        """それぞれの日のスケジュールを返す"""
        for day in days:
            lookup = {self.date_field: day}
            queryset = self.model.objects.filter(**lookup)
            if self.order_field:
                queryset = queryset.order_by(self.order_field)
            yield queryset

    def get_week_calendar(self):
        calendar_data = super().get_week_calendar()
        schedules = self.get_week_schedules(calendar_data['days'])
        calendar_data['schedule_list'] = schedules
        return calendar_data

WeekCalendarMixinの説明

get_week_calendarメソッドを上書きし、スケジュールを返すようにしています。 例えばその週が[1, 2, 3, 4, 5, 6, 7]となっていれば、schedule_listは[1日の全てのスケジュール, 2日の全てのスケジュール,....]となります。 scalendarで定義したScheduleモデルを使っていますが、それを変更することもできます。その際は、スケジュールの日付を表すフィールドをdate_field属性で指定してください。 また、並び替えをしたい場合は、order_fieldに指定します。

sampleapp/urls.py

週間カレンダーと殆ど同じですね。

    path('week_with_schedule/', views.WeekWithScheduleCalendar.as_view(), name='week_with_schedule'),
    path(
        'week_with_schedule/<int:year>/<int:month>/<int:day>/', views.WeekWithScheduleCalendar.as_view(),
        name='week_with_schedule'
    ),

sampleapp/views.py

WeekWithScheduleMixinを継承すれば、後は週間カレンダーと同様です。

class WeekWithScheduleCalendar(WeekWithScheduleMixin, generic.TemplateView):
    """スケジュール付きの週間カレンダーを表示するビュー"""
    template_name = 'sampleapp/week_with_schedule.html'

    def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs)
        context['week'] = self.get_week_calendar()
        return context

week_with_schedule.html

スケジュール付き週間カレンダーのテンプレートです。

{% extends 'sampleapp/base.html' %}
{% block content %}

<a href="{% url 'sampleapp:week_with_schedule' week.previous.year week.previous.month  week.previous.day %}">前週</a>
{{ week.first | date:"Y年m月d日" }}〜{{ week.last | date:"Y年m月d日" }}
<a href="{% url 'sampleapp:week_with_schedule' week.next.year week.next.month  week.next.day %}">次週</a>
<table class="table table-bordered">
  <thead>
    <tr>
      {% for w in week.week_names %}
        <th>{{ w }}</th>
      {% endfor %}
    </tr>
  </thead>
  <tbody>
      <tr>
        {% for day in week.days %}
          {% if week.now == day %}
            <td class="table-success">
          {% else %}
            <td>
          {% endif %}
          {% if week.first.month != day.month %}
            {{ day | date:"m/d" }}
          {% else %}
            {{ day.day }}
          {% endif %}
          </td>
        {% endfor %}
      </tr>
      <tr>
        {% for day_schedule_list in week.schedule_list %}
          <td>
            {% for s in day_schedule_list %}
                {{ s.start_time }} - {{ s.end_time }}<br>
                {{ s.summary }}<br>
                {{ s.description | linebreaks }}
            {% endfor %}
          </td>
        {% endfor %}
      </tr>
  </tbody>
</table>
{% endblock %}

週間カレンダーと違うのが、以下の部分です。スケジュールを取り出しています。

      <tr>
        {% for day_schedule_list in week.schedule_list %}
          <td>
            {% for s in day_schedule_list %}
                {{ s.start_time }} - {{ s.end_time }}<br>
                {{ s.summary }}<br>
                {{ s.description | linebreaks }}
            {% endfor %}
          </td>
        {% endfor %}
      </tr>

曜日と日付を一緒にする等のケース

現状では曜日と日付が別の行に表示されています。 もしかしたらスケジュール部分も一つの行に収めたいかもしれません。

views.py

プログラム中のコメントのとおりで、一緒に取り出したいものをzip関数内に指定します。

class WeekWithScheduleCalendar(WeekWithScheduleMixin, generic.TemplateView):
    """スケジュール付きの週間カレンダーを表示するビュー"""
    template_name = 'sampleapp/week_with_schedule.html'

    def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs)
        week = self.get_week_calendar()
        context['week'] = week
        # {% for week_name, day, day_schedule_list in week_row %}
        # とテンプレートで取り出すための記述
        context['week_row'] = zip(
            week['week_names'],
            week['days'],
            week['schedule_list']
        )
        return context

week_with_schedule.html

曜日、日付、スケジュールが毎回のループで同時に取り出せています。

{% extends 'sampleapp/base.html' %}
{% block content %}

<a href="{% url 'sampleapp:week_with_schedule' week.previous.year week.previous.month  week.previous.day %}">前週</a>
{{ week.first | date:"Y年m月d日" }}〜{{ week.last | date:"Y年m月d日" }}
<a href="{% url 'sampleapp:week_with_schedule' week.next.year week.next.month  week.next.day %}">次週</a>
<table class="table table-bordered">
  <tbody>
      <tr>
        {% for week_name, day, day_schedule_list in week_row %}
          <td>
          <!-- 曜日 -->
          {{ week_name }}
          <br>
          <!-- 日付 -->
          {% if week.first.month != day.month %}
            {{ day | date:"m/d" }}
          {% else %}
            {{ day.day }}
          {% endif %}
          <br>
            <!-- その日のスケジュール -->
            {% for s in day_schedule_list %}
                {{ s.start_time }} - {{ s.end_time }}<br>
                {{ s.summary }}<br>
                {{ s.description | linebreaks }}
            {% endfor %}
          </td>
        {% endfor %}
      </tr>
  </tbody>
</table>
{% endblock %}

このような見た目になります。

カレンダーを縦にする

さきほどのを応用すれば、縦のカレンダーも簡単に作れます。

week_with_schedule.html

{% extends 'sampleapp/base.html' %}
{% block content %}

<a href="{% url 'sampleapp:week_with_schedule' week.previous.year week.previous.month  week.previous.day %}">前週</a>
{{ week.first | date:"Y年m月d日" }}〜{{ week.last | date:"Y年m月d日" }}
<a href="{% url 'sampleapp:week_with_schedule' week.next.year week.next.month  week.next.day %}">次週</a>
<table class="table table-bordered">
  <tbody>
        {% for week_name, day, day_schedule_list in week_row %}
          <tr>
            <td>
              {% if week.first.month != day.month %}
                {{ day | date:"m/d" }}
              {% else %}
                {{ day.day }}
              {% endif %}
              ({{ week_name }})
            </td>

            <td>
            {% for s in day_schedule_list %}
                {{ s.start_time }} - {{ s.end_time }}<br>
                {{ s.summary }}<br>
                {{ s.description | linebreaks }}
            {% endfor %}
            </td>
          </tr>
        {% endfor %}
  </tbody>
</table>
{% endblock %}

このような見た目になります。

Twitterでシェア FaceBookでシェア はてなブックマークでシェア

記事にコメントする