naritoブログ

【お知らせ】
・コメントで質問等をしたが返事が返ってこない場合、私はそれを見落としています。
その場合は再度コメントをするかメールをしてください(toritoritorina@gmail.com)。

・近いうちに新しいブログが作成されます。わーお!

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

約94日前 2018年6月21日15:04
プログラミング関連
Django Python

概要


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です。
はみでた!

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

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

今回のスケジュール部分の表示の仕方はあくまで一例です。色々試してみてください。
生徒 約94日前 2018年6月22日1:21 返信する
以前、スケジュール付き月間カレンダーについて質問をさせていただいた者です。
narito様のお早い更新、それもアドバイスに留まらず完成したものとしてブログにあげていただいたこと、とても感謝しております。
今後も精進してまいります。本当にありがとうございました
名無し 約60日前 2018年7月25日19:44 返信する
添付ファイルダウンロード(2018-07-25_194255.png)
こんにちは

すみません、余計なお世話かもしれませんが
なりとさんのツイッターに載っているブログへのリンクが
おかしいことになっています。
添付画像のとおりです。
一応ご報告させていただきます。
なりと 約60日前 2018年7月25日19:57
わざわざのご連絡ありがとうございます。修正しました。