본문 바로가기
python/Django

3. 게시판 만들기 - Django 게시판 목록, 읽기, 쓰기 페이지 구성

by 맑은안개 2021. 2. 4.

1, 2편에 이어 본 블로그에서는 게시판 다운 화면을 구성하기 위해 html( view)페이지를 꾸며본다. ( 꾸민다고 했지만 이쁘진 않다 ^^; ) 진행하기 전에 전 블로그에서 다룬 model이 구성되어 있어야 원활한 진행이 가능하다.

 

2021/02/01 - [python/Django] - 1. 게시판 만들기 - Django 3.x 설치 및 핵심개념 파악

2021/02/01 - [python/Django] - 2. 게시판 만들기 - Django + mariaDB 연동( 접속부터 모델생성까지 - migration )

2021/02/04 - [python/Django] - 4. 게시판 만들기 - Django 게시판 페이징(Pagination) 처리( feat. GIF )

 

 

결과물은 다음과 같다. 페이지는 3개 페이지로 구성(index, detail, write)되며 detail 페이지에서 댓글을 달 수 있는 기능이 있다. 페이징 기능은 다음편에서 다루도록 한다. 

게시판 목록, 읽기, 쓰기 화면

board/views.py

from django.http import HttpResponseRedirect
from django.shortcuts import render
from django.utils import timezone
from django.urls import reverse

from .models import Board

def index(request):
    all_boards = Board.objects.all().order_by("-pub_date") # 모든 데이터 조회, 내림차순(-표시) 조회
    return render(request, 'board/index.html', {'title':'Board List', 'board_list':all_boards})

def detail(request, board_id):
    board = Board.objects.get(id=board_id)
    return render(request, 'board/detail.html', {'board': board})

def write(request):
    return render(request, 'board/write.html')

def write_board(request):
    b = Board(title=request.POST['title'], content=request.POST['detail'], author="choi", pub_date=timezone.now())
    b.save()
    return HttpResponseRedirect(reverse('board:index'))

def create_reply(request, board_id):
    b = Board.objects.get(id = board_id)
    b.reply_set.create(comment=request.POST['comment'], rep_date=timezone.now())
    return HttpResponseRedirect(reverse('board:detail', args=(board_id,)))    
  • index: 게시글 목록

  • detail: 게시글 제목 선택 시 상세 페이지 이동

  • write: 게시글 목록에서 "글쓰기" 버튼 클릭 시 쓰기 페이지 이동

  • write_board: 쓰기 페이지에서 새로운 글을 등록 시에 submit 처리 

  • create_reply: 상세 페이지에서 댓글 등록 시에 submit 처리

  • POST 데이터 전송(저장) 후 HttpResponseRedirect 를 사용하여 사용자가 뒤로 가기를 했을때 두번 POST 전송 처리 되지 않게 한다. 

 

board/urls.py

from django.urls import path

from . import views

app_name = 'board'
urlpatterns = [
    path('', views.index, name='index'),
    path('<int:board_id>/', views.detail, name='detail'),
    path('write/', views.write, name='write'),
    path('write/write_board', views.write_board, name='write_board'),
    path('<int:board_id>/create_reply', views.create_reply, name='create_reply'),
]

app_name은 아래와 같이 템플릿에서 네임스페이스 접근을 위해 사용된다.

<a href="{% url 'board:detail' data.id %}">
	{{ data.title }}
</a>

 

board/templates/board/index.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<style type="text/css">
        thead {
            background-color:#cecece;
        }

        .content {
            max-width:500px;
            margin: auto;
            padding:10px;
        }

        .header {
            text-align: center;
        }

        .board {
            width: 100%
        }

        .even_bgcolor {
            background-color:#e8efff;
        }
    </style>
<body>
    <div class="content">
        <div class="header">
            <h2>{{ title }}</h2>
        </div>
        <div style="text-align:right">
            <a href="{% url 'board:write' %}">글쓰기</a>
        </div>

        <table class="board">
            <thead>
                <tr class="header">
                    <th style="width:10%;">순번</th>
                    <th style="width:60%">제목</th>
                    <th style="width:30%">작성일자</th>
                </tr>
            </thead>
            <tbody>
            {% if board_list %}
                {% for data in board_list %}
                <tr class="{% if forloop.counter|divisibleby:2 %}even_bgcolor{% endif %}">
                    <td style="text-align:center;">{{ forloop.counter }}</td>
                    <td>
                        <a href="{% url 'board:detail' data.id %}">
                            {{ data.title }}
                        </a>
                    </td>
                    <td>{{ data.pub_date|date:'Y-m-d' }}</td>
                </tr>
                {% endfor %}
            {% else %}
                <tr>
                    <td>작성된 게시글이 없습니다.</td>
                </tr>
            {% endif %}
            </tbody>
        </table>
    </div>
</body>
</html>
  • {{ data.pub_date|date:'Y-m-d' }} 에서 date는 Django Filter 기능으로 datetime형태의 pub_date를 포맷변환한다. 더 자세한 내용은 여기를 참조한다. 

  • 짝수 행에 대해 even_bgcolor style을 입혀준다.  cycle을 사용하여 for를 사용한 반복문에 홀수, 짝수를 구할 수 있다.

