본문 바로가기
Web/Django

[Django로 배우는 쉽고 빠른 웹개발(기초편)] 03 Django 웹 프레임워크

by merona99 2022. 8. 10.
반응형

Django로 배우는 쉽고 빠른 웹개발(기초편)

Chapter 03 - Django 웹 프레임워크
이번 챕터에서 알아보는 것

일반적인 특징
장고 프로그램 설치
장고에서의 애플리케이션 개발 방식
애플리케이션 설계하기
프로젝트 뼈대 만들기
애플리케이션 개발하기 - Model 코딩
애플리케이션 개발하기 - View 및 Template 코딩

 


3.1 일반적인 특징

장고는 현재 가장 많이 사용되는 파이썬 웹 프레임워크

 

1) MVC 패턴 기반 MVT

  • 장고는 MVC(Model-View-Controller)를 기반으로 한 프레임 워크
  • 하지만 장고에서 View -> Template, Controller -> View
  • MVT(Model-View-Template)프레임워크라고 부르기도 하지만 개념은 동일

2) 객체 관계 매핑

  • 장고의 객체 관계 매핑(ORM, Object-Relational Mapping)은 데이터베이스 시스템과 모델이라는 파이썬 클래스를 연결시키는 다리역할
  • SQL 문장을 사용하지 않고도 테이블 조작 가능
  • 이미 구축한 db를 다른 db로 변경하는 경우에도 서정을 조금만 변경하면 가능하므로 쉽고 편리해짐

3) 자동으로 구성되는 관리자 화면

  • 기본 기능으로 관리자 화면을 제공
  • 관리자 화면을 통해 애플리케이션에서 사용하는 테이블과 데이터들을 쉽게 생성하거나 변경할 수 있음

4) 우아한 URL 설계

  • 웹 프로그래밍은 URL 디자인이 필수
  • 우아한 URL 방식을 채택하여 URL을 직관적이고 쉽게 표현 가능
  • 정규표현식을 사용하여 복잡한 URL도 표현가능하고, 각 URL 형태를 파이썬 함수에 1:1로 연결하도록 되어있어 개발이 편리하며, 이해하기도 쉬움

5) 자체 템플릿 시스템

  • 장고는 내부적으로 확장이 가능하고 디자인이 쉬운 강력한 템플릿 시스템을 갖고 있음
  • 화면 디자인과 로직에 대한 코딩을 분리하여 독립적으로 개발 진행이 가능함

6) 캐시 시스템

  • 캐시 시스템을 사용하여 자주 이용되는 내용을 저장해 두었다가 재사용하면 성능을 높일 수 있음
  • 장고의 캐시 시스템은 캐시용 페이지를 메모리, DB 내부, 파일 시스템 중 아무 곳에나 저장할 수 있음
  • 캐시 단위를 페이지에서부터 사이트 전체 or 특정 뷰의 결과, 템플릿의 일부 영역만을 지정하여 저장해 둘 수도 있음

7) 다국어 지원

  • 동일한 소스코드를 다른 나라에서도 사용할 수 있도록 텍스트의 번역, 날짜/시간/숫자의 포맷, 타임존의 지정 등과 같은 다국어 환경을 제공
  • 간단한 작업만으로 메시지를 하나 이상의 언어로 변역해주기 때문에 다국어를 제공하는 웹 사이트에 아주 유용하게 사용할 수 있음

8) 풍부한 개발 환경

  • 테스트용 웹 서버를 포함 -> 아파치 등의 웹 서버 없이도 테스트 진행 가능
  • 디버깅 모드 -> 에러를 쉽게 파악하고 해결할 수 있도록 상세한 메시지를 보여줌

9) 소스 변경사항 자동 반영

  • 개발 과정 -> *.py 파일의 변경 여부를 감시하고 있다가 변경이 되면 실행 파일에 변경 내역을 바로 반영
  • 소스파일을 수정하더라도 웹 서버를 다시 시작할 필요 없이 자동으로 새로운 파일이 반영 됨

3.2 장고 프로그램 설치

 

0) 파이썬 3.x 버전 설치

pip(python install package)
파이썬 오픈소스 저장소인 PyPI(Python Package Index)에 있는 SW 패키지를 설치하고 관리해주는 명령

1) Django 설치

(나의 경우는 이미 설치되어 있음)

django upgrade


3.3 장고에서의 애플리케이션 개발 방식

 

