본문 바로가기
Django/튜토리얼

[Django/장고] 파이썬으로 웹페이지 만들기 튜토리얼 - 5부

by 붕어사랑 티스토리 2021. 5. 27.
반응형

1. View 다루기

이전 튜토리얼에서는 model을 다루어 보았으니 이번에는 View에 대해서 공부해봅시다.

들어가기전에 MVC와 MTV에 대해서 복습해볼까요?

 

MVC 는 Model, View, Controller 의 약자이고 MTV는 Model, Template, View 의 약자라고 했습니다.

둘이 이름만 다르지 사실은 같은내용이고 장고에서 MVC를 MTV라고 부른다고 하였습니다.

 

Model      <->   Model

View        <->   Controller

Controller <->    View

 

이렇게 대응됩니다. 고로 장고에서 View는 유저가 보는 웹페이지와 서버의 중간다리라고 생각하시면 됩니다.

 

 

이제 polls 어플리케이션에 대한 구체적인 계획을 한번 짜봅시다.

  • index 페이지 – Question에 관한 정보를 나타내주는 페이지 입니다.
  • detail 페이지 – question text를 상세하게 나타내주는 페이지 입니다. 투표할수 있는 형태로 보여집니다.
  • result 페이지 – Question에 관한 결과를 나타내주는 페이지입니다.
  • Vote action – 투표하는 기능을 구현할 것입니다.

 

자그럼 아래 처럼 polls/view.py에 detail, results,vote view를 추가해 줍시다.

 

polls/views.py

from django.shortcuts import render
from django.http import HttpResponse


def index(request):
    return HttpResponse("Hello, World. You're at the polls index.")

def detail(request, question_id):
    return HttpResponse("You're looking at question %s." % question_id)

def results(request, question_id):
    response = "You're looking at the results of question %s."
    return HttpResponse(response % question_id)

def vote(request, question_id):
    return HttpResponse("You're voting on question %s." % question_id)

View를 추가해주셨다면 url에 연결해 줍니다.

 

from django.urls import path

from . import views

urlpatterns = [
    # ex: /polls/
    path('', views.index, name='index'),
    # ex: /polls/5/
    path('<int:question_id>/', views.detail, name='detail'),
    # ex: /polls/5/results/
    path('<int:question_id>/results/', views.results, name='results'),
    # ex: /polls/5/vote/
    path('<int:question_id>/vote/', views.vote, name='vote'),
]

자 위에 코드를 분석해봅시다.

 

이전 튜토리얼에서 polls의 root주소는 http://127.0.0.1:8000//polls/ 였죠?

 

아래코드를 분석하면

path('<int:question_id>/', views.detail, name='detail'),

http://127.0.0.1:8000//polls/퀘스쳔id 를 입력하면 views.py에서 detail view에 연결된다는 뜻입니다.

 

한번 http://127.0.0.1:8000//polls/34 로 들어가 봅시다.

질문을 34개까지 추가하지 않았는데 34라고 뜨는군요... 여기서 주목할 점은 <int:question_id> 입니다.

 

url에 <int:question_id>자리에 숫자를 입력하면 int형으로 question_id라는 변수를 만든뒤에 detail view에 넘겨줍니다.

def detail(request, question_id):
    return HttpResponse("You're looking at question %s." % question_id)

그럼 detail view에서 HttpResponse로 "You're looking at question 34." 라는 response를 넘겨주는 것이죠!

 

파이썬 코드로 생각하면 아래와같이 됩니다.

detail(request=<HttpRequest object>, question_id=34)

 

이해가 안되신다면 다시한번 차근차근 읽어주세요. 중요한 내용입니다.

 

127.0.0.1:8000/polls/34/results/

127.0.0.1:8000/polls/34/vote/

 

위 링크도 한번씩 들어가 봅시다.

 

 

 

2. 실제로 동작하는 View 만들기

위에 작성한 view는 빈껍데기에 불과합니다. 이번에는 view가 실제로 어떤 기능을 하도록 만들어 봅시다.

 

 

polls/views.py

from django.http import HttpResponse

from .models import Question


def index(request):
    latest_question_list = Question.objects.order_by('-pub_date')[:5]
    output = ', '.join([q.question_text for q in latest_question_list])
    return HttpResponse(output)

# Leave the rest of the views (detail, results, vote) unchanged

위 코드를 보시면 Question 모델에서 pub_date를 최신순으로 정렬하여 latest_question_list로 받아옵니다.

join 함수를 통하여 각 질문들을 output이라는 문자열로 합쳐주고요. httpresponse로 django에게 넘겨줍니다.

 

 

자 그럼 adim 페이지로 가서 question 데이터를 조금 추가해줍시다.

what's up이 제일 먼저 등록되어 있고 question 1~5까지 등록했습니다.

 

위 코드 기준으로 가장 최신의 데이터 5개만 출력하니 what's up을 제외한 question5부터 question1 까지 나오겠지요?

 

 

 

 

반응형

 

3. Template 이용하기