<tr class="{% cycle '' 'even_bgcolor' %}">

 

board/templates/board/detail.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<style type="text/css">
        .content {
            max-width:500px;
            margin: auto;
            padding:10px;
        }

		.reply_content {
			text-align:right	
		}

		.reply_content .reply{
			border-radius: 0.5em;
			display: block;
			margin: 4px;
			padding: 3px 10px;
		}

		.board_for_read {
			width: 100%;
			background-color:#f1f1f1;
		}

		#title {
			height: 32px;
			width: 100%;
			border-radius: 0.5em;
			border: none;
			font-family: inherit;
			font-size: inherit;
			background: none;
		}

        textarea {
			width: 100%;
            font-family: inherit;
            font-size: 100%;
			resize: none;
			border-radius: 0.5em;
			padding-block: 10px;
			border: 0px;
			background: none;
        }

		.td_title {
			background-color: #d8d8d8
		}
    </style>
<body>
    <div class="content">

		<table class="board_for_read">
			<tbody>
				<tr>
					<td class="td_title" style="text-align:center; width:20%;">제목</td>
					<td style="padding:10px;">
						<input type="text" name="title" id="title" value="{{ board.title }}" readonly/>
					</td>
				</tr>
				<tr>
					<td class="td_title" style="text-align:center; width:20%;">작성일자</td>
					<td style="padding:10px;">
						<span>{{ board.pub_date }}</span>
					</td>
				</tr>
				<tr>
					<td class="td_title" style="text-align:center;">내용</td>
					<td style="padding:10px;">
						<textarea name="detail" id="detail" rows="10" readonly>{{ board.content }}</textarea>
					</td>
				</tr>
			</tbody>
		</table>
		<div style="text-align:left;">
			<a href="{% url 'board:index' %}">> Home</a>
		</div>

		<form action="{% url 'board:create_reply' board.id %}" method="post">
		{% csrf_token %}
		<table class="board_for_read">
			<tbody>
				<tr>
					<td colspan="2" class="td_title" style="text-align:left; padding-left:10px; height:30px;">leave a comment</td>
				</tr>
				<tr>
					<td style="text-align:center;">
						<input type="text" name="comment" id="comment" style="width:100%">
					</td>
					<td><input type="submit" value="save"></td>
				</tr>
			</tbody>
		</table>
		</form>

		<div class="reply_content">
			{% for rep in board.reply_set.all %}
				<span class="reply">{{ rep.comment }} [{{ rep.rep_date|date:'Y-m-d H:i'}}]</span>
			{% endfor %}
		</div>
    </div>
</body>
</html>
  • POST 형식으로 데이터를 전송하기 위해선  데이터 위조방지를 위해 form 태그 내 {% csrf_token %} 이 필요하다.

 

board/templates/board/write.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<style type="text/css">
        .content {
            max-width:500px;
            margin: auto;
            padding:10px;
        }

		.board_for_read {
			width: 100%;
			background-color:#f1f1f1;
		}

		#title {
			height: 32px;
			width: 100%;
			border-radius: 0.5em;
			border: none;
			font-family: inherit;
			font-size: inherit;
		}

        textarea {
			width: 100%;
            font-family: inherit;
            font-size: 100%;
			resize: none;
			border-radius: 0.5em;
			padding-block: 10px;
			border: 0px;
        }

		.td_title {
			background-color: #d8d8d8
		}
    </style>
<body>
    <div class="content">
		<form action="{% url 'board:write_board' %}" method="post">
		{% csrf_token %}

		<div style="text-align:right;padding: 5px;">
			<input type="submit" value="저장">
		</div>

		<table class="board_for_read">
			<tbody>
				<tr>
					<td class="td_title" style="text-align:center; width:20%;">제목</td>
					<td style="padding:10px;">
						<input type="text" name="title" id="title" value="{{ board.title }}"/>
					</td>
				</tr>
				<tr>
					<td class="td_title" style="text-align:center;">내용</td>
					<td style="padding:10px;">
						<textarea name="detail" id="detail" rows="10">{{ board.content }}</textarea>
					</td>
				</tr>
			</tbody>
		</table>
		</form>

		<div style="text-align:left;">
			<a href="{% url 'board:index' %}">> Home</a>
		</div>

    </div>
</body>
</html>

 

 

마치며..

  화면의 기능이 추가될 수록 Django templates에 더 깊은 이해가 필요한것 같다. 여기선 정말 간단한 기능의 게시판만 적용하였으나 여유가 된다면 비밀번호 기능을 추가한다거나 파일첨부 기능을 추가해 보는 것도 좋을것 같다. 다음 편에선 페이징 기능을 적용해보도록 한다. 

 

2021/02/04 - [python/Django] - 4. 게시판 만들기 - Django 게시판 페이징(Pagination) 처리( feat. GIF )

 

4. 게시판 만들기 - Django 게시판 페이징(Pagination) 처리( feat. GIF )

전 편에 이어 게시판 필수 항목인 페이징 처리를 해보도록 한다. Django는 Paginator를 built-in module로 제공하는데 이를 사용하여 페이징 처리를 쉽게 구현할 수 있다. 1. 게시판 만들기 - Django 3.x 설치

youngwonhan-family.tistory.com

 

반응형