웹 사이트를 설계할 때 가장 먼저 해야할 일 : 프로그램이 해야 할 일을 적당한 크기로 나누어서 모듈화하는 것.

웹 사이트에 대한 전체 프로그램 : 프로젝트(Project)

모듈화된 단위 프로그램 : 애플리케이션(Application)

 

MVT 패턴

Model : DB에 저장되는 데이터를 의미 (블로그의 내용을 DB로부터 가져오거나 저장, 수정하는 기능)
Template : 사용자에게 보여지는 UI부분
View : 실질적으로 프로그램 로직이 동작하여 데이터를 가져오고 적절하게 처리한 결과를 템플릿에 전달하는 역할 (버튼을 눌렀을 때 어떤 함수를 호출하며 데이터를 어떻게 가공할 것인지 결정)

장고의 MVT 패턴

 

[웹 클라이언트의 요청을 받고 장고에서 MVT 패턴에 따라 처리하는 과정]

  1. 클라이언트의 요청 -> URLconf를 이용하여 URL 분석
  2. URL 분석 결과를 통해 해당 URL 처리를 담당할 뷰 결정
  3. 뷰는 자신의 로직을 실행시키면서 DB 처리 필요시 모델을 통해 처리 후 결과 반환 받음
  4. 뷰는 자신의 로직 처리가 끝나면 템플릿을 사용해 클라이언트에 전송할 HTML 파일 생성
  5. 뷰는 최종 결과로 HTML 파일을 클라이언트에 보내 응답

 

Model - 데이터베이스 정의

Model : 사용될 데이터에 대한 정의를 담고 있는 장고의 클래스

  • 장고는 ORM 기법을 사용하여 애플리케이션에서 사용할 DB를 클래스로 매핑할 수 있음
  • 즉, 하나의 모델 클래스는 하나의 테이블에 매핑되고, 모델 클래스의 속성은 테이블의 컬럼에 매핑 됨
  • ORM 기법을 사용해 테이블을 클래스로 매핑하면 DB에 대한 엑세스를 SQL 없이도 클래스를 다루는 것처럼 할 수 있고, DB 엔진을 변경하더라도 ORM을 통한 API는 변경할 필요가 없어서 편함
ORM이란?
ORM(Object-Relational Mapping)은 객체와 관게형 DB를 연결해주는 역할
ORM이 자동으로 적절한 SQL 구문이나 DB API를 호출해서 처리해주기 때문에 DB 대신에 객체(클래스)를 사용해 데이터를 처리할 수 있음

Primary Key는 Person 클래스에서 정의하지 않아도 장고에서 자동으로 부여

 

URLconf - URL 정의

  • 클라이언트의 요청을 받으면 가장 먼저 URL을 분석
  • 즉, 요청에 들어있는 URL이 urls.py 파일에 정의된 URL 패턴과 매칭되는 지를 분석
  •  URL/뷰 매핑(URLconf) : URL을 정의하기 위해서 urls.py 파일에 URL과 처리 함수(뷰)를 매핑하는 파이썬 코드를 작성

URLconf를 사용하면 URL과 뷰 함수를 서로 자유롭게 연결할 수 있어서 URL과 뷰 함수 이름이 자주 바뀌는 경우에도 URLconf에서 매핑한 부분만 수정하면 되므로 매우 편리함

 

[웹 클라이언트가 웹 서버에 페이지 요청 시, 장고에서 URL을 분석하는 순서]

  1. setting.py 파일의 ROOT-URLCONF 항목을 읽어 최상위 URLconf(urls.py) 위치를 알아냄
  2. URLconf를 로딩하여 urlpatterns 변수에 지정되어 있는 URL 리스트를 검사
  3. 위에서부터 순서대로 URL 리스트를 검사하면서 URL 패턴이 매치되면 검사를 종료
  4. 매치된 URL의 뷰(함수 or 클래스의 메소드)를 호출한다. 호출 시 HttpRequest 객체와 매칭할 때 추출된 단어들을 뷰에 인자로 넘겨 줌
  5. URL 리스트 끝까지 검사해도 매칭이 안되면 에러를 처리하는 뷰를 호출

Path Converter

  • <int:year>처럼 꺾쇠를 사용하는 부분으로 URL 패턴의 일부 문자열을 추출하기 위한 것
  • 요청 URL이 /articles/2018/인 경우 매치된 문자열 2018을 year에 할당힘
  • 즉, 뷰 함수를 views.year_archive(request, year=2018)처럼 호출하는 것

