프로그램 어플리케이션 확장하기

우리는 지금까지 웹사이트 제작에 필요한 모든 단계들을 마쳤어요. 모델, url, 뷰와 템플릿을 만드는지 알게 되었고요. 또 웹사이트를 어떻게 멋지게 꾸미는지 알게 되었어요.

이제 연습해 봅시다!

블로그에 가장 필요한 것은 페이지에 포스트가 보이게 만드는 것이겠죠?

이미 앞에서 Post 모델을 만들었으니 models.py에 추가할 내용은 없습니다.

Post에 템플릿 링크 만들기

blog/templates/blog/post_list.html 파일에 링크를 추가하는 것부터 시작합시다. 아마 아래와 같을 거에요. :

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

{% block content %}
    {% for post in posts %}
        <div class="post">
            <div class="date">
                {{ post.published_date }}
            </div>
            <h1><a href="">{{ post.title }}</a></h1>
            <p>{{ post.text|linebreaks }}</p>
        </div>
    {% endfor %}
{% endblock content %}

우리는 post 목록에 있는 제목에서 post의 내용 페이지로 가는 링크를 만들 거에요. <h1><a href="">{{ post.title }}</a></h1> 를 변경해 봅시다. post의 상세 페이지는 로 연결됩니다.

<h1><a href="{% url 'post_detail' pk=post.pk %}">{{ post.title }}</a></h1>

{% url 'post_detail' pk=post.pk %}에 대해 설명할 때가 왔군요! 예상하셨겠지만 {% %} 표기는 장고 템플릿 태그를 사용하고 있는 것을 말합니다. 이번에는 우리를 위한 URL를 만들어 사용해 봅시다!

blog.views.post_detail는 우리가 만들려고 하는 경로인 post_detail view 입니다. 반드시 주의하세요 : blog는 우리의 응용 프로그램 (디렉토리 blog)의 이름이에요. viewsviews.py 파일의 이름에서 나온 것이에요. 마지막인 post_detailview 의 이름입니다.

http://127.0.0.1:8000/를 열어보세요. 오류 메세지가 나올 거에요. (예상대로, 아직 post_detail을 위한 view 파일 만들지 않아 오류가 나는 것이죠.) 아마 이렇게 나왔을 거에요. :

NoReverseMatch error

Post 상세 페이지에 URL 만들기

urls.py 파일에 post_detail view 를 위한 URL를 만들어 봅시다!

첫 번째 게시물의 상세 URL 은 이렇게 나올 거에요. : http://127.0.0.1:8000/post/1/

blog/urls.py파일에 URL을 만들어, 장고가 post_detail이란 view 로 보내, 전체 블로그 글이 보일 수 있게 만들어 봅시다. url(r'^post/(?P<pk>[0-9]+)/$', views.post_detail, name='post_detail') 코드를 blog/urls.py 파일에 추가하세요. 그러면 아래처럼 보일 거에요. :

from django.conf.urls import include, url
from . import views

urlpatterns = [
    url(r'^$', views.post_list, name='post_list'),
    url(r'^post/(?P<pk>[0-9]+)/$', views.post_detail, name='post_detail'),
]

^post/(?P<pk>[0-9]+)/$ 이 부분이 무섭게 보이지만, 걱정하지 마세요. 하나씩 차근차근 설명해드릴 거에요.

  • ^은 "시작"을 뜻합니다.
  • 첫 부분 다음부터 나오는 post/는 URL이 post 를 포함해야한다는 것을 뜻합니다. 아직까지 할 만 하죠?
  • (?P<pk>[0-9]+) - 이 부분은 좀 까다롭습니다. 이 정규 표현식의 의미는 장고가 여러분이 여기에 넣은 모든 것을 pk변수에 넣어 뷰로 전송하겠다는 뜻입니다. [0-9]은 문자를 제외하고, 숫자 0부터 9까지 하나의 숫자만 있다는 뜻입니다. +는 하나 또는 그 이상의 숫자가 와야한다는 것을 의미합니다. 따라서 http://127.0.0.1:8000/post//라고 하면 post/ 다음에 숫자가 없기 때문에 해당이 안되지만, http://127.0.0.1:8000/post/1234567890/는 완벽하게 매칭됩니다.
  • / - 다음에 / 가 한번 더 와야 한다는 의미입니다.
  • $ - "마지막"! 입니다. 그 뒤로 더이상 문자가 오면 안됩니다.

브라우저에 http://127.0.0.1:8000/post/5/입력하면, 장고는 post_detailview 를 찾고 있다고 생각하고 pk5와 일치한 view 로 정보를 보내게 됩니다..

