backend/django

django tutorial: View, template 이용하기 (part3)

seul chan 2017. 3. 2. 17:17

part3에서는 'views' 기능을 만드는데 집중 (공식 웹사이트에서는 public interface라는 용어를 썼다.)


Overview

-'view'는 장고 어플리케이션의 일종의 웹페이지 타입으로, 특정한 템플릿을 가진 특정한 기능을 제공해준다. 

ex) 블로그 어플리케이션에서 볼 수 있는 view

-블로그 홈페이지: 최근의 entries를 보여준다

-'detail' 페이지 입구: single entry의 링크

-year-based 페이지

-month-based 페이지 등등... (왜 이런 예시를 들었는지 잘 모르겠다)


poll application에서는 4가지 view를 만들것이다.

- Question 'index' page: 가장 최근의 questoins들을 보여줌

- Question 'detail' page: question text를 투표 폼과 함꼐 보여줌

- Question 'results' page: 특정 질문에 대한 결과를 보여줌

- Vote action: 특정 질문 내 특정 선택에 대한 투표


장고에서 web page나 다른 컨텐츠들은 views를 통해서 전달된다. 각각의 view는 단순한 파이썬 function으로 구성된다. (CBV-class-based views인 경우에는 method)

장고는 URL에 맞는 view를 선택한다. (정확하게는 domain name 다음의 URL)


URL 패턴은 general form of URL: ex) /newsarchive/<year>/<month>/

자세한 내용은 장고의 django.urls(https://docs.djangoproject.com/en/1.10/ref/urlresolvers/#module-django.urls) 참고


Writing more views

polls.views.py에 몇 개의 views를 더 추가할 것이다. 

-------------------

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)

-------------------

detail, result, vote 뷰를 추가해 주었다. 


추가한 view들을 polls.urls 모듈에 url()를 추가해서 연결시킨다

polls/urls.py를 열고

-------------------

from django.conf.urls import url


from . import views


urlpatterns = [

    # ex: /polls/

    url(r'^$', views.index, name='index'),

    # ex: /polls/5/

    url(r'^(?P<question_id>[0-9]+)/$', views.detail, name='detail'),

    # ex: /polls/5/results/

    url(r'^(?P<question_id>[0-9]+)/results/$', views.results, name='results'),

    # ex: /polls/5/vote/

    url(r'^(?P<question_id>[0-9]+)/vote/$', views.vote, name='vote'),

]

-----------------

로 수정해준다. 기초적인 수준의 정규표현식이 필요하다. 

각각의 url 예시이다.

-처음 index의 url은 => localhost:8000/polls/

-detail => localhost:8000/polls/1/ (숫자)

-results => localhost:8000/polls/1/results/

-vote => localhost:8000/polls/1/vote/


Write views that actually do something

view는 두가지 중 하나이다.

-HttpResponse object 반환: 요청된 페이지의 콘텐츠를 담고 있음

-Http404와 같은 예외


모든 장고는 HttpResponse를 필요로 한다(?)

왜냐면 편하기 때문에! 


새로운 index() view를 만들자: 5개의 최신 poll questions을 콤마 (',')로나눠서 불러오는 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

-----------

를 추가한다. 


여기에 문제가 있다: 페이지 디자인이 hard-coded, 

 => Djanog의 템플릿 시스템을 통해 Python의 디자인을 템플릿으로 나눔


- templates 폴더를 polls 디렉토리에 만듬

'TEMPLATES' 세팅은 장고가 어떻게 템플릿을 불러오고 사용하는지를 나타냄. 

default settiing file: DjangoTemplate ?? 

- templates 디렉토리에 polls 디렉토리를 만들고 안에 index.html 파일을 만들기

polls/temlplates/polls/index.html 처럼


#Template namespacing

#Now we might be able to get away with putting our templates directly in polls/templates (rather than creating another polls subdirectory), but it would actually be a bad idea. Django will choose the first template it finds whose name matches, and if you had a template with the same name in a different application, Django would be unable to distinguish between them. We need to be able to point Django at the right one, and the easiest way to ensure this is by namespacing them. That is, by putting those templates inside another directory named for the application itself.