[정규표현식을 사용한 URL 패턴]

path() 함수에서의 <int:year>부분이 re_path() 함수에서는 (?P< year>[0-9]{4}) 처럼 정규표현식을 사용하면서 모든 정수가 아닌 0-9로 이루어진 4자리 숫자만 매치되도록 한정하고 있음

 

View - 로직 정의

  • 웹 요청에 있는 URL을 분석하고 그 결과로 해당 URL에 매핑된 뷰를 호출
  • 뷰 : 웹 요청을 받아서 DB 접속 등 해당 애플리케이션의 로직에 맞는 처리를 하고, 그 결과 데이터를 HTML로 변환하기 위해 템플릿 처리 후 최종 HTML로 된 응답 데이터를 웹 클라이언트로 반환하는 역할

뷰 함수는 첫 번째 인자로 HttpRequest 객체(위에서는 request)를 받고 필요한 처리를 한 후 최종적으로 HttpResponse 객체를 반환

 

Template - 화면 UI 정의

  • 클라이언트에게 반환하는 최종 응답 : HTML 텍스트
  •  클라이언트는 응답으로 받은 HTML 텍스트를 해석해 웹 브라우저 화면에 UI를 보여 줌
  • 장고에서 템플릿 파일을 찾을 때는 프로젝트 설정 파일인 settings.py 파일에 정의되어 있는 TEMPLATES 및 INSTALLED_APPS에서 지정된 앱의 디렉토리를 검색
※ TEMPLATES 항목에 정의된 디렉토리를 먼저 찾고 그 다음에 INSTALLED_APPS 항목에 등록된 각 앱의 templates 디렉토리를 찾는다는 점 유의

 

MVT 코딩 순서

모델, 뷰, 템플릿 중 정해진 순서는 없지만 독립적으로 개발할 수 있는 모델을 먼저 코딩하고 템플릿은 서로 영향을 미치므로 모델 이후에 같이 코딩하는 것이 일반적

  1. 프로젝트 뼈대 만들기 : 프로젝트 및 앱 개발에 필요한 디렉토리와 파일 생성
  2. 모델 코딩 : 테이블 관련 사항 개발(models.py, admin.py)
  3. URLconf 코딩 : URL 및 뷰 매핑 관게 정의(urls.py)
  4. 템플릿 코딩 : 화면 UI 개발(templates/디렉토리 하위의 .html 파일들)
  5. 뷰 코딩 : 애플리케이션 로직 개발(views.py)

// 내가 했던 개발 순서와 동일했다.


3.4 애플리케이션 설계하기

※ 만들어 볼 프로그램 ※
설문에 해당하는 질문을 보여준 후 질문에 포함되어 있는 답변 항목에 투표하면 그 결과를 알려주는 예제

1) UI 설계

  • index.html : 질문 리스트
  • detail.html : 질문에 대해 투표할 수 있도록 답변 항목 폼으로 제공
  • results.html : 질문에 따른 투표 결과

2) 테이블 설계

 

Quesion 테이블

컬럼명 타입 제약 조건 설명
id integer NotNull, PK, Autolncrement Primary Key
question_text varchar(200) NotNull 질문 문장
pub_date datetime NotNull 질문 생성 시각

Choice 테이블

컬럼명 타입 제약 조건 설명
id integer NotNull, PK, Autolncrement Primary Key
choice_text varchar(200) NotNull 답변 항목 문구
votes integer NotNull xnvy zkdnsxm
question integer NotNull, FK(Question.id), Index Foreign Key
  • Choice 테이블의 question 컬럼은 Question 테이블과 Foreign Key 관계로 연결되도록 했고, 장고는 ForeignKey인 경우 자동으로 Index를 생성함

3.5 프로젝트 뼈대 만들기

  • 프로젝트에 필요한 디렉토리 및 파일을 구성
  • 설정 파일을 셋팅
  • 기본 테이블 생성
  • 관리자 계정 슈퍼유저 생성

프로젝트가 만들어지면 그 하위에 애플리케이션 디렉토리 및 파일을 구성

장고는 이런 작업을 위한 장고 쉘 커맨드를 제공

 

[최종 프로젝트 체계]

프로젝트 뼈대의 최종 디렉토리 모습

이외에도 templates, static, logs 등의 디렉토리가 더 필요함

 

