장고 - django-fullcalendar 라이브러리 적용

  • 이번 포스트에서는 django에서 fullcalendar 라이브러리를 적용하는 방법에 대해 알아볼 것이다.

ex) extore_project

1. fullcalendar 설치

  • 경로 : extore_project 파일 위치
    1
    $ pip install -e git+https://github.com/rodrigoamaral/django-fullcalendar.git#egg=django-fullcalendar

2. INSTALLED_APPS에 fullcalendar 모듈 추가

  • 경로 : config(프로젝트) > settings.py
    1
    2
    3
    4
    INSTALLED_APPS = (
    ...
    'fullcalendar',
    )

3. fullcalendar 파일 작성

  • 경로 : schedule > fullcalendar.py
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    from django.conf import settings

    FULLCALENDAR_DEFAULTS = {
    'css_url': '//cdnjs.cloudflare.com/ajax/libs/fullcalendar/1.6.4/fullcalendar.css',
    'print_css_url': '//cdnjs.cloudflare.com/ajax/libs/fullcalendar/1.6.4/fullcalendar.print.css',
    'javascript_url': '//cdnjs.cloudflare.com/ajax/libs/fullcalendar/1.6.4/fullcalendar.min.js',
    'jquery_url': '//code.jquery.com/jquery-3.4.1.min.js',
    'jquery_ui_url': '//code.jquery.com/ui/1.10.4/jquery-ui.js',
    }

    FULLCALENDAR = FULLCALENDAR_DEFAULTS.copy()
    FULLCALENDAR.update(getattr(settings, 'FULLCALENDAR', {}))

    def css_url():
    return FULLCALENDAR['css_url']

    def print_css_url():
    return FULLCALENDAR['print_css_url']

    def javascript_url():
    return FULLCALENDAR['javascript_url']

    def jquery_url():
    return FULLCALENDAR['jquery_url']

    def jquery_ui_url():
    return FULLCALENDAR['jquery_ui_url']

4. fullcalendar 템플릿 태그 작성

  • 경로 : schedule > templatetags > fullcalendar_tags.py
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    from django import template
    from django.utils.safestring import mark_safe
    from ..fullcalendar import css_url, print_css_url, javascript_url, jquery_url, jquery_ui_url

    register = template.Library()

    @register.simple_tag
    def fullcalendar_css_url():
    return css_url()

    @register.simple_tag
    def fullcalendar_print_css_url():
    return print_css_url()

    @register.simple_tag
    def fullcalendar_javascript_url():
    return javascript_url()

    @register.simple_tag
    def fullcalendar_jquery_url():
    return jquery_url()

    @register.simple_tag
    def fullcalendar_jquery_ui_url():
    return jquery_ui_url()

    @register.simple_tag
    def fullcalendar_css():
    url = fullcalendar_css_url()
    return mark_safe("<link href='%s' rel='stylesheet'>" % url)

    @register.simple_tag
    def fullcalendar_print_css():
    url = fullcalendar_print_css_url()
    return mark_safe("<link href='%s' rel='stylesheet' media='print'>" % url)

    @register.simple_tag
    def fullcalendar_jquery():
    url = fullcalendar_jquery_url()
    return mark_safe("<script src='%s'></script>" % url)

    @register.simple_tag
    def fullcalendar_jquery_ui():
    url = fullcalendar_jquery_ui_url()
    return mark_safe("<script src='%s'></script>" % url)

    @register.simple_tag
    def fullcalendar_javascript():
    url = fullcalendar_javascript_url()
    return mark_safe("<script src='%s'></script>" % url)

5. fullcalendar를 표시할 html 파일 작성

  • 경로 : config > layout > schedule-base.html

    • 이곳에 fullcalendar_tags 탬플릿 태그 적용
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      <!DOCTYPE html>
      <html class="no-js">
      <head>
      <meta charset="utf-8">
      <meta http-equiv="X-UA-Compatible" content="IE=edge">
      <title></title>
      <meta name="description" content="">
      <meta name="viewport" content="width=device-width, initial-scale=1">
      {% load fullcalendar_tags %}

      {% fullcalendar_css %}
      {% fullcalendar_print_css %}
      {% fullcalendar_jquery %}
      {% fullcalendar_jquery_ui %}
      {% fullcalendar_javascript %}

      {% calendar_init calendar_config_options %}

      {% block extra_style %}
      {% endblock %}
      </head>
      <body>
      <!-- 주의 : 이 곳에 jquery script를 작성하면, fullcalendar가 작동되지 않는다. -->
      {% block extra_script %}
      {% endblock %}
      </body>
      </html>
  • 경로 : schedule(앱) > templates > schedule > index.html

    • fullcalendar가 보여질 html 코드 작성
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      {% extends "schedule-base.html" %}

      {% load fullcalendar_tags %}

      {% block content %}
      <div>
      ...
      <div style="height:800px; line-height:800px; width:800px; margin:0 auto;">
      <div style="width:800px; display:inline-block; vertical-align:middle; line-height:normal;">
      {% calendar %}
      </div>
      </div>
      </div>
      {% endblock %}