(더 읽어보자..)


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에서 index view를 업데이트 한다.

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))

------------

를 추가한다.

이 코드는 polls/index.html 템플릿을 불러오는 코드이다. 

즉, index를 불러오는 url(ex: localhost:8000/polls/)을 입력하면

=> polls/view.py 안의 index 함수 호출 

=> index 함수에서 loarder 함수를 통해 polls/index.html 호출


과 같은 순서로 진행이 된다.


A shortcut: render()¶


템플릿을 불러오고, context를 채우고 HttpResponse object를 불러오는 방식의 숏컷을 제공한다.

다시 써진 index()이다. (polls/viewsw.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)

------

loader이나 HttpResponse를 불러오지 않고 views를 만들수 있다. 

render() 기능은 첫 argument로 request object를 받고(request), 

두번째 argument로 HttpResponse를 받고 ('polls/index.html')

세번째 optional argument를 받는다. 



Raising a 404 error
HttpResponse를 받지 않고 질문의 id 값이 없을 경우 exception을 부르기 위함이다.

polls/view.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})
------
이 새로운 코드는 만약 요청된 질문의 id 값이 없다면 Http404 예외를 부른다. 
polls/detail.html을 넣는 것은 나중에 다룰 것이다.

A shortcut: get_object_or_404()¶
detail()을 사용해 Http404 예외의 숏컷을 사용할 수 있다.
polls/views.py에
------
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})
------
get_object_or_404()의 기능은 첫번째 orgument를 장고 모델로 받는다..


Use the template system¶

detail.html을 수정한다.
polls/temlplates/polls/detail.html에
----------
<h1>{{ question.question_text }}</h1>
<ul>
{% for choice in question.choice_set.all %}
    <li>{{ choice.choice_text }}</li>
{% endfor %}
</ul>
----------

The template system uses dot-lookup syntax to access variable attributes. In the example of {{ question.question_text }}, first Django does a dictionary lookup on the object question. Failing that, it tries an attribute lookup – which works, in this case. If attribute lookup had failed, it would’ve tried a list-index lookup.

Method-calling happens in the {% for %} loop: question.choice_set.all is interpreted as the Python code question.choice_set.all(), which returns an iterable of Choice objects and is suitable for use in the {% for %} tag.

See the template guide for more about templates.
(무슨 말인지 잘 모르겠다... 추후에 해석해 놓겠따 ㅠㅠ)

Removing hardcoded URLs in templates

polls/index.html 템플릿에 링크를 적었던 하드코드는 다음과 같다.
----
<li><a href="/polls/{{ question.id }}/">{{ question.question_text }}</a></li>
----
이 하드코드의 문제는 url() functions의 이름을 바꾸면 연관성이 없어져서?
-----
<li><a href="{% url 'detail' question.id %}">{{ question.question_text }}</a></li>
-------


Namespacing URL names
튜토리얼에서는 polls 한 개의 앱만을 가지지만, 실제에서는 많은 앱을 갖는다.
이들 URL간의 차이 => URLconf에 namespaces를 추가하여 장고가 알 수 있게 한다.
polls/urls.py 파일에 app_name을 추가한다
-------
from django.conf.urls import url

from . import views

app_name = 'polls'
urlpatterns = [
    url(r'^$', views.index, name='index'),
    url(r'^(?P<question_id>[0-9]+)/$', views.detail, name='detail'),
    url(r'^(?P<question_id>[0-9]+)/results/$', views.results, name='results'),
    url(r'^(?P<question_id>[0-9]+)/vote/$', views.vote, name='vote'),
]
-------
그리고 polls/index.html 템플릿을 바꿔준다.
polls/templates/polls/index.html에서 
------
<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>
-------
이걸로 바꿔준다. 
이걸로 tutorial part3는 끝이다. 3부터는 제대로 이해가 되지 않아서 다시 한 번 일독이 필요할 듯 하다
(한글 튜토리얼을 찾아서 읽어보자)