티스토리 뷰

View는 우리의 장고 애플리케이션에서 웹페이지의 형식인데, 그것은 일반적으로 특수한 함수를 제공하고 특수한 템플릿을 가진다. 


Django에서, 웹페이지와 다른 컨텐츠는 views에 의해서 배달된다. 각각의 view는 하나의 간단한 Python function(or method, in the case of class-based views.)에 의해서 표현되는데, Django는 요청된 URL(정확하게는 도메인이름 다음의 URL의 파트)을 검사함으로써 view를 고를 것이다. 


URL로부터 view까지 도달하는데, 장고는 URLconfs라고 알려진 것을 사용한다. 이것은 URL patterns을 views로 매핑한다.


이번 튜토리얼에서는 URLconfs의 사용에 대한 기본적인 구조를 제공하며, 그 후는 알아서 찾길 바란다.


*Write your first view


polls/views.py 를 열고 다음의 코드를 입력해보자.


from django.http import HttpResponse


# Create your views here.


def index(request):

  return HttpResponse("Heelo, world. You're at the polls index.")


이것은 장고에서 가능한 가장 간단한 view이다. 이 view를 부르기 위해서, 우리는 이것을 a URL과 map시킬 필요가 있다. 그리고 이것을 위해 우리는 URLconf가 필요하다.


To create a URLconf in the polls directory, create a file called urls.py. Your app directory should now look like:


polls/

├── __init__.py

├── admin.py

├── models.py

├── tests.py

├── urls.py

└── views.py


in the polls/urls.py file include the following code:


from django.conf.urls import url


from . import views


urlpatterns = [

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

]


The next step is to point the root URLconf at polls.urls modules. In mysite/urls.py insert an include(), leaving you with:


from django.conf.urls import include, url

from django.contrib import admin


urlpatterns = [

  url(r'^polls/', include('polls.urls')),

  url(r'^admin/', include(admin.site.urls)),

]


You have now wired an index view into the URLconf. Go to http://localhost:8000/polls/ in your browser, and you should see the text "Hello, world. You're at the polls index.", which you defined in the index view.


정규식: r'^polls/'  

-위와 같이 정규식 문자열 앞에 r 문자를 선행하면 이 정규식은 Raw String 규칙에 의하여 백슬래시 두개 대신 한개만 써도 두개를 쓴 것과 동일한 의미를 갖게 된다.


- ^ 문자: 문자클래스 내에는 어떤 문자나 메타문자도 사용가능하지만 주의해야 할 메타문자가 한가지 있는데, 바로 ^ 문자이다. 문자클래스 내에 ^ 메타문제가 사용된 경우에는 반대(not)의 의미를 갖게된다. 

i.e. [\D - 숫자가 아닌것과 매치, [^0-9]와 동일한 표현식이다.]


-^ 메타문자는 문자열의 맨 처음과 일치함을 의미한다. 컴파일 옵션 re.MULTILINE 을 사용할 경우에는 여러줄의 문자열에서는 각 라인의 처음과 일치하게 된다. 

>>> print(re.search('^Life', 'Life is too short')) <_sre.SRE_Match object at 0x01FCF3D8> >>> print(re.search('^Life', 'My Life')) None


^Life 정규식은 "Life"라는 문자열이 처음에 올 경우에는 매치하지만 처음이 아닌 위치에 있을경우에는 매치되지 않음을 알 수 있다.


$

$ 메타문자는 ^ 메타문자의 반대의 경우이다. $는 문자열의 끝과 매치함을 의미한다.

다음의 예를 보자.

>>> print(re.search('short$', 'Life is too short'))
<_sre.SRE_Match object at 0x01F6F3D8>
>>> print(re.search('short$', 'Life is too short, you need python'))
None




The url() function is passed four arguments, two required: regex and view and two optional: kwargs, and name. At this point, it's worth reviewing what these arguments are for.


-url() argument: regex

regex is used short form meaning "regular expression", which is a syntax formatting patterns in strings, or in this case, url patterns.


Note that these regular expressions do not search GET and POST parameters, or the domain name. In a request to http://www.example.com/myapp/, the URLconf will look for myapp/. In a request to http://www.example.com/myapp/?page=3, the URLconf will also look for myapp/


Finally, a performance note: these regular expressions are compiled the first time the URLconf module is loaded. They're super fast(as long as the lookups aren't too complex as noted above).


-url() argument: view

When Django finds a regular expression match, Django calls the specified view function, with an HttpRequest object as the first argument and any "captured" values from the regular expression as other arguments.


