3. 장고 핵심 기능 - Template
(1) 템플릿 설정 항목
- DTL(Django Template Engine) : 장고의 코어 템플릿 엔진. Jinja 엔진도 기본적으로 지원
- settings.py 에 TEMPLATES 항목에 있음
- BACKEND : 사용할 템플릿 엔진을 지정
- DIRS : 항목은 프로젝트 템플릿 파일이 위치한 디렉터리를 지정
- APP_DIRS : 템플릿 파일을 찾을 때 애플리케이션 내의 템플릿 디렉터리에서도 찾을지 여부를 지정. 기본값은 false, startproject 명령에 의해 생성될 경우에는 true로 설정됨
- OPTIONS : 템플릿 엔진에 따라 해당하는 옵션 항목들을 설정
- context_processors : 웹 요청에 들어있는 파라미터들을 인자로 받아 컨텍스트 데이터로 사용될 사전을 만드는 호출 가능한 객체를 지정, 보통은 함수로 정의됨. 이 함수들이 반환하는 사전은 최종 컨텍스트 데이터를 만들 때 추가, 디폴트는 빈 리스트
- debug : 템플릿 디버그 모드를 설정. true 설정 시 템플릿 렌더링 과정에서 에러 발생 시 템플릿 파일 내에서 에러가 발생한 줄을 다른 색으로 표시, 디폴트는 다른 설정 항목인 DEBUG 항목의 값을 따름
- loaders : 템플릿 로더 클래스를 지정. 템플릿 파일을 찾아 메모리로 로딩하는 역할
- string_if_invalid : 템플릿 변수가 잘못된 경우 대신 사용할 문자열을 지정, 디폴트는 공백 문자열
- file_charset : 템플릿 파일을 읽어 디코딩할 때 사용하는 문자셋 지정. 디폴트는 utf-8
(2) 템플릿 내부 처리 과정
- 사용할 템플릿 엔진 및 관련 옵션들을 결정해 Engine 객체를 생성
- TEMPLATES 설정 항목에 지정된 값들로 Engine 객체를 생성할 때 인자로 사용
- Engine 객체 뿐만이 아닌 Engine 객체에 소속된 Loader 객체도 같이 생성됨
- Engine 객체에 지정된 템플릿 로더는 하나 또는 여러 개로 나뉜 템플릿 파일들을 찾고 그 내용, 즉 템플릿 코드들을 하나로 모아 Template 객체를 생성
- 설정 옵션에 Loaders가 지정되지 않을 경우 디폴트 로더로 아래 2개의 클래스를 사용. 특별한 경우가 아닐 경우 디폴트 로더 2개를 변경 없이 사용하는 것이 일반적
- django.template.loaders.filesystem.Loader : DIRS 항목에 지정된 디렉터리 검색
- django.template.loaders.app_directories.Loader : 각 INSTALLED_APPS 하위의 templates/ 디렉터리를 검색
- 템플릿 파일 찾는 순서는 loaders 디폴트 설정에 filesystem.Loader가 먼저 나오므로 DIRS 항목에 지정된 디렉터를 가장 먼저 찾게 됨
- Loader 객체는 템플릿 파일들을 찾은 후 Template 객체를 생성
- 템플릿 파일을 찾은 결과는 보통 하나의 파일이나 {% extends %}, {% include %} 태그가 있는 경우 여러 개의 파일을 찾게 됨
- Template 객체의 render() 메서드를 호출, 컨텍스트 데이터와 요청(request) 데이터를 템플릿 코드에 대입하고 렌더링 결과로 최종 텍스트 파일을 만듬
- 렌더링을 위해 템플릿 코드와 컨텍스트 데이터가 필요
- 템플릿 코드는 앞선 단계에서, 컨텍스트 데이터는 뷰 함수에서 만들어져 템플릿 시스템으로 전달됨
- 웹 요청 객체인 HttpRequest 객체에 들어 있는 데이터도 컨텍스트 데이터로사 용됨
- 뷰에서 전달된 데이터만으로 최종 컨텍스트 데이터를 만들 때는 Context, HttpRequest 데이터를 포함해 최종 컨텍스트 데이터를 만들 때는 RequestContext 객체를 사용
- startproject 명령으로 프로젝트 생성 시 다음과 같은 컨텍스트 프로세서가 생성됨
- django.template.context_processors.debug : 현재 실행 환경의 DEBUG 모드를 지칭하는 debug 변수 및 웹 요청 처리 과정에 사용된 SQL 쿼리 정보를 담은 sql_queries 변수 2가지가 최종 컨텍스트 데이터에 추가됨
- django.template.context_processors.request : 현 요청의 HttpRequest를 지칭하는 request 변수가 최종 컨텍스트 데이터에 추가
- django.contrib.auth.context_processors.auth : 로그인 사용자를 지칭하는 user 변수 및 그 사용자의 권한을 지칭하는 perms 변수가 최종 컨텍스트 데이터에 추가됨
- django.contrib.messages.context_processors.messages : 메시지 리스트를 지칭하는 messages 변수와 메시지 레벨을 지칭하는 DEFAULT_MESSAGE_LEVELS 변수가 최종 컨텍스트에 추가됨
- django.template.context_processors.csrf : {% csrf_toekn %} 템플릿 태그 처리에 필요한 토큰이 최종 컨텍스트 데이터에 추가됨
(3) 제네릭 뷰의 디폴트 템플릿
- 모델을 대상으로 로직을 처리하는 제네릭 뷰 대부분은 디폴트 템플릿명을 가지고 있음 (template_name 속성을 지정하지 않는 겨우 사용되는 이름)
<app_label><model_name 소문자>_<template_name_suffix>.html
blog/post_list.html
(4) {% include %} 태그
- DRY 원칙 (Don't Repeat Yourself) 에 따라 코드의 중복을 줄일 수 있는 여러 가지 기능을 제공
- {% extends %} 태그 : 템플릿 상속 기능이 가장 대표적임
- {% include %} 태그 : 공통적으로 사용할 수 있는 템플릿 파일을 따로 만들어둔 다음 해당 태그로 공통 파일을 가져와 사용. 다른 템플릿 파일을 현재의 템플릿에 포함시킴
{% include "foo/bar.html" %}
{% include "foo/bar.html" with person="Jane" greeting="Hello" %}
{% include "foo/bar.html" with greeting="Hi" only %}
{{ greeting }}, {{ person|default:"friend" }}
(5) {% static %} 템플릿 태그
- 이미지, JS, CSS 파일들이 static 파일
- {% static %} 태그를 통해 정적 파일을 쉽게 처리할 수 있음
(6) staticfiles 애플리케이션 기능
- 정적 파일을 처리하기 위한 staticfiles 애플리케이션을 제공
- 다만 이는 개발 환경에서 사용되며 상용 환경에서는 사용하지 않음
- runserver를 실행시키고 정적 파일 처리가 필요할 경우 staticfiles 앱을 사용해 정적 파일을 처리
- 장고의 폼 동작은 Form 클래스로 폼을 정의하고 정의된 폼을 뷰에서 사용하며 최종적으로 템플릿 엔진에 의해 HTML 텍스트로 렌더링되는 절차를 거쳐 사용자에게 보여짐
- 장고에서 폼은 기본적으로 Form 클래스를 상속받아 정의
- 폼의 종류
- 일반 폼 : Form 클래스를 상속 받아 정의
- 모델 폼 : ModelForm 클래스를 상속 받아 정의. 폼 필드의 구성을 데이터베이스 모델 정의 기반으로 폼을 정의하는 경우에 사용. modelform_factory() 함수를 사용해 모델 폼을 정의할 수도 있음
- 폼셋 : 일반 폼을 여러 개 묶어서 한 번에 보여줌. formset_factory() 함수를 사용해 폼셋을 정의
- 모델 폼셋 : 데이터베이스 모델에 기초해서 만든 모델 폼을 여러 개 묶은 폼셋. modelformset_factory() 함수를 사용해 모델 폼셋을 정의
- 인라인 폼셋 : 두 모델 간의 관계가 1:N인 경우 N 모델에 기초해서 만든 모델 폼을 여러 개 묶은 폼셋, inlineformset_Factory() 함수를 사용해 인라인 폼셋을 정의
(2) 일반 폼 정의
class PhotoForm(forms.Form):
album = forms.ModelChoiceField(queryset=Album.objects.all())
title = forms.CharField(label = 'TITLE', max_length=30)
description = forms.CharField(label = 'Photo Description', widget=forms.Textarea)
image = ImageField(label='IMAGE')
upload_dt = forms.DateTimeField(label = 'UPLOADATE')
- 일반 폼으로 작성하기 위해서는 직접 폼 필드를 지정해야 하므로 모델의 필드와 폼의 필드 간 매핑 룰이 필요
- 모델의 ForeignKey 필드는 폼의 ModelChoiceField 필드로 매핑, 선택 항목들은 queryset 속성으로 지정)
- 모델의 CharField 필드는 폼의 CharField로 매핑됨, 모델의 verbose_name 속성은 폼의 label 속성으로 매핑. max_length 속성도 그대로 매핑
- 모델의 TextField 필드는 폼의 CharField로 매핑, widget 속성을 forms.Textarea로 지정, 모델 정의에서 blank=True 일 경우 폼 필드는 required=False가 됨
- 모델의 ImageField는 폼의 ImageField로 매핑
(3) 모델 폼 정의
- 단순하게 이름을 입력하는 폼 등은 모델과 무관하며 이 경우에는 일반 폼을 사용
- 모델 정의를 기초로 모델의 테이블에 새로운 레코드를 생성하거나 변경을 하려면 모델 폼을 사용
- 폼 필드를 정의하지 않아도 장고가 알아서 폼을 정의해주며, 모델 폼을 만드는 방법은 세 가지가 있음
- ModelForm 클래스 방식
- 장고에서 기본적으로 제공하는 ModelForm 클래스는 모델에 정의한 필드를 참조해서 모델 폼을 만듬
- ModelForm 클래스를 상속 받아 정의하면 되므로 작업이 매우 간단
class PhotoForm(forms.ModelForm):
class Meta:
model = Photo
fields = ['title', 'image', 'description']
- modelform_factory 함수 방식
- modelform_factory 함수를 사용해 위에서 설정한 모델 ㅍ모을 함수를 사용해 정의할 수 있음
- forms.py에 해당 내용을 적용
PhotoForm = modelfrom_factory(Photo, fields='__all__')
modelform_factory(model, form=ModelFrom, fields=None, exclude=None, formfield_callback=None, widgets=None, localized_fields=None, labels=None, help_texts=None, error_messages=None, field_classes=None)
- 제네릭 뷰에서의 폼 정의
- 제네릭 뷰 중 CreateView와 UpdateView는 ModelForm의 기능을 내부에 포함하고 있음
- Meta 클래스를 사용하지 않고 간단하게 model과 fields 속성을 정의하면 됨
- 제네릭 뷰 내부적으로 적절한 모델 폼을 만들고 관련 뷰 처리를 함
class PhotoCreateView(CreateView):
model = Photo
fields = '__all__'
class PhotoUpdateView(UpdateView):
model = Photo
fields = '__all__'
(4) 폼셋 정의
- 폼셋은 폼의 집합이며 일반 폼을 여러 개 묶어서 하나의 폼으로 취급
- 폼셋을 정의할 때 BaseFormSet 클래스를 상속 받아 작성할 수도 있으나 보통은 formset_factory() 함수를 사용
- formset_factory 함수
PostSearchFormSet = formset_factory(PostSearchForm)
forset_factory(form, formset=BaseFormSet, extra=1, can_order=False, can_delete=False, max_num=None, validate_max=False, min_num=None, validate_min=False)
- 폼셋의 관리폼(ManagementForm)
- form-TOTAL_FORMS : 폼의 총 개수 지정
- form-INITIAL_FORMS : 폼의 초기 데이터가 들어 있는 폼의 개수 지정
- form-MAX_NUM_FORMS : 폼셋의 max_num 값을 지정
- form-MIN_NUM_FORMS : 폼셋의 min_num 값을 지정
(5) 모델 폼셋 정의
- 모델 폼과 폼셋의 특징을 둘 다 가지고 있는 폼, 즉 모델 폼을 여러 개 묶은 것
- modelformset_factory() 함수를 사용
PhotoFormSet = modelformset_factory(Photo, fields='__all__')
- 해당 함수는 내부적으로 modelform_factory()와 formset_factory() 함수를 호출
(6) 인라인 폼셋 정의
- 메인 폼에 종속된 폼셋이며 주종 관계는 테이블의 관계가 1:N 관계에서 외래 키로 연결된 경우로부터 비롯 됨
- 메인 폼 : 1:N 관계에서 1 테이블에 대한 폼
- 인라인 폼셋 : N 테이블에 대한 폼
- 보통은 inlineformset_factory() 함수를 사용
(7) 파일 업로드 폼
- 폼을 정의할 때 FileField 또는 ImageField 필드가 있으면 주의가 필요
- 파일 업로드 폼을 다룰 때 다음을 유의해야 함
<form enctype="multipart/form-data" method="post" action="/foo/">
- 폼에 데이터를 바인딩할 때 폼 데이터 뿐만 아니라 파일 데이터도 같이 바인딩해야 함
f = ContactFormWithMugshot(request.POST, request.FILES)
f = ContactFormWithMugshot()
f.is_multipart()
- 템플릿 파일 예제
{% if form.is_multipart %}
<form enctype="multipart/form-data" method="post" action="/foo/">
{% else %}
<form method="post" action="/foo/">
{% csrf_token %}
{% endif %}
{{ form }}
</form>