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

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

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

旧ブログ移行記事です。

概要

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

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

scalendar/views.py

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

class MonthWithScheduleMixin(MonthCalendarMixin):
    """スケジュール付きの、月間カレンダーを提供するMixin"""
    model = Schedule
    date_field = 'date'
    order_field = 'start_time'

    def get_month_schedules(self, days):
        """(日付, その日のスケジュール)なリストを返す"""
        day_with_schedules = []
        for week in days:
            week_list = []
            for day in week:
                lookup = {self.date_field: day}
                queryset = self.model.objects.filter(**lookup)
                if self.order_field:
                    queryset = queryset.order_by(self.order_field)
                week_list.append(
                    (day, queryset)
                )
            day_with_schedules.append(week_list)
        return day_with_schedules

    def get_month_calendar(self):
        calendar_data = super().get_month_calendar()
        day_with_schedules = self.get_month_schedules(calendar_data['days'])
        calendar_data['days'] = day_with_schedules
        return calendar_data

MonthWithScheduleMixinの説明

get_month_calendarメソッドを上書きし、親のget_month_calendarで返された辞書のdaysキーを上書きします。 daysキーには、もともと以下のようなリストで値が保存されています。2次元で、週ごとに日付データが入っています。

[
[datetime.date(2018, 5, 28), datetime.date(2018, 5, 29), datetime.date(2018, 5, 30), datetime.date(2018, 5, 31), datetime.date(2018, 6, 1), datetime.date(2018, 6, 2), datetime.date(2018, 6, 3)],
[datetime.date(2018, 6, 4), datetime.date(2018, 6, 5), datetime.date(2018, 6, 6), datetime.date(2018, 6, 7), datetime.date(2018, 6, 8), datetime.date(2018, 6, 9), datetime.date(2018, 6, 10)],
[datetime.date(2018, 6, 11), datetime.date(2018, 6, 12), datetime.date(2018, 6, 13), datetime.date(2018, 6, 14), datetime.date(2018, 6, 15), datetime.date(2018, 6, 16), datetime.date(2018, 6, 17)],
[datetime.date(2018, 6, 18), datetime.date(2018, 6, 19), datetime.date(2018, 6, 20), datetime.date(2018, 6, 21), datetime.date(2018, 6, 22), datetime.date(2018, 6, 23), datetime.date(2018, 6, 24)],
[datetime.date(2018, 6, 25), datetime.date(2018, 6, 26), datetime.date(2018, 6, 27), datetime.date(2018, 6, 28), datetime.date(2018, 6, 29), datetime.date(2018, 6, 30), datetime.date(2018, 7, 1)]
]

これを、以下のように変更しているのが今回のMixnです。内部が(日付, スケジュール)なタプルになりました。

[
[(datetime.date(2018, 5, 28), <QuerySet []>), (datetime.date(2018, 5, 29), <QuerySet []>), (datetime.date(2018, 5, 30), <QuerySet []>), (datetime.date(2018, 5, 31), <QuerySet []>), (datetime.date(2018, 6, 1), <QuerySet []>), (datetime.date(2018, 6, 2), <QuerySet []>), (datetime.date(2018, 6, 3), <QuerySet []>)], 
...
...
]

sampleapp/urls.py

    path(
        'month_with_schedule/',
        views.MonthWithScheduleCalendar.as_view(), name='month_with_schedule'
    ),
    path(
        'month_with_schedule/<int:year>/<int:month>/',
        views.MonthWithScheduleCalendar.as_view(), name='month_with_schedule'
    ),

sampleapp/views.py

今までと同じ流れです。

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

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

month_with_schedule.html

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

{% extends 'sampleapp/base.html' %}
{% block content %}
<style>
    table {
        table-layout: fixed;
    }

    td > div {
      height: 100px;
      overflow: hidden;
      white-space: nowrap;
    }

</style>
<a href="{% url 'sampleapp:month_with_schedule' month.previous.year month.previous.month %}">前月</a>
{{ month.current | date:"Y年m月" }}
<a href="{% url 'sampleapp:month_with_schedule' month.next.year month.next.month %}">次月</a>
<table class="table">
  <thead>
    <tr>
      {% for w in month.week_names %}
        <th>{{ w }}</th>
      {% endfor %}
    </tr>
  </thead>
  <tbody>
    {% for week in month.days %}
      <tr>
        {% for day, schedules in week %}
          {% if month.now == day %}
            <td class="table-success">
          {% else %}
            <td>
          {% endif %}

          <div>
            {% if month.current.month != day.month %}
              {{ day | date:"m/d" }}
            {% else %}
              {{ day.day }}
            {% endif %}

            {% for schedule in schedules %}
                <p>{{ schedule.summary }}</p>
            {% endfor %}
          </div>
          </td>
        {% endfor %}
      </tr>
    {% endfor %}
  </tbody>
</table>
{% endblock %}

いくつか月間カレンダーと違う部分があります。 (日付, スケジュール)というタプルになったので、そのようにforタグで取り出しています。

        {% for day, schedules in week %}

スケジュールを取り出しているのは以下の部分です。とりあえず、概要だけ表示しています。

            {% for schedule in schedules %}
                <p>{{ schedule.summary }}</p>
            {% endfor %}

また、日付やスケジュール部分をdivで囲っていることに注意してください。 後ほど紹介しますが、divで囲わないと、いくつかのCSSの設定ができなくなります。

          <div>
            {% if month.current.month != day.month %}
              {{ day | date:"m/d" }}
            {% else %}
              {{ day.day }}
            {% endif %}

            {% for schedule in schedules %}
                <p>{{ schedule.summary }}</p>
            {% endfor %}
          </div>

次に<style>内です。table-layout: fixedはテーブルの各幅を均等にします。

    table {
        table-layout: fixed;
    }

そして、td内につくったdivにいくつか指定をしています。heightは、高さの指定ですね。 overflow:hiddenですが、これは説明するよりも実際に外して見せたほうがわかりやすいので、外してみます。

    td > div {
      height: 100px;
      overflow: hidden;
      white-space: nowrap;
    }

外すと、隠れていた内容がはみでましたね。 今回はheight:100pxとして、100pxに収まりきらない内容は隠しました。その指定がoverflow:hiddenです。 overflow:hiddenがない場合ははみ出る

white-space: nowrap;を消すと、幅に収まらない場合は折り返されて表示されます。 white-space:nowrapを消すと折り返される

もしはみでた部分も表示したいならば、overflow:auto;にすると不恰好ですがスクロールバーがつけれます。 overflow:autoにするとスクロールバーがつく

今回のスケジュール部分の表示の仕方はあくまで一例です。色々試してみてください。

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

記事にコメントする