-url() argument: kwargs

Arbitrary keyword arguments can be passed in a dictionary to the target view. We aren't going to use this feature of Django in the tutorial.


-url() argument: name

Naming your URL lets you refer to it unambiguously from elsewhere in Django especially templates. This powerful feature allows you to make global changes to the url patterns of your project while only touching a single file.


*Writing more views

Now let's add a few more views to polls/views.py. These views are slightly different, because they take an argument:


from django.shortcuts import render

from django.http import HttpResponse


# Create your views here.


def index(request):

  return HttpResponse("Heelo, 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)


Wire these new views into the polls.urls module by adding the following url() calls:


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


]


브라우저에서 "/polls/34/"를 치면 이것은 detail() method를 실행하고 네가 URL에서 제공한 어떤 ID라도 보여줄 것 이다. "/polls/34/results/" 그리고 "/polls/34/vote/" 또한 시도해보자. 


누군가가 너의 웹사이트에서 페이지를 요청할 때 - "polls/34/", Django는 mysite.urls Python module을 로드 할 것이다. 왜냐하면 이것은 

ROOT_URLCONF setting에 의해서 (mysite/setting.py에 존재하는; ROOT_URLCONF = 'mysite.urls') 가리켜진다. 그것은urlpatterns라고 이름지어진 변수를 찾으며 제대로 된 정규식을 추적한다. 우리가 사용하는 include() 함수들은 다른 URLconf들을 참조한다. 주목해야하는게, include() 함수의 정규식들은 $(end-of-string match character)를 가지지않는다. 그러나 오히려 /가 뒤로 빠져있다. 그것은 매치되는 URL의 부분을 제거하고 남아있는 string을 다음의 프로세스를 위해 included된 URLconf에게 전달한다.


include() 뒤의 idea는 URLs를 plug-and-play하기 쉽게 만들기 위함이다. polls가 그들이 가지는 URLconf(polls/urls.py)안에 있기 때문에, 그들은 "/polls/" 혹은 "/fun_polls/" 또는 "/content/polls/"또는 어떤 다른 path root 아래에 위치할 수 있고 그앱은 여전히 작동한다.


만약 유저가 "/polls/34/"를 입력한다면,


1. 장고는 '^/polls/'에 매치되는 것을 찾을 것이다.

2. 그다음, 매치되는 텍스트 ("polls/")를 벗겨내고, 남은 텍스트 "34/"를 'poll.urls' URLconf에 전달할 것 인데, 이것은 detail() view가 다음과 같이 결과를 내는 r'(?P<question_id>[0-9]+)/$'와 매치시키는 다음의 프로세스를 위함이다.

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


question_id = '34' 는 (?P<question_id>[0-9]+)로부터 왔다. "captures" text 패턴 주위에 parentheses를 사용하는 것은 저 패턴에 의해 매치했고, 그것을 view 함수에 매개변수로써 전달한다; ?P<question_id>은 매치된 패턴을 확인하기위해 사용되는 이름을 정의한다. 그리고 [0-9]+ 는 숫자의 결과를 매치하기 위한 정규식이다.


the URL 패턴이 정규식이기 때문에, 네가 그들과 함꼐 할수 있는 것에 대한 제한은 실제로 없다. 그리고 .html과 같은 허접쓰레기같은 URL을 추가할 필요가 없다. - 네가 원하지 않는 이상- 

url(r'^polls/lastest\.html$', views.index),

그러지 말자. 바보같은 짓이다.


정규식: 

반복 (+)

또하나 반복을 나타내는 메타문자로 + 가 있다. +는 최소 1개 이상의 반복을 필요로 하는 메타문자이다. *가 반복횟수 0부터의 의미라면 +는 반복횟수 1부터의 의미이다.

다음의 정규식을 보자.

ca+t

위 정규식의 의미는 다음과 같다.

"c + a(1번 이상 반복) + t"

위 정규식에 대한 매치표는 다음과 같다.

정규식문자열Match 여부설명
ca+tctNo"a"가 0번 반복되어 매치되지 않음
ca+tcatYes"a"가 1번 이상 반복되어 매치 (1번 반복)
ca+tcaaatYes"a"가 1번 이상 반복되어 매치 (3번 반복)


Named Groups

그룹은 매우 유용하지만 정규식 내에 그룹이 무척 많아 진다고 가정 해 보자. 예를 들어 정규식 내에 그룹이 10개 이상만 되어도 매우 혼란스러울 것이다. 거기에 더해 정규식이 수정되면서 그룹이 추가 삭제될 경우 그 그룹을 index로 참조했던 프로그램들도 모두 변경해 주어야 하는 위험도 갖게 된다.

