[Project] 플레이데이터 장고 미니프로젝트 - 캘린더 앱 생성하기

싱숭생숭어·2023년 5월 14일
2

project

목록 보기
4/5
post-thumbnail

위 포스팅을 작성하는데 참고한 블로그


calendar app

calendar app 생성

VSCODE 터미널에 python3 manage.py startapp cal 통해 새로운 앱 생성


settings.py의 installed_apps에 cal app 추가

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',
]

cal/views.py에 index view 생성

from django.shortcuts import render
from django.http import HttpResponse

# Create your views here.

def index(request):
    return HttpResponse('hello')

cal/urls.py에 생성한 view 추가

from django.urls import path
from . import views

app_name = 'cal'

urlpatterns = [
    path('', views.index, name='index'),
]

config/urls.py에 cal path 추가

프로젝트 자체의 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" 표시)


migration 실행, 페이지가 잘 실행되는지 확인

python3 manage.py migrate
python3 manage.py runserver

cal 페이지 실행 시, 화면에 hello가 잘 나오는 걸 확인할 수 있다 !


cal/models.py에 model 생성

내가 구현하고 싶은 것: 하루하루 수업때마다 배운 수업 키워드와 그에 대한 수업 내용을 간략하게 입력가능한 캘린더로, 캘린더 페이지에 접속하면 달력안에 작성한 키워드들을 한눈에 확인가능하게 끔 !

따라서 캘린더의 날짜를 선택하면 나오는 수업 내용을 작성가능한 페이지를 만들고자 했다.
(+만약 한가지의 수업을 여러 일차동안 진행했을 경우, 진행한 기간도 확인 가능한,,!)

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()

위 모델을 입력 페이지에 구현한다면 아마도 위 그림과 같을 것이다 !


admin 페이지에서도 관리할 수 있도록 cal/admin.py 수정

from django.contrib import admin
from cal.models import class_contents

admin.site.register(class_contents)
  • 이때 만약 admin(superuser)이 없다면, python manage.py createsuperuser를 통해 관리자 계정을 생성하도록 하자

cal/utils.py에서 calendar 클래스 정의

방금 위에서 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>'
  • formatday 함수는 달력에서 하루를 형식화하는 함수로, 필터링한 class_contents 모델의 데이터를 contents라는 인자로 받아서 해당 날짜에 대한 수업의 keyword를 모두 출력함
	def formatweek(self, theweek, contents):
		week = ''
		for d, weekday in theweek:
			week += self.formatday(d, contents)
		return f'<tr> {week} </tr>'
  • formatweek 함수는 달력에서 주를 형식화하는 함수로, theweek와 contents를 인자로 받아 각 날짜에 대해 formatday 함수를 호출하여 출력함
	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
  • formatmonth 함수는 달력에서 한달을 형식화하는 함수로, withyear 인자가 True일 경우 연도를 포함한 형식으로 출력함. 이 함수는 class_contents 모델의 데이터를 필터링하여 해달 월에 대한 달력을 출력하고, formatmonthname, formatweekheader, monthdays2calendar, formatweek 메서드를 호출하여 달력을 생성함

웹페이지에서 달력을 표시하기 위해 cal/views.py 수정

위에서 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
  • datetime 모듈과 장고의 여러 기능을 활용하기 위한 모듈들을 import
  • 방금 전 cal/utils.py에서 생성한 Calendar 클래스도 import
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의 값을 받아오는 변수

    • URL 매개변수는 웹 페이지의 URL에서 추출되는 값으로, 사용자가 웹 페이지에 접근할 때 URL에 포함된 정보임. 예를 들어, /cal/?day=2023-05-20와 같은 URL이 있다면, day라는 이름의 URL 매개변수가 2023-05-20이라는 값으로 전달되는 것 !
    • 그 후, 위의 get_date() 함수를 통해 해당 값을 처리하여 날짜 객체로 변환해 get_context_data() 함수 안에서 d라는 인자로 정의해줌

cal의 template 파일 생성

templates/cal/base.html

{% 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>

templates/cal/calendar.html

{% extends 'cal/base.html' %}

{% block content %}
{{ calendar }}
{% endblock %}

cal/urls.py 수정

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로 넣어주었다.


static/cal/style.css 파일 생성

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과 웹페이지를 통해 새로운 수업 내용을 기록하는 기능에 대해 구현을 해볼 것이다 !

profile
공부합시당

1개의 댓글

comment-user-thumbnail
2024년 4월 17일

대단하십니다! 저도 따라해보고 있는데 오류가 하나 있습니다. model = Event 에서 Event가 아니죠?

답글 달기