[해당 순서대로 명령 실행]

1) 프로젝트 생성

django-admin startproject mysite
  • 하위 mysite 디렉토리 : 프로젝트 디렉토리
    상위 mysite 디렉토리 : 프로젝트 관련 디렉토리/파일을 모으는 역할만 하는 디렉토리
  • 상위 mysite 디렉토리는 혼동을 피하기 위해 ch3라고 변경

2) 애플리케이션 생성

python manage.py startapp polls

3) 프로젝트 설정 파일 변경

settings.py 파일은 프로젝트의 전반적인 사항들을 설정해주는 곳으로, 루트 디렉토리를 포함한 각종 디렉토리 위치, 로그의 형식, 프로젝트에 포함된 애플리케이션의 이름 등이 지정되어 있음

 

3-1. ALLOWED_HOSTS 항목 지정

  • 장고는 DEBUG=True이면 개발 모드로, False이면 운영 모드로 인식
  • 운영 모드인 경우는 ALLOWED_HOSTS에 반드시 서버의 IP나 도메인을 지정해야 하고, 개발 모드일 경우 지정하지 않아도 [ 'localhost', '127.0.0.1' ] 로 간주

3-2. 프로젝트에 포함되는 애플리케이션들은 모두 설정 파일에 등록되어야 한다. (polls)

  • 애플리케이션 모델명이 polls만 등록해도 되지만 애플리케이션의 설정 클래스로 등록하는 것이 더 정확함

3-3. 프로젝트에 사용할 DB 엔진

  • 장고는 디폴트로 SQLite3 DB 엔진을 사용하도록 설정되어 있음

3-4. 타임존 지정

  • DEFAULT: 세계표준시(UTC) -> 한국 시간으로 변경

4) 기본 테이블 생성

python manage.py migrate
  • DB 테이블을 생성하진 않았지만 장고가 미리 정의해둔 사용자 및 그룹 테이블 등을 만들어주기 위해 프로젝트 개발 시작 시점에 이 명령을 실행하는 것

5) 지금까지 작업 확인하기

확인을 위해 웹 서버를 실행하고, 웹 서버에 접속

python manage.py runserver 0.0.0.0:8000
  • 명령 입력 : 자신의 서버에 맞는 IP 주소와 포트번호
  • 0.0.0.0 : IP 주소의 의미는 현재 명령을 실행 중인 서버의 IP 주소가 무엇으로 설정되어 있더라도 그와는 무관하게 웹 접속 요청을 받겠다는 의미
  • 즉, 웹 브라우저의 주소창에서는 runserver를 실행중인 서버의 실제 IP 주소를 입력하면 됨.
  • Ctrl + C : runserver 중지
※ runserver 실행 방법

> python manage.py runserver
디폴트: 127.0.0.1 8000
> python manage.py runserver 8888
포트번호만 지정시, 디폴트 127.0.0.1 주소 및 지정한 8888번 포트를 사용
> python manage.py runserver 0.0.0.0:8000 &
& 명령을 끝에 추가하면, 웹 서버 프로그램이 백그라운드에서 실행 됨(단, 리눅스, 맥만 가능)

 

6) 슈퍼 계정 생성하기

python manage.py createsuperuser

http://127.0.0.1:8000/admin 접속

  • 장고에서 만들어준 Users, Groups 테이블이 생성된 것을 확인


3.6 애플리케이션 개발 - Model 코딩

[테이블 정의]

polls 애플리케이션은 Question과 Choice 두 개의 테이블이 필요 함

테이블은 models.py 파일에 정의

  • PK는 클래스에 지정해주지 않아도 장고는 항상 PK에 대한 속성을 Not Null 및 Autoincrement로 이름은 id로 해서 자동으로 만들어 줌
  • FK는 항상 다른 테이블의 PK에 연결되므로, Question 클래스의 id 변수까지 지정할 필요 없이 Question 클래스만 지정하면 됨
  • __str__() 메소드객체를 문자열로 표현할 때 사용하는 함수로 Admin 사이트나 장고 쉘 등에서 테이블명을 보여줄 때, 이때 __str__() 메소드를 정의하지 않으면 테이블명이 제대로 표시되지 않음

[Admin 사이트에 테이블 반영]