만약 그룹을 index가 아닌 이름으로 참조할 수 있다면 어떨까? 그렇다면 이런 문제들에서 해방되지 않을까? 

이러한 이유로 정규식은 그룹을 만들 때 그룹명을 지정할 수 있게 했다. 그 방법은 다음과 같다.

(?P<name>\w+)\s+((\d+)[-]\d+[-]\d+)

위 정규식은 이전에 보았던 이름과 전화번호를 추출하는 정규식이다. 기존과 달라진 부분은 다음과 같다.

  • (\w+) --> (?P<name>\w+)

뭔가 대단히 복잡해 진것처럼 보이지만 (\w+) 라는 그룹에 "name"이라는 이름을 지어 준 것에 불과하다. 여기서 사용한 (?...) 표현식은 정규표현식의 확장구문이다. (여기서 ...은 다양하게 변한다는 anything의 의미이다.) 이 확장구문을 이용하기 시작하면 가독성이 무척 떨어지지만 그에 더해 강력함을 갖게 되는 것이다. 앞으로 알아볼 "전방탐색" 역시 이 확장구문을 이용해야만 한다.

그룹에 이름을 지어주기 위해서는 다음과 같은 확장구문을 사용해야 한다.

  • (?P<그룹명>...)

참고로 전방탐색의 확장구문은 다음과 같다. (조금 후에 자세히 알아볼 것이다.)

  • (?=...) - 긍정형 전방탐색
  • (?!...) - 부정형 전방탐색

그룹에 이름을 지정하고 참조하는 다음의 예를 보자.

>>> p = re.compile(r"(?P<name>\w+)\s+((\d+)[-]\d+[-]\d+)")
>>> m = p.search("park 010-1234-1234")
>>> print(m.group("name"))
park

위 예에서 보듯이 "name"이라는 그룹명으로 참조가 가능함을 확인 할 수 있다.

그룹명을 이용하면 정규식 내에서 재 참조역시 가능하다.

>>> p = re.compile(r'(?P<word>\b\w+)\s+(?P=word)')
>>> p.search('Paris in the the spring').group()
'the the'

위 예에서 보듯이 재 참조시에는 (?P=그룹명) 이라는 확장구문을 이용해야 한다.


*Write views that actually do something


Each view is responsible for doing one of two things: returning an HttpResponse object containing the content for the requested page, or raising an exception such as Http404. The rest is up to you.


Your view can read records from a database, or not. 그것은 template system을 사용할 수도 있고, PDF file을 생성할 수도 있고, XML을 출력할 수도 있고, ZIP파일을 생성할수도 있고,,, 네가 원하는 Python libraries를 사용한다면 네가 원하는 모든 것을 할 수 있다.


이것은 편리하기 때문에, Django 소유의 database API를 사용해보자(Tutorial 1).  

시스템의 마지막 5개의 poll question을 보여주는 새로운 index() 뷰의 one stab이다. publication date에 따라 콤마에 의해 분리된다.


from django.shortcuts import render

from django.http import HttpResponse

from .models import Question


# Create your views here.


def index(request):

  latest_question_list = Question.objects.order_by('-pub_date')[:5]

  output = ', '.join([p.question_text for p in latest_question_list])

  return HttpResponse(output)


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)


장고 문법:

리스트 내포(List comprehension)

리스트 안에 for문을 이용하면 좀 더 편리하고 직관적이게 프로그램을 만들 수 있다. 다음의 예제를 보자.

>>> a = [1,2,3,4]
>>> result = []
>>> for num in a:
...     result.append(num*3)
...
>>> print(result)
[3, 6, 9, 12]

위 예제는 a라는 리스트의 각 항목에 3을 곱한 결과를 result라는 리스트에 담는 예제이다. 이것을 리스트 내포를 이용하면 다음과 같이 간단히 해결할 수 있다.

>>> result = [num * 3 for num in a]
>>> print(result)
[3, 6, 9, 12]

만약 3을 곱한 수 중 짝수만 담고 싶다면 다음과 같이 할 수 있다.

>>> result = [num * 3 for num in a if num % 2 == 0]
>>> print(result)
[6, 12]

리스트 내포의 일반적인 문법은 다음과 같다.

[표현식 for 항목 in 반복가능객체 if 조건]