6. util.py 작성

  • 경로 : schedule > util.py
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    # coding: utf-8
    import json


    def date_handler(obj):
    """
    Handles JSON serialization for datetime values
    """
    return obj.isoformat() if hasattr(obj, 'isoformat') else obj


    def convert_field_names(event_list):
    """
    Converts atribute names from Python code convention to the
    attribute names used by FullCalendar
    """
    for event in event_list:
    for key in event.keys():
    event[snake_to_camel_case(key)] = event.pop(key)
    return event_list


    def snake_to_camel_case(s):
    """
    Converts strings from 'snake_case' (Python code convention)
    to CamelCase
    """
    new_string = s

    leading_count = 0
    while new_string.find('_') == 0:
    new_string = new_string[1:]
    leading_count += 1

    trailing_count = 0
    while new_string.rfind('_') == len(new_string) - 1:
    new_string = new_string[:-1]
    trailing_count += 1

    new_string = ''.join([word.title() for word in new_string.split('_')])
    leading_underscores = '_' * leading_count
    trailing_underscores = '_' * trailing_count
    return leading_underscores + new_string[0].lower() + new_string[1:] + trailing_underscores


    def events_to_json(events_queryset):
    """
    Dumps a CalendarEvent queryset to the JSON expected by FullCalendar
    """
    events_values = list(events_queryset.values('id', 'title', 'start', 'end', 'all_day'))
    events_values = convert_field_names(events_values)
    return json.dumps(events_values, default=date_handler)


    def calendar_options(event_url, options):
    """
    Builds the Fullcalendar options array
    This function receives two strings. event_url is the url that returns a JSON array containing
    the calendar events. options is a JSON string with all the other options.
    """
    event_url_option = 'events: "%s"' % (event_url,)
    s = options.strip()
    if s is not None and '{' in s:
    pos = s.index('{') + 1
    else:
    return '{%s}' % (event_url_option)
    return s[:pos] + event_url_option + ', ' + s[pos:]

7. fullcalendar 모델 작성

  • 경로 : schedule > models.py
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    from django.db import models
    from django.contrib.auth import get_user_model
    from django.utils.translation import ugettext_lazy as _


    class CalendarEvent(models.Model):
    author = models.ForeignKey(get_user_model(), on_delete=models.SET_NULL, null=True, blank=True, related_name="schedules")
    title = models.CharField(_('Title'), max_length=200)
    start = models.DateTimeField(_('Start'))
    end = models.DateTimeField(_('End'))
    all_day = models.BooleanField(_('All day'), default=False)
    # models.DateTimeField(input_formats=["%d %b %Y %H:%M:%S %Z"])
    class Meta:
    verbose_name = _('Event')
    verbose_name_plural = _('Events')

    ordering = ['-start']

    # 관리자 사이트에 표시될 객체 이름 설정
    def __str__(self):
    return self.title

8. fullcalendar 뷰 작성

  • 경로 : schedlue > views.py
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    from django.shortcuts import render
    from django.http import HttpResponse
    from .models import CalendarEvent
    from .util import events_to_json, calendar_options

    OPTIONS = """{ timeFormat: "H:mm",
    header: {
    left: 'prev,next today',
    center: 'title',
    right: 'month,agendaWeek,agendaDay',
    },
    allDaySlot: false,
    firstDay: 0,
    weekMode: 'liquid',
    slotMinutes: 15,
    defaultEventMinutes: 30,
    minTime: 8,
    maxTime: 20,
    editable: false,
    dayClick: function(date, allDay, jsEvent, view) {
    if (allDay) {
    $('#calendar').fullCalendar('gotoDate', date)
    $('#calendar').fullCalendar('changeView', 'agendaDay')
    }
    },
    eventClick: function(event, jsEvent, view) {
    if (view.name == 'month') {
    $('#calendar').fullCalendar('gotoDate', event.start)
    $('#calendar').fullCalendar('changeView', 'agendaDay')
    }
    },
    }"""

    def index(request):
    event_url = 'all_events/'
    return render(request, 'schedule/index.html', {'calendar_config_options': calendar_options(event_url, OPTIONS)})

    def all_events(request):
    events = CalendarEvent.objects.all()
    return HttpResponse(events_to_json(events), content_type='application/json')

9. URL 설정

  • 경로 : schedule > urls.py
    1
    2
    3
    4
    5
    6
    7
    8
    9
    from django.urls import path
    from .views import *

    app_name = 'schedule'

    urlpatterns = [
    path('', index, name='index'),
    path('all_events/', all_events, name='all_events'),
    ]