위 포스팅을 작성하는데 참고한 블로그
- https://www.huiwenteo.com/normal/2018/07/24/django-calendar.html
- 위 블로그를 참고했지만 기존 project에 맞게 입맛대로 수정한 부분이 있으므로 주의 !
VSCODE 터미널에 python3 manage.py startapp cal
통해 새로운 앱 생성
INSTALLED_APPS = [
'cal.apps.CalConfig', #이 부분 추가함(이건 cal/apps.py의 class명으로 지정하면 됨)
'common.apps.CommonConfig',
'pybo.apps.PyboConfig',
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
]
from django.shortcuts import render
from django.http import HttpResponse
# Create your views here.
def index(request):
return HttpResponse('hello')
from django.urls import path
from . import views
app_name = 'cal'
urlpatterns = [
path('', views.index, name='index'),
]
프로젝트 자체의 urls.py에도 127.0.0.1:8000/cal을 통해 cal 페이지로 접속하기 위한 path를 추가해주어야 한다.
from django.contrib import admin
from django.urls import include, path
from pybo.views import base_views
urlpatterns = [
path('admin/', admin.site.urls),
path('pybo/', include('pybo.urls')),
path('common/', include('common.urls')),
path('', base_views.index, name='index'), # '/' 에 해당되는 path
path('cal/', include('cal.urls')), #새로운 cal app에 대한 path <- 이 부분 추가
]
현재 127.0.0.1:8000로 접속하면 초기 페이지 -> pybo
path('', base_views.index, name='index'),
이 부분에서 확인할 수 있음
path('cal/', include('cal.urls')),
이 부분을 추가해 127.0.0.1:8000/cal을 입력하면 cal app 화면이 나오게 지정했다.(cal.urls -> views.index -> 화면에 "hello" 표시)
python3 manage.py migrate
python3 manage.py runserver
cal 페이지 실행 시, 화면에 hello가 잘 나오는 걸 확인할 수 있다 !
내가 구현하고 싶은 것: 하루하루 수업때마다 배운 수업 키워드와 그에 대한 수업 내용을 간략하게 입력가능한 캘린더로, 캘린더 페이지에 접속하면 달력안에 작성한 키워드들을 한눈에 확인가능하게 끔 !
따라서 캘린더의 날짜를 선택하면 나오는 수업 내용을 작성가능한 페이지를 만들고자 했다.
(+만약 한가지의 수업을 여러 일차동안 진행했을 경우, 진행한 기간도 확인 가능한,,!)
class_content라는 모델을 생성하고 모델 속성을 아래 표와 같이 정리하였다.
<class_content 모델>
속성 | 설명 |
---|---|
keyword | 수업 대표 키워드(ex: docker, sql, django ..) |
content | 수업의 세부 내용(ex: sql inner join 구문, django 게시판 로그인 기능) |
start_time | 해당 수업의 시작 일자 |
end_time | 해당 수업의 끝난 일자 |
from django.db import models
class class_contents(models.Model):
keyword = models.CharField(max_length=200)
content = models.TextField()
start_time = models.DateTimeField()
end_time = models.DateTimeField()
위 모델을 입력 페이지에 구현한다면 아마도 위 그림과 같을 것이다 !
from django.contrib import admin
from cal.models import class_contents
admin.site.register(class_contents)
python manage.py createsuperuser
를 통해 관리자 계정을 생성하도록 하자방금 위에서 class_contents 모델을 설정했으므로 달력을 만들 차례 !
cal/utils.py 파일을 생성한 후, python 기본 calendar 모듈의 HTMLcalendar 메서드를 상속받은 Calendar 클래스를 정의해줄 것이다.
파이썬으로 html 달력페이지를 만드는 방법은 아래 블로그 참고 !
from datetime import datetime, timedelta
from calendar import HTMLCalendar
from .models import class_contents
class Calendar(HTMLCalendar):
def __init__(self, year=None, month=None):
self.year = year
self.month = month
super(Calendar, self).__init__()
def formatday(self, day, contents):
contents_per_day = contents.filter(start_time__day=day)
d = ''
for event in contents_per_day:
d += f'<li> {class_contents.keyword} </li>'
if day != 0:
return f"<td><span class='date'>{day}</span><ul> {d} </ul></td>"
return '<td></td>'
def formatweek(self, theweek, contents):
week = ''
for d, weekday in theweek:
week += self.formatday(d, contents)
return f'<tr> {week} </tr>'
def formatmonth(self, withyear=True):
contents = class_contents.objects.filter(start_time__year=self.year, start_time__month=self.month)
cal = f'<table border="0" cellpadding="0" cellspacing="0" class="calendar">\n'
cal += f'{self.formatmonthname(self.year, self.month, withyear=withyear)}\n'
cal += f'{self.formatweekheader()}\n'
for week in self.monthdays2calendar(self.year, self.month):
cal += f'{self.formatweek(week, contents)}\n'
return cal
위 코드는 달력을 생성하고 수업 내용을 표시하는 calendar
클래스를 정의한 코드이다.
각 단락마다 코드를 나누어 해석해보자 !
from datetime import datetime, timedelta
from calendar import HTMLCalendar
from .models import class_contents
날짜 및 시간 연산을 위해 datetime 및 timedelta 모듈을 import
HTML 형식의 달력을 생성하기 위해 파이썬 기본 내장 메서드인 HTMLCalendar import
cal 앱의 class_contents 모델을 가져옴
class Calendar(HTMLCalendar):
def __init__(self, year=None, month=None):
self.year = year
self.month = month
super(Calendar, self).__init__()
Calendar 클래스는 HTMLCalendar 클래스를 상속받아 달력을 생성함
Calendar 클래스는 __init__
,formatday
, formatweek
, formatmonth
네 가지의 내장 함수를 가짐
init 함수는 Calendar 객체가 생성될 때 호출되며, 이 함수는 년도와 월을 지정하고 super 메서드 호출을 통해 HTMLCalendar 클래스를 초기화 함
def formatday(self, day, contents):
contents_per_day = contents.filter(start_time__day=day)
d = ''
for event in contents_per_day:
d += f'<li> {class_contents.keyword} </li>'
if day != 0:
return f"<td><span class='date'>{day}</span><ul> {d} </ul></td>"
return '<td></td>'
def formatweek(self, theweek, contents):
week = ''
for d, weekday in theweek:
week += self.formatday(d, contents)
return f'<tr> {week} </tr>'
def formatmonth(self, withyear=True):
contents = class_contents.objects.filter(start_time__year=self.year, start_time__month=self.month)
cal = f'<table border="0" cellpadding="0" cellspacing="0" class="calendar">\n'
cal += f'{self.formatmonthname(self.year, self.month, withyear=withyear)}\n'
cal += f'{self.formatweekheader()}\n'
for week in self.monthdays2calendar(self.year, self.month):
cal += f'{self.formatweek(week, contents)}\n'
return cal
위에서 utils.py파일에서 달력을 만들었으므로, 이 달력을 웹페이지에서 표시하기 위해 views.py파일을 수정해야 한다.
from datetime import datetime
from django.shortcuts import render
from django.http import HttpResponse
from django.views import generic
from django.utils.safestring import mark_safe
from .models import *
from .utils import Calendar
class CalendarView(generic.ListView):
model = Event
template_name = 'cal/calendar.html'
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
# use today's date for the calendar
d = get_date(self.request.GET.get('day', None))
# Instantiate our calendar class with today's year and date
cal = Calendar(d.year, d.month)
# Call the formatmonth method, which returns our calendar as a table
html_cal = cal.formatmonth(withyear=True)
context['calendar'] = mark_safe(html_cal)
return context
def get_date(req_day):
if req_day:
year, month = (int(x) for x in req_day.split('-'))
return date(year, month, day=1)
return datetime.today()
각 단락마다 코드를 나누어 해석해보자 !
from datetime import datetime
from django.shortcuts import render
from django.http import HttpResponse
from django.views import generic
from django.utils.safestring import mark_safe
from .models import *
from .utils import Calendar
class CalendarView(generic.ListView):
model = class_contents
template_name = 'cal/calendar.html'
CalendarView 클래스는 Django의 generic.ListView 클래스를 상속받아서, 우리의 class_contents 모델을 이용하여 DB에서 달력에 보여줄 이벤트들을 가져오는 뷰
이 뷰는 cal/calendar.html 템플릿 파일과 연결 !(calendar.html 템플릿 파일은 아래에서 생성할 예정 ~)
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
# use today's date for the calendar
d = get_date(self.request.GET.get('day', None))
# Instantiate our calendar class with today's year and date
cal = Calendar(d.year, d.month)
# Call the formatmonth method, which returns our calendar as a table
html_cal = cal.formatmonth(withyear=True)
context['calendar'] = mark_safe(html_cal)
return context
get_context_data() 함수는 context 데이터를 가져오는 함수로, 현재 달력에 보여줄 년도와 월 정보를 가져오고 Calendar 클래스의 인스턴스를 생성함
이후 Calendar 클래스의 formatmonth() 메서드를 호출하여 달력을 HTMl 형식으로 반환하고, 이를 템플릿에서 활용할 수 있도록 context에 저장함
위의 get_context_data() 함수는 기본적으로 부모 클래스인 django.views.generic.base.View 클래스의 get_context_data() 메서드를 오버라이딩한 것이며, **kwargs
는 부모 클래스에서 전달된 인자를 받아오는 용도로 사용됩니다. 따라서, **kwargs
를 사용하여 기존의 컨텍스트 데이터를 유지하면서 새로운 데이터를 추가하거나 변경할 수 있도록 한 코드!(어렵지만 이 정도로만 이해하자 ^^,,)
def get_date(req_day):
if req_day:
year, month = (int(x) for x in req_day.split('-'))
return date(year, month, day=1)
return datetime.today()
get_date() 함수는 URL에서 전달된 년도와 월 정보를 추출하여, datatime 객체로 변환하는 함수로, 위에서 선언한 get_context_data 함수 내에서 호출됨
위의 if문을 해석 -> req_day 값이 존재하면 해당 날짜로부터 date 객체를 생성하고, req_day 값이 존재하지 않을 경우 현재 날짜를 사용해 date 객체를 생성한다는 뜻
req_day는 URL 매개변수 day의 값을 받아오는 변수
{% load static %}
<!doctype html>
<html lang="ko">
<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://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css">
<link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.0.10/css/all.css">
<link rel="stylesheet" type="text/css" href="{% static 'cal/style.css' %}">
<title>Django Calendar App</title>
</head>
<body>
{% block content %}
{% endblock %}
<!-- Optional JavaScript -->
<!-- jQuery first, then Popper.js, then Bootstrap JS -->
<script src="https://code.jquery.com/jquery-3.2.1.slim.min.js" integrity="sha384-KJ3o2DKtIkvYIK3UENzmM7KCkRr/rE9/Qpg6aAZGJwFDMVNA/GpGFF93hXpG5KkN"
crossorigin="anonymous"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.12.9/umd/popper.min.js" integrity="sha384-ApNbgh9B+Y1QKtv3Rn7W3mgPxhU9K/ScQsAP7hUibX39j7fakFPskvXusvfa0b4Q"
crossorigin="anonymous"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/js/bootstrap.min.js" integrity="sha384-JZR6Spejh4U02d8jOt6vLEHfe/JQGiRRSQQxSfFWpi1MquVdAyjUar5+76PVCmYl"
crossorigin="anonymous"></script>
{% block script %}
{% endblock %}
</body>
</html>
{% extends 'cal/base.html' %}
{% block content %}
{{ calendar }}
{% endblock %}
from django.urls import path
from . import views
app_name = 'cal'
urlpatterns = [
path('', views.CalendarView.as_view(), name='calendar')
]
위처럼 기존의 views.index
path를 삭제하고 대신 views.CalendarView.as_view()
을 localhost/cal/의 기본 path로 넣어주었다.
cal/base.html 템플릿 내용 중
<link rel="stylesheet" type="text/css" href="{% static 'cal/style.css' %}">
이 부분에 해당되는 static/cal/style.css 파일을 생성하고 내용을 아래와 같이 입력하였다.
.calendar {
width: 98%;
margin: auto;
font-size: 13px;
}
.calendar tr, .calendar td {
border: 1px solid black;
}
.calendar th {
padding: 10px;
text-align: center;
font-size: 18px;
}
.calendar td {
width: 200px;
height: 150px;
padding: 20px 0px 0px 5px;
}
.month {
font-size: 25px;
}
.date {
font-size: 16px;
}
ul {
height: 100%;
padding: 0px 5px 0px 20px;
}
a {
color: #17a2b8;
}
여기까지 수정하고 python3 manage.py makemigrations
+ python3 manage.py migrate
+ python3 manage.py runserver
를 해보자 !
그럼 이제 드디어 127.0.0.1:8000/cal/
페이지에서 구현된 달력을 볼 수 있다 !!!!🥹🥹🥹(다섯시간동안의 피땀눈물로 드뎌 1차 구현 완료 ,,,챗 지피티 넘 감사하다,, 근데 달력 소스가 아무래도 영어다보니 ,,, 한국어로 구현하는 건 어찌하는지 아직 모르겠다 ㅠ)
이제 다음 포스팅에서는 이전/이후의 월으로 이동가능한 button과 웹페이지를 통해 새로운 수업 내용을 기록하는 기능에 대해 구현을 해볼 것이다 !
대단하십니다! 저도 따라해보고 있는데 오류가 하나 있습니다. model = Event 에서 Event가 아니죠?