위 문법에서 조건식 부분은 위 예제에서 보듯이 생략이 가능하다.

조금 복잡하지만 for문을 2개이상 사용하는 것도 가능하다. for문을 여러개 사용할 경우의 문법은 다음과 같다.

[표현식 for 항목1 in 반복가능객체1 if 조건1
        for 항목2 in 반복가능객체2 if 조건2
        ...
        for 항목n in 반복가능객체n if 조건n]

위에서 살펴본 구구단의 모든 결과를 리스트에 담고 싶다면 리스트 내포를 이용하여 아래와 같이 구현할 수 있을 것이다.

>>> result = [x*y for x in range(2,10)
...               for y in range(1,10)]
>>> print(result)
[2, 4, 6, 8, 10, 12, 14, 16, 18, 3, 6, 9, 12, 15, 18, 21, 24, 27, 4, 8, 12, 16,
20, 24, 28, 32, 36, 5, 10, 15, 20, 25, 30, 35, 40, 45, 6, 12, 18, 24, 30, 36, 42
, 48, 54, 7, 14, 21, 28, 35, 42, 49, 56, 63, 8, 16, 24, 32, 40, 48, 56, 64, 72,
9, 18, 27, 36, 45, 54, 63, 72, 81]


그러나 여기서 문제는 페이지의 디자인이 뷰에 하드코딩되었다는 것이다. 만약 페이지의 모습을 바꾸고 싶다면 더는 이 파이썬 코드를 바꿔야할 것이다. 그래서, 파이썬으로부터 디자인을 분리하기 위해 뷰가 사용가능한 템플릿을 생성함으로써 Django's template system을 사용해보자.


First, 너의 polls 디렉토리에 templates라는 디렉토리를 생성하자. Django는 거기서 template을 찾을 것이다.


너의 프로젝트의 TEMPLATES 세팅은 장고가 템플릿들을 로드하고 읽을 방법을 설명한다. 디폴트 셋팅 파일은 DjangoTemplates의 백엔드를 정리하는데, 그것의 APP_DIRS 옵션이 True로 셋팅된다. convention에 의해 DjangoTemplates는 INSTALLED_APPS의 각각에서 서브디렉토리 "templates"를 찾는다. 이것이 우리가 tutorial 2에서 했던 것처럼 DIRS 옵션을 수정하지 않았더라도, 장고가 polls templates 를 찾는 것을 아는 방법이다.


생성한 templates 디렉토리 안에, polls라는 또다른 디렉토리를 생성하고, 그안에 index.html이라는 파일을 생성하라. 즉, 너의 템플릿은 polls/templates/polls/index.html에 있어야 한다. app_directories 템플릿 로더가 위에 묘사된 것처럼 작동하는 방법 때문에, 너는 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 %}


자, 이제 템플릿을 사용하기 위해 우리의 index view(in polls/views.py)를 업데이트 해보자. 


from django.http import HttpResponse

from django.template import RequestContext, 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 = RequestContext(request, {

    'latest_question_list': latest_question_list,

  })

  return HttpResponse(template.render(context))


이 코드는 템플릿 polls/index.html을 로드하고, 이것을 context에 패스시킨다. 그 context는 딕셔너리인데, 이 딕셔너리는 템플릿 변수 이름을 파이썬 객체에 매핑한다.


/polls/를 가리키게 하여 페이지를 로드해보면 너는 다음과 같은 화면을 보게 될 것이다. 링크는 question의 detail 페이지를 가리킨다.





A shortcut: render()

이것은 템플릿을 로드하는 매우 흔한 idiom인데, context를 채우고, HttpResponse 객체를 rendered된 템플릿의 결과와 함께 반환한다.장고는 a shortcut을 재공하는데 여기 전체 index() 뷰가 새로 쓰여있다.


from django.shortcuts import render


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, RequestContext and HttpResponse를 import할 필요가 없다.


The render() function은 매개변수 첫번째에 the request object를 가지고, 두번째 매개변수로써 template name을 가지고 선택적으로 딕셔너리를 세번째 매개변수로 가진다. 이 함수는 rendered된 주어진 템플릿의 HttpResponse 객체를 주어진 컨텍스트와 함께 반환한다.



*Raising a 404 error

Now, let’s tackle the question detail view – the page that displays the question text for a given poll. Here’s the view:


from django.shortcuts import render

from django.http import Http404


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


새로운 개념이 있다: 이 뷰는 만약 requested ID와 함께 question이 존재하지 않는다면 Http404 exception을 발생시킨다.