pkprimary key의 약자입니다. 장고 프로젝트에서 자주 사용되는 이름이에요. 물론 여러분은 내가 원하는 변수 이름을 사용할 수 있어요. (기억하세요 : 변수 이름에 공백문자는 사용할 수 없으며 소문자와 _를 사용할 수 있습니다.) 예를 들어, (?P<pk>[0-9]+)의 변수를 post_id바꾼다면 하면 정규표현식도 (?P<post_id>[0-9]+)으로 바뀌게 됩니다.

좋아요, 이제 새로운 URL 패턴을 blog/urls.py 파일에 추가했어요! 이제 페이지를 새로고침 하세요. : http://127.0.0.1:8000/ 쾅! 또 에러가 났어요! 예상대로에요!

AttributeError

다음 단계는 무엇일까요? 그렇죠. : view를 추가해야죠!

Post 상세 페이지에 뷰 추가하기

이제 view 는 추가적으로 매개 변수pk를 받아야합니다. view 에 가져다가 써야겠죠? 그래서 함수를 정의할 때, pk를 받도록 def post_detail(request, pk):라고 정의 할 것입니다. 기입된 urls(pk)에 지정한 것과 정확히 똑같은 이름을 사용해야함을 주의하세요. 변수가 생략되면 문제가 생겨 오류가 날 거에요!

이제, 우리는 딱 한 개 블로그만 보고 싶어요. 이를 위해 다음과 같이 쿼리셋(queryset)을 사용해야해요. :

    Post.objects.get(pk=pk)

하지만 이 코드에는 문제가 있어요. primary key (pk)가있는 Post가 없다면 보고 싶지 않은 오류가 나올 거에요!

DoesNotExist error

우리가 원하는게 아니에요! 장고에서는 이를 해결 하기위해 특별한 기능을 제공해요. : get_object_or_404이에요. 이 기능을 사용하면 pk에 맞는 Post가 없을 경우, 멋진 페이지(페이지 찾을 수 없음 404 : Page Not Found 404)를 보여줄 거에요.

Page not found

좋은 점은 여러분 만의 페이지 찾을 수 없음(Page not found) 페이지를 예쁘게 만들 수 있다는 거에요. 지금 당장 중요한 것이 아니까 생략할게요.

좋아요. 이제 views.py 파일에 view 를 추가합시다!

blog/views.py 파일을 열어서 아래 코드를 추가하세요. :

    from django.shortcuts import render, get_object_or_404

from행 근처로 가서, 파일 맨 끝에 view 를 추가하세요. :

    def post_detail(request, pk):
        post = get_object_or_404(Post, pk=pk)
        return render(request, 'blog/post_detail.html', {'post': post})

브라우저를 새로고침 해보세요. : http://127.0.0.1:8000/

Post list view

잘 되네요! 그런데 블로그 제목 안의 링크를 클릭하면 어떻게 되나요?

TemplateDoesNotExist error

이런! 또 에러가 나왔네요! 하지만 이제는 이런 걸 어떻게 다뤄야하는지 알고 있죠? 우리는 이제 템플릿을 추가해 볼 거에요!

Post 상세 페이지에 템플릿 만들기

blog/templates/blog 디렉토리 안에 post_detail.html라는 새 파일을 생성하세요.

아마도 화면에는 이런 것들이 보이겠죠?

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

{% block content %}
    <div class="post">
        {% if post.published_date %}
            <div class="date">
                {{ post.published_date }}
            </div>
        {% endif %}
        <h1>{{ post.title }}</h1>
        <p>{{ post.text|linebreaks }}</p>
    </div>
{% endblock %}

다시 한 번 base.html을 확장해 봅시다. content 블록에서, 블로그 글의 published_date 출판일(존재한다면) 과 제목, 내용을 보이게 할 거에요. 그런데 제일 중요한 것을 얘기해봐야하지 않겠어요?

{% if ... %} ... {% endif %} 는 무엇인가를 확인할 때 사용하는 템플릿 태그 입니다. (if ... else ..파이썬 들어가기 장에서 본 기억이나나요?) 우리는 post의 출판 날짜(published_date)가 비어있는지 아닌지 확인하고 싶어요.

페이지를 새로고침하면 페이지 찾을 수 없음(Page not found) 페이지가 없어진 것을 알 수 있어요.

Post detail page

야호! 잘 되네요!

한 가지만 더: 배포하세요!

PythonAnywhere에서도 웹사이트가 잘 작동하는지 봐야겠죠? 다시 한 번 배포해봅시다.

$ git status
$ git add -A .
$ git status
$ git commit -m "Added view and template for detailed blog post as well as CSS for the site."
$ git push
$ cd my-first-blog
$ source myvenv/bin/activate
(myvenv)$ git pull
[...]
(myvenv)$ python manage.py collectstatic
[...]
  • 마지막으로 웹 탭(Web tab)에서 다시 불러오기(Reload) 를 누르세요.

이제 배포가 완료 되었어요. 잘 작동되는지 확인해 보세요! 축하합니다. :)