- [Django] 다양한 템플릿 태그2023년 05월 04일
- 조별하
- 작성자
- 2023.05.04.:01
게시판과 페이징을 구현하며 템플릿 태그를 사용하였다.
✔️ {% csrf_token %}: 서버와 api 통신에 필요한 크로스 사이트 요청 위조를 방지하기 위해 사용되는 태그
✔️ {% if %}{% else %}: 조건을 이용하여 html에서 python 문법 사용 가능
✔️ {% for item in items %}: for 루프를 이용하여 html에서 python 문법 사용 가능🍃 순차적 출력 cycle
✔️ 게시판을 구현하면서 부트스트랩으로 디자인을 입히고, 짝수와 홀수 라인의 백그라운드 색상을 다르게 주었다.
<table class="table table-striped"> <thead> <tr class="table-success"> <th>아이디</th> <th>유저이름</th> <th>이메일</th> <th>가입일</th> <th>페이플랜</th> <th>가격</th> </tr> </thead> <tbody> {% for user in users %} <tr class={% cycle "" "table-dark" %}> <td>{{ user.id }}</td> <td>{{ user.username }}</td> <td>{{ user.email }}</td> <td>{{ user.date_joined }}</td> <td>{{ user.pay_plan.name }}</td> <td>{{ user.pay_plan.price }}</td> </tr> {% endfor%} {% if users|length == 0 %} 조회할 데이터가 없습니다. {% endif %} </tbody> </table>
✔️ 부트스트랩에서는 table 태그에
table-striped
클래스를 주면 아래와 같이 자동으로 짝수 홀수 백그라운드가 구분되며 디자인이 입혀졌다.✔️ 이제 부트스트랩을 이용한 효과를 템플릿 태그로 다시 구현하려고 한다.
<!--<table class="table table-striped">--> <table class="table">
✔️ 테이블 태그에 있는
table-striped
클래스를 제거한다.{% for user in users %} <tr class={% cycle "" "table-dark"%}> <td>{{ user.id }}</td> <td>{{ user.username }}</td> <td>{{ user.email }}</td> <td>{{ user.date_joined }}</td> <td>{{ user.pay_plan.name }}</td> <td>{{ user.pay_plan.price }}</td> </tr> {% endfor%}
✔️ for 루프로 렌더링하는 tr 태그에 {% cycle "" "table-dark" %} 템플릿 태그를 추가하여 첫 번째는 "" 빈 클래스가 두 번째는
table-dark
클래스가 번갈아가면서 나오게 한다.✔️ 위의 코드를 변경 후 화면을 띄우면 아래와 같이 부트스트랩과 동일한 효과의 디자인이 입혀진 것을 확인 할 수 있다.(사실상 부트스트랩과 템플릿 태그의 조합이다.)
🍃 template의 공통 영역 분리
<!DOCTYPE html> <html lang="ko"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <!-- Bootstrap CSS --> <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-EVSTQN3/azprG1Anm3QDgpJLIm9Nao0Yz1ztcQTwFspd3yD65VohhpuuCOmLASjC" crossorigin="anonymous"> <title>Index Page</title> </head> <!-- Option 1: Bootstrap Bundle with Popper --> <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/js/bootstrap.bundle.min.js" integrity="sha384-MrcW6ZMFYlzcLA8Nl+NtUVF0sA7MsXsP1UyJoMp4YLEuNSfAP+JcXn/tWtIaxVXM" crossorigin="anonymous"></script> <body>
✔️ 대부분의 template에 상위의 header와 하위의 footer 등의 공통된 부분을 .html파일마다 중복으로 들어가는 경우가 있다.
✔️ 이렇게 중복되는 부분을 템플릿 태그 {% extendx %} 와 {% block %}{% endblock %} 를 이용하여 분리해보자.
➤ index.html
<!DOCTYPE html> <html lang="ko"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <!-- Bootstrap CSS --> <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-EVSTQN3/azprG1Anm3QDgpJLIm9Nao0Yz1ztcQTwFspd3yD65VohhpuuCOmLASjC" crossorigin="anonymous"> <title>Index Page</title> </head> <!-- Option 1: Bootstrap Bundle with Popper --> <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/js/bootstrap.bundle.min.js" integrity="sha384-MrcW6ZMFYlzcLA8Nl+NtUVF0sA7MsXsP1UyJoMp4YLEuNSfAP+JcXn/tWtIaxVXM" crossorigin="anonymous"></script> <body> <h1>The is a Index Page</h1> <h3>{{ user }}</h3> {% if user.is_authenticated %} <a class="btn btn-primary" role="button" aria-disabled="true" href="/logout">로그아웃</a> <a class="btn btn-primary" role="button" aria-disabled="true" href="/user/list">유저목록</a> <a class="btn btn-primary" role="button" aria-disabled="true" href="/payplan/list">PayPlan목록</a> {% else %} <a class="btn btn-primary" role="button" aria-disabled="true" href="register">회원가입</a> <a class="btn btn-primary" role="button" aria-disabled="true" href="login">로그인</a> {% endif %} <br><br> {% block content %} {% endblock %} </body> </html>
✔️ 위의 .html파일은 게시판마다 공통으로 불러와야하는 부분이다.
✔️ 공통으로 들어가는 부분은 그대로 두고 변경되는 다른 template이 들어가는 부분에 아래의 코드를 추가해 준다.
{% block content %} {% endblock %}
➤ boards.html
{% extends "index.html" %} {% block content %} <h2>Pay Plan 리스트</h2> <h4>{% if msg %}{{msg}}{% endif %}</h4> <!-- <table class="table table-striped"> --> <table class="table"> <thead> <tr class="table-primary"> <th>아이디</th> <th>이름</th> <th>가격</th> <th>생성일</th> </tr> </thead> <tbody> {% for plan in pay_plans %} <!-- 부트스트랩 table-striped 대신 cycle 템플릿 태그 이용 --> <tr class={% cycle "" "table-dark" %}> <td>{{ plan.id }}</td> <td>{{ plan.name }}</td> <td>{{ plan.price }}</td> <td>{{ plan.create_at }}</td> </tr> {% endfor%} {% if pay_plans|length == 0 %} 조회할 데이터가 없습니다. {% endif %} </tbody> </table> ... {% endblock %}
✔️ 이제 게시판 .html에 들어가서 위와 같이 index.html에 중복으로 들어가있는 코드는 지워 준 후, 최 상단과 최 하단에 아래와 같은 코드를 붙여준다.
{% extends "index.html" %}
{% block content %}{% endblock %}✔️ 두 개의 다른 게시판에서 index.html 부분이 공통으로 나오며 boards.html은 서로 다른 소스가 렌더링되는 것을 확인하였다.
✔️ 이렇게 템플릿 태그를 사용하면 소스코드를 절약할 수 있고, 템플릿 구조를 역할 별로 구조화 시킬 수 있다.
🍃 다른 템플릿 추가 include
✔️ 특정 .html 파일을 생성 후 이 파일의 내용을 다른 template에 불러와서 사용할 수 있다.
➤ include.html
<p>이 페이지는 인클루드 되었습니다.</p>
➤ boards.html
... </nav> {% include "include.html" %} <p> <a class="btn btn-primary" role="button" aria-disabled="true" href="{% url 'index' %}">홈으로</a> <a class="btn btn-primary" role="button" aria-disabled="true" href="{% url 'register' %}">회원가입</a> </p> ...
✔️ 위와 같이 특정 템플릿을 만든 후 그 템플릿을 불러오고 싶은 template에 아래와 같은 코드를 추가 하면 특정 템플릿의 코드를 불러와서 사용할 수 있다.
{% include "include.html" %}
✔️ 위의 템플릿 태그는 공통된 것이 아닌 팝업이나 특정 html을 불러와서 보여줄 때 사용할 수 있다.
🍃 템플릿 필터
1. 사용자 정의 템플릿 태그
✔️ 지금까지는 Django에서 제공되는 템플릿 태그를 사용해보았다.
✔️ 하지만 웹 개발에 있어 Django에서 제공되는 태그만 사용해서 모든 것을 다 구현할 수 없기 때문에, 사용자 정의로 만든 태그가 필요한 경우가 있을 것이다.
✔️ 현재 출력된 데이터는 유저명, 이메일, 가입일, 페이플랜, 가격 등의 필드 데이터 이다.
✔️ 이메일은 @가 붙은 이메일 형식의 필드이지만 제 3자가 보았을 때 스팸메일을 보내거나 악용하는 경우가 있을 수 있으며,
✔️ 가입일은 서버에서 등록되는 데이터이기 때문에 년/월/일 뿐 아니라 시간까지 들어 있다.
✔️ 또한 가격은 정수형으로 데이터가 저장되어 있기 때문에 천자리 수가 구분이 되지 않아✔️ 위와 같은 정제되지 않는 데이터를 그대로 보여주게 되면 사용자는 가독성이 떨어질 수 있으므로 내가 정의한 형식으로 데이터에 필터를 걸 수 있게 템플릿 태그를 만들어 보자.
2. 템플릿 태그 생성과 필터 적용
✔️ 프로젝트 App안에 templatetags 폴더를 생성 후, 내부에
__init__.py
,custom_tags.py
파일을 만들어 주자.✔️ 위에서 언급한 것과 같이 이메일, 가입일, 가격 3개의 필드를 커스텀 필터를 걸어 보겠다.
📌 이메일
templatetags > custom_tags.pyfrom django import template register = template.Library() @register.filter(name="email_ma") def email_masker(value): email_split = value.split("@") return f"{email_split[0]}@******.***"
✔️ django의 template모듈 사용을 위해 template.Library()
✔️ "email_ma"라는 명칭으로 filter를 적용
- email_masker 함수명으로 필터를 적용시키지 않기위한 설정
✔️ 이메일 형식의 @ 이후 부분을 split함수로 잘라내어 뒷부분은 masker 처리user > boards.html
{% load custom_tags %} {% for user in users %} <tr class={% cycle "" "table-dark" %}> <td>{{ user.id }}</td> <td>{{ user.username }}</td> <td>{{ user.email|email_ma }}</td> <td>{{ user.date_joined }}</td> <td>{{ user.pay_plan.name }}</td> <td>{{ user.pay_plan.price }}</td> </tr> {% endfor%}
✔️ "|"를 붙여 내가 정의한 태그 명칭을 뒷 부분에 붙여서 아래와 같이 수정하면 클라이언트 렌더링 시 email은 내가 정의한 함수를 거쳐 필터링 된다.
{{ user.email|email_ma }}
📌 가격
templatetags > custom_tags.pyfrom django import template from django.utils.safestring import mark_safe register = template.Library() @register.filter(name="price_comma") def price_comma(value): return format(value, ',d')
✔️ 숫자 천자리 수 마다 ","가 붙게 데이터를 정제하기 위해 함수 정의
✔️ format() 함수를 사용하여 첫 번째 인자는 값, 두 번째 인자는 ',d/f'
- d또는 f 중 하나만 넣어주면 되는데 d 는 정수 f 실수를 의미한다.user > boards.html
{% load custom_tags %} {% for user in users %} <tr class={% cycle "" "table-dark" %}> <td>{{ user.id }}</td> <td>{{ user.username }}</td> <td>{{ user.email|email_ma }}</td> <td>{{ user.date_joined }}</td> <td>{{ user.pay_plan.name }}</td> <td>{{ user.pay_plan.price|price_comma }}</td> </tr> {% endfor%}
✔️ 이메일과 동일하게 아래와 같이 수정
{{ user.pay_plan.price|price_comma }}
📌 가입일
✔️ 가입일의 경우는 내가 정의한 것이 아닌 제공되는 필터를 사용하여 날짜에 대한 데이터를 정제해 보겠다.user > boards.html
... {{ user.date_joined|date:"Y M d D" }} ...
✔️ 위에서 사용한 "date" 태그는 따로 정의한 태그가 아니지만 기본적으로 django에서 제공하기 때문에 사용이 가능한다.
✔️ "Y"는 년도, "M"은 월, "d"는 일자, "D"는 요일을 의미한다.
✔️ 상세한 date에 대한 조건은 공식문서에 다양하게 나와 있으니 확인 바란다.
참고 : Django 공식 문서
2. 기타 필터 적용 방법
✔️ 필터를 적용할 때, 인자를 추가로 보내 데이터 처리에 사용할 수 있다.
📌 인자 추가
user > boards.html{% load custom_tags %} ... <td>{{ user.email|email_ma:user.id }}</td> ...
✔️ 위와 같이 정의한 "email_ma"태그 뒤에
:[보내고 싶은 인자]
를 붙여 준다.templatetags > custom_tags.py
from django import template register = template.Library() @register.filter(name="email_ma") def email_masker(value, arg): email_split = value.split("@") return f"{email_split[0]}@******.***" if arg % 2 == 0 else value
✔️ 위에서 작성한 코드를 조금 수정하였는데, 매개변수 arg를 추가 한 후, 클라이언트에서 전달 받은 user.id값이 짝수, 홀수에 따라 이메일 뒤의 masker처리를 주었다가 안주게 코드를 변경해 보았다.
✔️ 결과는 아래와 같이 나온다.
3. filter vs simple_tag
✔️ 위에서 태그의 명칭을 정할 때 "filter"를 사용하여 아래와 같이 사용하였다.
from django import template register = template.Library() @register.filter(name="email_ma") def email_masker(value, arg): ...
✔️ 위의 filter와 동일한 기능을 하는 simple_tag도 사용할 수 있는데 두개의 차이점이 존재한다.
✔️ filter를 사용하면 필터링할 값과 추가 인자를 1개 받을 수 있다.
✔️ 하지만 simple_tag를 사용하면 인자의 개수에 제한 없이 넘겨 줄 수 있다.
from django import template from django.utils.safestring import mark_safe register = template.Library() # takes_context를 True로 설정하지 않으면 메소드에서 context를 사용할 수 없음 @register.simple_tag(name="test_tags", takes_context=True) def test_tags(context): tag_html = "<span class='badge badge-primary'>테스트 태그</span>" # 클라이언트로 전달 시 스트링만 보내게 되면 html 태그로 인식하지 못함 return mark_safe(tag_html)
✔️ 위는 simple_tag를 사용해보기 위해 만든 함수이며 filter와 동일하게 사용자 정의 템플릿 태그를 만들 수 있다.
✔️ 코드는 필터를 거는 것 뿐 아니라 html를 전달하여 렌더링 할 수 있는지 확인하는 내용이다.
🍃 참고
https://itinerant.tistory.com/161
'LANGUAGES > Django' 카테고리의 다른 글
[Django] django-crontab 스케줄러 작성 (0) 2023.05.22 [Docker] docker-compose를 이용한 프로젝트 배포 (0) 2023.05.08 [Django] django-seed를 이용한 데이터 만들기 (0) 2023.05.03 [Django] Django 커스텀 커맨드 만들기 (0) 2023.05.02 [Django] debug_toolbar 사용하기 (0) 2023.04.06 다음글이전글이전 글이 없습니다.댓글