나중에 polls/details.html 템플릿에 무엇을 넣을 수 있는지 간략히 더 이야기 해보도록 하고, 일단 간단히 코드를 다음과 같이 입력해보자.


{{ question }}



A shorcut: get_object_or_404()

이것은 get()과 만약 오브젝트가 존재하지 않는다면 Http404를 사용하는 매우 흔한 idiom이다. Django는 이 shortcut을 제공한다. 여기 새로 쓰여진 detail() 뷰이다.


from django.shortcuts import get_object_or_404, render


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()함수는 Django model을 첫번째 매개변수로 갖고, 두번째로 arbitrary number of keyword arguments를 가지는데 이것은 그 모델의 매니저의 get()에 넘겨진다. 만약 객체가 존재하지 않는다면 Http404를 반환시킨다.


또한 get_list_or_404()함수도 있다.


*Use the template System


detail()뷰로 다시가자. 컨텍스트 변수 question이 주어지면서, 여기 polls/detail.html 템플릿이 다음과 같이 생긴 것이 있다.


<h1>{{ question.question_text }}</h1>

<ul>

{% for choice in question.choice_set.all %}

  <li> {{ choice.choice_text }} </li>

{% endfor %}

</ul>


템플릿 시스템은 변수 속성들에 접근하기 위해 dot-lookup syntax를 이용한다. 예제의 {{ question.question_text }}에서, 처음으로 장고는 question 객체의 딕셔너리 검색을 한다. 실패한다면, 그것은 속성 검색을한다.(여기선 작동하는) 만약 속성 검색이 실패한다면, 장고는 리스트 인덱스 검색을 시도할 것이다.


메소드 호출이 {% for %} 루프안에서 일어난다.: question_choice_set.all 은 파이썬 코드 question_choice_set.all()로 번역되는데, 이것은 반복가능한 Choice 객체를 반환하고 {% for %}태그에서 사용하기 적합하다.


See the template guide for more about templates.



*Removing hardcoded URLs in templates


기억하라. 우리가 a question으로 가는 링크를 polls/index.html에 썼을 때, 그 링크는 부분적으로 다음과 같이 하드코딩 되어 있다.


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


이 하드코딩된 tightly-coupled approach와 함께 문제는 그것이 수 많은 템플릿과 함께 프로젝트들에서 URLs을 바꾸는데 Challenge가 되는 것이다. 그러나, 우리가 url() function에서(polls.urls 모듈에서) name argument를 정의했기 때문에, 우리는 {% url %} 템플릿 태크를 사용함으로써 우리의 url configurations에 정의한 특수한 URL paths에 대한 의존성을 제거할 수 있다. 


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


이것이 작동하는 방법은 polls.urls 모듈에 특수화함으로써 URL definition을 찾는 것이다. 너는 URL name 'detail'이 정의되어 있는 곳을 정확히 볼 수 있다.


#the 'name' value as called by the {% url %} template tag

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


만약 네가 polls detail view의 URL을 바꾸고 싶다면, (polls/specifics/12/ 과 같은) 템플릿에서 그것을 하는 것 대신에, 너는 그것을 polls/urls.py에서 할 수 있다.


 #add the word 'specifics'

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



*Namespacing URL names

이 튜토리얼의 프로젝트는 단 하나의 앱 polls를 가진다. 실제 장고 프로젝트들에선, 아마 5,10,20개 혹은 그 이상의 앱이 있을 수 있다. 어떻게 장고는 그들 가운데서 URL을 구별할 수 있을까? polls 앱은 하나의 detail 뷰를 가진다. 그래서 이앱은 블로그를 위한 같은 프로젝트에서 하나의 앱일 것이다.(???) (could't understand this sentence->) How does one make it so that Django knows which app view to create for a url when using the {% url %} template tag?


해답은 namespaces를 너의 root URLconf에 추가하는 것이다. mysite/urls.py 파일에서 namespaces를 추가하라.


urlpatterns = [

    url(r'^polls/', include('polls.urls', namespace="polls")),

    url(r'^admin/', include(admin.site.urls)),

]


그리고 polls/index.html 템플릿을 수정하라.


{% if latest_question_list %}

  <ul>

  {% for question in latest_question_list %}

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

  {% endfor %}

  </ul>

{% else %}

  <p>No polls are available.</p>

{% endif %}




댓글
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2024/04   »
1 2 3 4 5 6
7 8 9 10 11 12 13
14 15 16 17 18 19 20
21 22 23 24 25 26 27
28 29 30
글 보관함