models.py에서 정의한 테이블도 Admin 사이트에 보이도록 admin.py 파일에 등록

  • models.py 모듈에서 정의한 Question, Choice 클래스를 import하고 admin.site.register() 함수를 이용하여 임포트한 클래스를 Admin 사이트에 등록
  • 이와 같이 테이블을 새로 만들 때 models.py와 admin.py 두 개의 파일을 함꼐 수정해야 함

[데이터베이스 변경사항 반영]

테이블의 신규 생성, 테이블의 정의 변경 등 DB에 변경이 필요한 사항이 생기면 반영해주어야 함

※ migrate 사용 시 장고가 사용하는 SQL 문장을 확인하는 방법
> python manage.py sqlmigrate 테이블명 0001

3.7 애플리케이션 개발하기 - View 및 Template 코딩

polls 애플리케이션을 개발할 때 3개의 페이지가 필요했음

이 페이지를 보여주기 위해 필요한 뷰와 템플릿을 코딩하는 과정

  • URL과 뷰는 1:1 관계로(N:1) 매핑
  • 이러한 URL/뷰 매핑을 URLconf라고 하며 urls.py 파일에 작성
URL 패턴 뷰 이름 뷰가 처리하는 내용
/polls/ index() index.html 템플릿을 보여줌
/polls/5/ detail() detail.html 템플릿을 보여줌
/polls/5/vote/ vote() detail.html에 있는 폼을 POST 방식으로 처리
/polls/5/results/ results() results.html 템플릿을 보여줌
/admin/ (장고 기능) Admin 사이트를 보여줌(장고에서 기본으로 제공)

 

[코딩 순서]

로직의 흐름상 URLocnf를 먼저 코딩한 후에 뷰, 템플릿 또는 템플릿, 뷰 순서로 코딩하는 것이 일반적

  1. urls.py 작성 - URLconf 내용 코딩
  2. views.index() 함수 작성 - index.html 템플릿도 같이 작성
  3. views.detail() 함수 작성 - detail.html 템플릿도 같이 작성
  4. views.vote() 함수 작성 - 리다이렉션 처리 들어있음
  5. views.results() 함수 작성 - results.html 템플릿도 같이 작성

1) URLconf 코딩

Admin 사이트 포함 5개의 URL과 뷰가 필요

 

urls.py

urls.py

path() 함수는 route, view 2개의 필수 인자와 kwargs, name 2개의 선택 인자를 받음

  • route : URL 패턴을 표현하는 문자열 (=URL 스트링)
  • view : URL 스트링이 매칭되면 호출되는 뷰 함수. HttpRequest 객체와 URL 스트링에서 추출된 항목이 뷰 함수의 인자로 전달
  • kwargs : URL 스트링에서 추출된 항목 외에 추가적인 인자를 뷰 함수에 전달할 때, 파이썬 사전 타입으로 인자를 정의
  • name : 각 URL 패턴별로 이름을 붙임. 여기서 정해준 이름은 주로 템플릿 파일에서 사용

만일 URL이 /polls/3/ 이라면, URL 스트링에서 3이 추출되므로 뷰 함수를 호출 시 views.detail(request, question_id=3)처럼 인자가 대입 됨

 

URLconf를 코딩할 때 앞에서처럼 하나의 urls.py 파일에 작성할 수도 있고, 다음과 같이 mysite/urls.py와 polls/urls.py 2개의 파일에 작성할 수도 있음

mysite/urls.py
polls/urls.py

URLconf 모듈을 계층적으로 구성하는 것이 변경도 쉬워지고 확장도 용이해지기 때문에 더 좋음.

 

2) 뷰 함수 index() 및 템플릿 작성

UI 화면을 생각하면서 로직을 풀어나가는 것이 쉽기 때문에 뷰보다 템플릿을 먼저 코딩하는 것이 좋음

화면 UI 설계 - index.html
index.html

  • index.html을 작성하면서 필요한 변수가 무엇인지 찾아내야 함
  • 이 변수는 뷰 함수에서 context 변수로 정의해서 템플릿으로 넘겨줘야 하기 때문임
  • 예제의 템플릿에서는 질문으로 사용될 여러 개의 question_text를 화면에 보여줘야 하고 URL 링크를 연결하기 위해 question.id도 필요
  • 이 두 가지 정보가 함께 들어있는 Question 객체뷰 함수로부터 넘겨받으면 됨
  • Question 객체들의 리스트가 들어있는 latest_question_list 변수를 뷰 함수로부터 템플릿 파일로 전달해야 함