자 위의 예제에서 view가 실제로 어떤 동작을 하도록 작성해 보았습니다.

모델과 뷰를 배웠으니 이제 MTV 모델의 템플릿을 배워보겠습니다.

 

다시 복습해보죠.

 

모델 : 서버의 데이터베이스

뷰 : 모델과 템플릿의 중간다리 역할

템플릿 : 유저에게 브라우저로 보여지는 웹페이지

 

이전예제에서 우리는 models.py를 작성하여 데이터베이스를 설계하였고

view를 이용해 데이터베이스 정보를 가져와 웹페이지에 전달까지 해 보았습니다.

 

 

템플릿은 간단히 말하면 HTML 파일입니다. 다들 한번씩 학교다닐때 컴퓨터 수업에서 HTML을 해보신적이 있을겁니다.

 

장고의 Template의 특징은 HTML 코드 일부를 파이썬 코드로 작성할 수 있는 기능을 제공합니다.

 

 

 

먼저 Template파일의 위치에대해서 배워봅시다.

 

어플리케이션폴더이름/templates/어플리케이션폴더이름/템플릿파일

 

자 위의 예제에서 polls 어플리케이션의 index 뷰에 관한 템플릿을 만들면

 

polls/templates/polls/index.html 이 됩니다.

 

 

 

어 그런데 왜 polls를 두번 반복하죠? 그냥 polls/templates/index.html 하면 되지 않나요?

 

이유는 다음과 같습니다.

장고에서는 템플릿을 찾을때 templates 폴더를 싹다 뒤진뒤 이름만 맞으면 템플릿파일을 가져오게 됩니다.

 

예를들어 app2에서 abc라는 템플릿을 가져오겠다고 합시다.

app1/templates/abc.html

app2/templates/abc.html

헌데 위와같이 똑같이 app1에서 abc라는 템플릿이 있을경우 장고에서는 이름만 맞으면 되니 app1의 abc를 가져오는 경우가 생깁니다.

 

이를 방지하기위해 templates 폴더 믿에 polls 폴더를 한번 더 만들어준뒤 템플릿을 넣으라고 권장하고 있습니다.

이를 template namespacing이라고 합니다.

 

 

polls/templates/polls/index.html

{% if latest_question_list %}
    <ul>
    {% for question in latest_question_list %}
        <li><a href="/polls/{{ question.id }}/">{{ question.question_text }}</a></li>
    {% endfor %}
    </ul>
{% else %}
    <p>No polls are available.</p>
{% endif %}

polls/views.py

from django.http import HttpResponse
from django.template import loader

from .models import Question


def index(request):
    latest_question_list = Question.objects.order_by('-pub_date')[:5]
    template = loader.get_template('polls/index.html')
    context = {
        'latest_question_list': latest_question_list,
    }
    return HttpResponse(template.render(context, request))

 

자 위의코드는 템플릿을 실제로 작성하고 view와 연동하는 코드입니다.

 

아까 장고에서 템플릿은 파이썬으로 html코드를 작성하도록 해준다 했죠?

 

{% %} 이렇게 묶여있는게 파이썬 코드입니다.

 

 

코드를 분석해봅시다.

 

  • view에서 index.html을 로드합니다.
  • view에서 model에서 Question 데이터를 추출한뒤에 context 라는 딕셔너리에 담습니다.
  • template을 rendering 할때 context에서 데이터를 실어담에 템플릿에 정보를 보냅니다.
  • template은 context의 담긴 내용을 기반으로 html코드를 파이썬으로 작성합니다

 

위 코드에서 latest_question_list로 모델에서 데이터를 얻어왔습니다.

그걸 context에 담습니다. 여기서 context는 이삿짐 센터라고 생각하시면 됩니다! 필요한 물품을 여기다 다 담는것이죠!

그리고 latest_question_list 데이터가 유효하면 아래 코드를 돌립니다.

 

{% for question in latest_question_list %}
    <li><a href="/polls/{{ question.id }}/">{{ question.question_text }}</a></li>
{% endfor %}

 

<li><a href="/polls/{{ question.id }}/">{{ question.question_text }}</a></li>

라는 코드를 for문으로 여러번 작성하라는 뜻이지요

 

 

 

runserver을 해보면 아래와같이 나옵니다! <li> 코드가 다섯개가 딱 생성되었군요!

 

브라우저에서 우클릭을 눌러 페이지 원본보기 를 클릭합시다.

 

<ul> 
    <li><a href="/polls/6/">question5</a></li>
    <li><a href="/polls/5/">question4</a></li>
    <li><a href="/polls/4/">question3</a></li>
    <li><a href="/polls/3/">question2</a></li>
    <li><a href="/polls/2/">question1</a></li>
</ul>

위 파이썬 코드로 html 코드가 작성된걸 확인하실 수 있습니다.

 

 

 

4. render() 함수

 

위의 views.py 코드에서 loader 하고 httpresponse 하는 작업은 render라는 wrapping 함수로 간략하게 표현될 수 있습니다.

 

polls/views.py

