달나라 노트

Python django project 1 - 게시판 만들기 ch.12 : 게시글 내용 보기 본문

Python django/Python django project 1

Python django project 1 - 게시판 만들기 ch.12 : 게시글 내용 보기

CosmosProject 2020. 12. 21. 01:51
728x90
반응형

 

 

이번엔 게시글 내용을 볼 수 있는 기능을 만들어보겠습니다.

 

마찬가지로 MTV 순서대로 코딩하겠습니다.

Model은 이미 post app model이 있으니 패스.

 

Template을 봅시다.

pro/app/post/templates에 post_detail.html 파일을 만들고 아래처럼 적어둡시다.

{% extends 'base.html' %}

{% block body %}
<div class="row mt-5">
    <div class="col-12 text-center">
        <h1>Post Detail</h1>
    </div>
</div>

<div class="row mt-5">
    <div class="col-12">
        <ul class="list-group">
            <li class="list-group-item">
                <b>Title</b><br/>
                
            </li>
            <li class="list-group-item">
                <b>Contents</b><br/>
                
            </li>
            <li class="list-group-item">
                <b>Writer</b><br/>
                
            </li>
            <li class="list-group-item">
                <b>Registered at</b>: 
            </li>
        </ul>
    </div>
</div>

<div class="row mt-5">
    <div class="col-6">
        <button type="button" class="btn btn-primary btn-block" onclick="location.href='/post/post_list/'">Post List</button>
    </div>
    <div class="col-6">
        <button type="button" class="btn btn-primary btn-block" onclick="location.href='/post/post_register/'">Post Register</button>
    </div>
</div>
{% endblock %}

상세 내용은 어떤 게시글의 상세내용을 볼 것인지가 필요하겠죠.

따라서 views.py로부터 내가 보고싶은 post의 model objects를 template에 전달하여 표시해주는 방식을 사용할 것입니다.

따라서 일단 위처럼 대체적인 틀만 만들어두고 views.py를 작성한 후 다시 template을 보완해봅시다.

 

 

 

 

 

pro/app/post/views.py에 view_post_detail 함수를 아래처럼 추가합시다.

def view_post_detail(request, post_key):
    login_userkey = request.session.get('login_userkey', None)

    if not login_userkey:
        return redirect('/user/user_login/')
    else:
        try:
            modelpost = postapp_model.ModelPost.objects.get(pk=post_key)
        except postapp_model.ModelPost.DoesNotExist:
            raise Http404('The post cannot be found.')
        else:
            dict_render = {
                'post_detail': modelpost
            }

            return render(request, 'post_detail.html', dict_render)

뭔가 이상한 점을 발견하셨나요?

지금까지 만들었던 view 함수는 인자로서 request만 있었는데 이번에는 post_key라는 인자가 추가되었습니다.

 

이것을 위해선 먼저 post/urls.py에 위 함수를 어떻게 연결하였는지를 봐야합니다.

from django.contrib import admin
from django.urls import path, include
from . import views as postapp_view

urlpatterns = [
    path('post_list/', postapp_view.view_post_list),
    path('post_register/', postapp_view.view_post_register),
    path('post_detail/<int:post_key>', postapp_view.view_post_detail)
]

post app의 urls.py를 보면 위처럼 post_detail이라는 url이 입력되었을 때 view_post_detail 함수를 실행하도록 연결해놨습니다.

근데 문제는 post_detatil 뒤에 <int:post_key>라는 것이 붙었죠.

 

일단 우리는 어떤 게시글의 상세설명을 보여줄 때 가장 먼저 보여줄 게시글이 어떤 게시글인지를 파악하는 것입니다.

따라서 저는 이를 url에 전달하기로 정했습니다.

예를들어 /post/post_detail/3 과 같은 url이 요청되면 primary key가 3인 게시글을 띄우라는 뜻입니다.

여기서 3이라는 숫자를 받기 위해 url에 <int:post_key>라는 부분을 쓴 것입니다.