index() 함수

단축함수란?
웹 프로그램 개발 시 자주 사용되는 기능들, 예를 들어 render() 함수처럼 템플릿 코드를 로딩한 후에 컨텍스트 변수를 적용하고, 그 결과를 HTTPResponse 객체에 담아 반환하는 작업 등의 공통적으로 사용되는 기능들을 장고에서는 이미 개발하여 내장 함수로 제공하고 있음

[뷰에서 지정하는 템플릿의 위치]

애플리케이션 개수가 늘어나고 템플릿 파일이 많아지면 이름이 같은 경우가 발생할 수 있기 때문에 templates 디렉토리 하위에 다시 애플리케이션 명으로 디렉토리를 만들어 템플릿 파일을 위치시키는 방법을 사용함

 

3) 뷰 함수 detail() 및 폼 템플릿 작성

detail() 템플릿 작성

질문에 대한 답변 항목을 보여주고 투표하도록 하는 화면

detail.html
ch3/polls/templates/polls/detail.html

코드설명

{% if error_message %}
    <p><strong>{{ error_message }}</strong></p>
{% endif %}
  • 에러를 체크하는 로직은 다음 절의 vote() 뷰 함수에 있음
  • vote() 뷰 함수에서 익셉션이 발생하면 error_message를 담아 detail.html 템플릿을 렌더링하고, 그에 따라 지금의 detail.html 템플릿에서 에러 메시지를 보여줌
<form action="{% url 'polls:vote' question.id %}" method="post">
  • 서버 측에서 데이터를 변경하는 경우 일반적으로 POST 방식을 사용
  • < form action> 속성에 {% url %} 템플릿 태그를 사용하여 받을 곳의 URL을 polls:vote로 지정 함
  • polls:vote는 URLconf에서 정의한 URL 패턴 이름이다. 동일한 일므으로 인한 충돌을 막기 위해 이름 공간을 추가해서 polls:vote가 된 것
{% for choice in question.choice_set.all %}
  • for 태그로 뷰 함수에서 넘어온 객체를 순회
  • question.choice_set.all의 의미는 Question 객체의 choice_set 속성에 들어있는 항목 모두를 뜻 함.
<input type="radio" name="choice" id="choice{{ forloop.counter }}" \
               value="{{ choice.id }}" />
  • 라디오 버튼으로 답변 항목
  • 해당 라디오 버튼을 선택하면 POST 데이터가 'choice'='3' 형태로 구성되도록 name과 value 속성을 정의
<label for="choice{{ forloop.counter }}">{{ choice.choice_text }}</label>
  • forloop.counter : for 루프를 실행한 횟수를 담고 있는 변수
  • label for 속성과 input id 속성은 값이 같아야 서로 바인딩
<input type="submit" value="Vote" />
  • Vote 버튼을 클릭하면 사용자가 선택한 폼 데이터가 POST 방식으로 polls:vote URL로 전송 됨
  • 전송된 데이터는 vote() 뷰 함수에서 request.POST['choice'] 구문으로 엑세스 함
  • input 태그의 name과 value 속성값들이 request.POST 사전에 key, value로 사용된다는 점 유의

 

[Question 객체의 choice_set 속성]

Question과 Choice 테이블의 관계는 1:N 관계이고, 외래키로 연결되어 있음

즉, question.choice_set.all() 이라고 하면 Question 테이블의 question 레코드에 연결된 Choice 테이블의 레코드 모두를 뜻함

 

※ detail.html 파일에서 필요한 변수, 즉 detail() 뷰 함수에서 정의해야 할 context 변수의 종류
question.text, error_message, question.id, question.choice_set, forloop.counter, choice.id, choice.choice_text 등의 변수들이 사용되고 있음

  • forloop.counter : 장고에서 제공하므로 정의할 필요 없음
  • error_message : vote() 함수에서 정의할 예정
  • question.text, question.id, question.choice_set : 각각 context 변수로 정의해도 되지만 question 변수만 정의하면 그 변수의 속성으로 엑세스할 수 있음
  • choice.id, choice.choice_text : question.choice_set 변수가 정의되면 엑세스 가능
  • 결론적으로 detail() 뷰 함수에서 정의해야 할 변수는 question 변수 하나 뿐임

이렇게 html 파일을 먼저 코딩하면서 context 변수를 찾는 식으로 숙지하는 것이 좋음

 