from django.shortcuts import render

from .models import Question


def index(request):
    latest_question_list = Question.objects.order_by('-pub_date')[:5]
    context = {'latest_question_list': latest_question_list}
    return render(request, 'polls/index.html', context)

 

코드내용만 다르고 기능은 같습니다. render를 애용합시다.

 

(wrapping 함수란 다른 함수 여러개 섞어 놓은 작업을 함수로 한번 더 묶어서 한번에 call할수 있도록 하는 함수입니다.)

 

 

 

5. 404 에러 일으키기

이번에는 detail view의 템플릿을 작성해 봅시다.

 

polls/views.py

from django.http import Http404
from django.shortcuts import render

from .models import Question
# ...
def detail(request, question_id):
    try:
        question = Question.objects.get(pk=question_id)
    except Question.DoesNotExist:
        raise Http404("Question does not exist")
    return render(request, 'polls/detail.html', {'question': question})

polls/templates/polls/detail.html

{{ question }}

 

 

위의 detail views 에서 404 에러를 일으키는 코드는 render와 비슷하게 아래와 같이 shorcut 코드로 작성 될 수 있습니다.

 

from django.shortcuts import get_object_or_404, render

from .models import Question
# ...
def detail(request, question_id):
    question = get_object_or_404(Question, pk=question_id)
    return render(request, 'polls/detail.html', {'question': question})

 

runserver를 한뒤 퀘스쳔을 클릭해보면 detail 내용이 나옵니다.

 

여기서 잠깐 template 문법에 대해서 말씀드리겠습니다.

 

{{ 로 시작하고 }} 닫히는 문법은 variable이라고 합니다.

{% 로 시작하고 %} 로 닫히는 문법은 tag라고 합니다.

 

variable은 말그대로 context에서 넘어오는 변수를 접근하는 syntax이고

tag는 tag내에 파이썬 코드를 실행 시킬 수 있는 syntax입니다.

 

고로 단순히 변수를 받아오고 싶으면 {{ }} 이걸 쓰면되고

파이썬 코드를 실행시키고 싶으시면 {% %} 하시면 되겠죠?

 

 

그럼 이제 아래와 같이 detail view에 choice를 넣어주는 코드를 작성해봅시다

 

polls/templates/polls/detail.html

<h1>{{ question.question_text }}</h1>
<ul>
{% for choice in question.choice_set.all %}
    <li>{{ choice.choice_text }}</li>
{% endfor %}
</ul>

 

위 코드가 어떻게 돌아가는지 한번 직접 분석해보시기 바랍니다.

 

6. URL 하드코드 제거하기

다시 index.html로 돌아갑시다.

{% if latest_question_list %}
    <ul>
    {% for question in latest_question_list %}
        <li><a href="/polls/{{ question.id }}/">{{ question.question_text }}</a></li>
    {% endfor %}
    </ul>
{% else %}
    <p>No polls are available.</p>
{% endif %}

여기서 

        <li><a href="/polls/{{ question.id }}/">{{ question.question_text }}</a></li>

이부분에서 /polls/ 부분이 하드코드 된것을 볼 수 있습니다.

 

간단히 말해서 저 href 링크는 detail 뷰로 가는 링크입니다. 이를 아래 처럼 바꿔 줍시다.

 

<li><a href="{% url 'detail' question.id %}">{{ question.question_text }}</a></li>

{% url %} 태그를 이용하면 위와같이 하드코드가 아닌 유동적인 코드로 바꿔주실 수 있습니다.

 

사용법은 다음과 같습니다.

 

{% url (원하는뷰의 url 이름) (url에 인풋으로 들어갈 수) %]

 

위 코드에서는 원하는 뷰의 이름은 detail 인데 이 detail의 url 이름은 urls.py에 가보시면 우리가 설정했던걸 아실 수 있습니다!

 

polls/urls.py

그럼 변수는 여기서 <int:question_id> 에 들어갈 변수겠지요? 여기서 question.id가 저 변수의 인풋으로 들어가게 됩니다!

 

 

그림으로 나타내면 이렇게 되겠네요.

 

 

7. URL namespacing

 

 

자 그런데 url name이 겹치는 경우는 어떻게 할가요?

 

app1에 detail 이라는 url을 가지고 있고

app2에 detail 이라는 url이 똑같이 있다면?

 

이를 구분해주기 위해 Django에서는 url namespacing 이라는 기능을 제공합니다.

 

 

사용방법은 urls.py 에 app_name 변수를 정의해주는 것입니다.

 

 

그럼 detail 템플릿 코드를 아래처럼 바꿔주면 됩니다.

 

 

 

<li><a href="{% url 'detail' question.id %}">{{ question.question_text }}</a></li>

에서

<li><a href="{% url 'polls:detail' question.id %}">{{ question.question_text }}</a></li>

으로

 

 

 

 

 

 

 

자. 여러분은이제 MTV 모델에 Model Template View 사용법을 모두 배우셨습니다!

 

오늘은 푹쉬시길 바랍니다

반응형

댓글