int는 정수(integer)를 의미하고 이 정수를 post_key라는 변수에 저장하여 view_post_detail이라는 함수가 실행될 때 이 함수의 인자 중 하나로서 post_key 정보를 전달한다는 것이죠.

 

 

 

def view_post_detail(request, post_key):
    login_userkey = request.session.get('login_userkey', None)

    if not login_userkey:
        return redirect('/user/user_login/')
    else:
        try:
            modelpost = postapp_model.ModelPost.objects.get(pk=post_key)
        except postapp_model.ModelPost.DoesNotExist:
            raise Http404('The post cannot be found.')
        else:
            dict_render = {
                'post_detail': modelpost
            }

            return render(request, 'post_detail.html', dict_render)

다시 post app view의 view_post_detail 함수를 봅시다.

원래 기본적으로 제시되었던 request 인자와 함께 post_key가 전달된 것을 알 수 있는데 이제 이것의 의미가 뭔지 아시겠죠?

 

 

 

 

 

def view_post_detail(request, post_key):
    login_userkey = request.session.get('login_userkey', None)

    if not login_userkey:
        return redirect('/user/user_login/')
    else:
        ...

마찬가지로 login 상태가 아니면 게시글을 보지 못하게 하고 login 화면으로 redirect하게 해줍니다.

 

 

 

 

def view_post_detail(request, post_key):
    ...
    else:
        try:
            modelpost = postapp_model.ModelPost.objects.get(pk=post_key)
        except postapp_model.ModelPost.DoesNotExist:
            raise Http404('The post cannot be found.')
        else:
            dict_render = {
                'post_detail': modelpost
            }

            return render(request, 'post_detail.html', dict_render)

자 이제 전달된 post_key에 해당하는 post model을 불러오면 되겠죠.

하지만 그냥 불러오면 안됩니다.

만약 게시글이 10개밖에없다고 가정합시다. 그러면 각 게시글의 primary key는 1부터 10까지 존재하겠죠.

근데 만약 사용자가 url을 /post/post_detail/120 과 같이 요청했다고 가정해봅시다.

그럼 primary key가 120인 게시글을 띄워줘야하는데 post app model에는 120번 게시글 정보가 없습니다.

 

이렇게 게시글이 존재하는지 아닌지를 판단하기 위해서 try excep구문을 사용합시다.

만약 게시글이 존재하지 않는 경우 Http404 error를 raise하여 해당 게시글이 찾아지지 않는다는 에러메세지를 띄워줍니다.

또한 게시글이 있으면 해당 게시글 정보를 dictionary의 형태로 render함수를 통해 template에 전달하게 되죠.

 

 

 

 

그리고 다시 post_detail.html에서 표시할 값들을 채워줍시다.

{% extends 'base.html' %}

{% block body %}
<div class="row mt-5">
    <div class="col-12 text-center">
        <h1>Post Detail</h1>
    </div>
</div>

<div class="row mt-5">
    <div class="col-12">
        <ul class="list-group">
            <li class="list-group-item">
                <b>Title</b><br/>
                {{ post_detail.post_title }}
            </li>
            <li class="list-group-item">
                <b>Contents</b><br/>
                {{ post_detail.post_contents }}
            </li>
            <li class="list-group-item">
                <b>Writer</b><br/>
                {{ post_detail.post_writer }}
            </li>
            <li class="list-group-item">
                <b>Registered at</b>: {{ post_detail.registered_dttm }}
            </li>
        </ul>
    </div>
</div>

<div class="row mt-5">
    <div class="col-6">
        <button type="button" class="btn btn-primary btn-block" onclick="location.href='/post/post_list/'">Post List</button>
    </div>
    <div class="col-6">
        <button type="button" class="btn btn-primary btn-block" onclick="location.href='/post/post_register/'">Post Register</button>
    </div>
</div>
{% endblock %}

view_post_detail 함수의 render 로부터 전달된 post_detail이라는 값은 post app model로부터 가져온 데이터를 담고있습니다.

따라서 post app model에 정의된 field이름을 통하여 각 field의 값을 참조할 수 있습니다.

 

 

 

 

 

 