detail() 뷰 함수 작성

detail() 함수

get_object_or_404() 란?
이 함수의 첫 번째 인자모델 클래스이고, 두 번째 인자부터 검색 조건을 여러 개 사용할 수 있음
이 예제에서는 Question 모델 클래스로부터 pk=question_id 검색 조건에 맞는 객체를 조회하고, 조건에 맞는 객체가 없으면 Http404 익셉션을 발생시킴

 

4) 뷰 함수 vote() 및 리다이렉션 작성

vote() 뷰 함수의 호출과 연계된 URL은 detail.html 템플릿 파일에서 받음

즉, detail.html 템플릿에 있는 폼을 제출하면 폼의 기능에 의해 /polls/5/vote/와 같은 URL이 POST 방식으로 넘어오게 됨

 

detail.html의 form 부분

<form action="{% url 'polls:vote' question.id %}" method="post">

urls.py의 vote 부분

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

위 두 라인에 의해 사용자가 Vote 버튼을 누르면 vote() 뷰 함수가 호출 됨

form으로부터 수신한 POST 데이터를 처리하는 vote() 뷰 함수를 views.py 파일에 작성 함

 

vote() 함수

  • vote() 뷰 함수가 반환하는 객체 : HttpResponseRedirect 객체
  • HttpResponseRedirect 클래스의 생성자는 리다이렉트할 타겟 URL을 인자로 받고 타겟 URL은 reverse() 함수로 만듬
  • 이처럼 POST 방식의 폼 데이터를 처리하는 경우, 그 결과를 보여줄 수 있는 페이지로 이동시키기 위해 HttpResponseRedirect 객체를 리턴하는 것이 일반적

reverse() 함수

  • URLconf는 일반적으로 URL 스트링과 뷰를 매핑한 각 라인을 URL 패턴이라 하고 이름을 하나씩 부여 함
  • 그런데 그 반대로 reverse() 함수를 사용하여 URL 패턴명으로부터 URL 스트링을 구할 수도 있음
  • reverse() 함수의 인자로 URL 패턴의 이름과 URL 스트링에 사용될 파라미터, 2개의 인자를 받음

  • reverse() 함수를 사용하여 URL을 구하는 것은 URLconf에 이미 정의된 URL 패턴을 활용해서 URL 스트링을 추출하는 방식이므로, 소스에 URL 스트링을 하드코딩하지 않도록 해줌

5) 뷰 함수 results() 및 템플릿 작성

리다이렉트하라는 응답을 받은 웹 브라우저는 리다이렉트 URL로 다시 요청을 보냄

 

urls.py의 results 부분

    path('<int:question_id>/results/', views.results, name='results'),
  • 리다이렉트 요청과 위 라인에 의해 results() 뷰 함수가 호출
  • results() 뷰 함수에 폼 데이터 처리 결과를 보여주는 로직을 작성

이번엔 '뷰' -> '템플릿' 순으로 코딩

 

views.py의 results() 뷰 함수

results() 함수

results.html UI

result.html
results.html

  • vote{{choice.voteslpluralize}} : choices.votes 값에 따라 복수 접미사(s)를 붙여주는 것
  • 결과적으로 choice.votes 값에 따라 vote 또는 votes가 표시 됨

※ 흥미로운 점

뷰 함수템플릿 태그 양쪽에서 모두 URL 스트링을 추출할 수 있다는 것

뷰 함수에서는 reverse() 함수를 사용하고 템플릿에서는 {% url %} 태그를 사용 함

{% url 'polls:detail' question.id %}           # 템플릿에서 사용됨
reverse('polls:detail', args=(question.id,))   # 뷰 함수에서 사용됨

 


 

// 그래..!! 이런 ORM같은 이미 쓰고있지만 따로 부르는 명칭은 몰랐던 개념을 알기 원했다. 

대부분 다 아는 내용이어서  쉽게 이해가 간다.

이 책에서는 항상 404를 넣어줬고 try-catch문을 사용해서 코드를 작성했다.

한눈에 보기에도 정리되어있는 듯해서 나도 저렇게 코딩스타일을 바꿔봐야 겠다.

redirect 부분도 나의 경우에는 redirect만 import한 후 명칭 그대로 적었는데 여기서는 reverse()를 사용해서 보다 긴 코드로 작성했다. 어떠한 차이점이 있는지 궁금하다.

 

반응형

댓글