자 여기까지하면 detail 기능은 다 만들었죠.

근데 보통 게시글을 볼 때 여러분이 url창에 직접 url을 입력해서 들어가나요?

그렇지않죠? 게시글 list에서 게시글을 클릭하면 해당 게시글의 detail로 이동이되죠.

이 기능도 구현하기 위해 post_list.html의 post_title 부분을 a태그를 이용하여 변경해줍시다.

{% extends 'base.html' %}

{% block body %}
<div class="row mt-5">
    <div class="col-12 text-center">
        <h1>Post list</h1>
    </div>
</div>



<div class="row mt-5">
    <div class="col-12">
        <table class="table table-light">
            <thead class="thead-light">
                <tr>
                    <th>Post key</th>
                    <th>Title</th>
                    <th>Writer</th>
                    <th>Registered at</th>
                </tr>
            </thead>
            <tbody class="text-dark">
                {% for field in show_page %}
                <tr>
                    <th>{{ field.id }}</th>
                    <th><a href="/post/post_detail/{{ field.id }}" style="color: skyblue">{{ field.post_title }}</a></th>
                    <td>{{ field.post_writer }}</td>
                    <td>{{ field.registered_dttm }}</td>
                </tr>
                {% endfor %}
            </tbody>
        </table>
    </div>
</div>

<div class="row mt-5">
     <div class="col-12">
         <nav class="pagination justify-content-center">

             {% if show_page.has_previous %}
             <li class="page-item">
                 <a class="page-link" href="?p={{ show_page.previous_page_number }}">Previous</a>
             </li>
             {% else %}
             <li class="page-item disabled">
                 <a class="page-link" href="">Previous</a>
             </li>
             {% endif %}

             <li class="page-item active">
                 <a class="page-link" href="">{{ show_page.number }} / {{ show_page.paginator.num_pages }}</a>
             </li>

             {% if show_page.has_next %}
             <li class="page-item">
                 <a class="page-link" href="?p={{ show_page.next_page_number }}">Next</a>
             </li>
             {% else %}
             <li class="page-item disabled">
                 <a class="page-link" href="">Next</a>
             </li>
             {% endif %}

         </nav>
     </div>
</div>


<div class="row mt-5">
    <div class="col-6">
        <button type="button" class="btn btn-primary btn-block" onclick="location.href='/'">Home</button>
    </div>
    <div class="col-6">
        <button type="button" class="btn btn-primary btn-block" onclick="location.href='/post/post_register/'">Post Register</button>
    </div>
</div>

{% endblock %}

 

변경된 부분은 아래 부분인데 아까 설명했던 post/post_detail/정수의 형태로 되도록 a 태그의 href 속성을 설정하였으며,

field.id란 값은 해당 게시글의 primary key를 의미하죠.

<th><a href="/post/post_detail/{{ field.id }}" style="color: skyblue">{{ field.post_title }}</a></th>

- tip

a태그의 글자 색을 skyblue로 설정해주었습니다. 이 부분은 사실 원하는 색으로 설정해도 됩니다.

 

 

 

 

 

 

여기까지 하고 post list화면으로 이동해봅시다.

그러면 각 게시글의 제목이 하늘색 글씨로 바뀌고, 클릭이 가능하도록 변한 것을 볼 수 있죠.

 

 

 

 

 

8번 게시글의 제목을 클릭하면

위처럼 게시글의 title, contents, writer, registered at 정보가 다 보이는 것을 알 수 있습니다.

 

또한 이때의 url을 보면 http://127.0.0.1:8000/post/post_detail/8 이렇게 되어있죠.

8번 게시글이라는 뜻입니다.

 

 

만약 저 8이란 숫자를 지우고 http://127.0.0.1:8000/post/post_detail/120 이와같은 주소를 입력해봅시다.

 

그러면 위 화면처럼 Page not found error(404 error)가 뜨면서 The post cannot be found.라는 제가 설정했던 에러메세지가 뜨는 것을 알 수 있죠.

 

 

 

728x90
반응형
Comments