[Django] 파이썬 웹 프로그래밍 - Django 웹 프레임워크 #1
< 3.4. 프로젝트 뼈대 만들기 >
-
프로젝트란 개발 대상이 되는 전체 프로그램을 의미하며,
프로젝트를 몇 개의 기능 그룹으로 나누었을 때, 프로젝트의 하위 서브 프로그램을 앱이라 말한다.
즉 서브 프로그램인 앱을 개발하고, 이들을 모아 프로젝트 개발을 완성하게 되는 것이다.
이런 개념으로 프로젝트 디렉토리와 앱 디렉토리를 구분하고,
코딩하는 파일도 프로젝트 파일인지 앱 파일인지 구분해서 적절한 위치에 저장해야 한다.
-
중요한 점은 하나의 앱이 여러 개의 프로젝트에 포함될 수 있기 때문에,
앱을 한 번만 개발하고 이를 다른 프로젝트에 재사용하여 개발의 생산성을 높일 수 있다.
앱 단위로 이들을 모아 프로젝트로 만들고,
프로젝트를 모아서 더 큰 프로젝트를 만드는 방식으로,
계층적인 웹 프로그램 개발이 가능하다는 장점이 있다.
-
프로젝트와 앱 모두 파이썬 패키지 디렉토리에 해당하는데, 파이썬에서는 __init__.py 파일이 존재하는 디렉토리를 패키지라고 한다.
-
기본 뼈대는 아래와 같다.
프로젝트가 완성된 후에는 templates, static, logs 등의 디렉토리가 더 필요하다.
-
root mysite 디렉토리 : 프로젝트 관련 디렉토리 및 파일을 모아주는 최상위 디렉토리. 보통 settings.py 파일의 BASE_DIR 항목으로 지정된다. ( 이 녀석은 특별한 의미를 가지지 않기 때문에 rename 해도 괜찮다. )
db.sqlite3 : SQLite3 DB 파일. db 디렉토리를 만들고 그 하위에 두기도 한다.
manage.py : 장고의 명령어를 처리하는 파일
mysite 디렉토리 : 프로젝트명으로 만들어진 디렉토리. 프로젝트 관련 파일들이 있다.
__init__.py : 패키지 인식
settings.py : 프로젝트 설정 파일
urls.py : 프로젝트 레벨의 URL 패턴을 정의하는 최상위 URLconf. 보통 하위 앱 디렉토리마다 urls.py 가 있다.
wsgi.py : Apache 와 같은 상용 웹 서버와 WSGI 규격으로 연동하기 위한 파일
polls 디렉토리 : 앱명으로 만들어진 디렉토리. 해당 앱 관련 파일이 들어 있다.
__init__.py
admin.py : Admin 사이트에 모델 클래스를 등록해주는 파일
migration 디렉토리: DB 변경사항을 관리하기 위한 디렉토리, DB 에 추가, 삭제, 변경 등이 발생하면 변경 내역을 기록한 파일들이 위치한다.
models.py : DB 모델 클래스를 정의
tests.py : unit test 용 파일
views.py : 뷰 함수를 정의하는 파일. 함수형 뷰 및 클래스형 뷰 모두 이 파일에 정의
-
뼈대를 만들기 위해서 다음의 순서로 명령을 실행한다.
$ django-admin.py startproject mysite
$ python manage.py startapp polls // polls 라는 앱을 생성
$ python manage.py migrate // db 변경사항 반영
$ python manage.py runserver // 현재까지 작업을 개발용 웹 서버로 확인
* 3.4.1. 프로젝트 생성
* 3.4.2. 앱 생성
* 3.4.3. 데이터베이스 변경사항 반영
-
DB 를 만들지도 않았는데 python manage.py migrate 를 호출해주는 이유는..
장고는 모든 웹 프로젝트 개발 시 반드시 사용자와 사용자의 그룹 테이블이 필요하다고 가정하고 설계되었다.
따라서 우리가 테이블을 만들지 않았더라도, 사용자 및 사용자 그룹 테이블을 만들어주기 위해 이 명령을 실행해야 한다.
명령을 실행하면 migrate 명령에 대한 로그가 보이고, 실행 결과로 SQLite3 DB 파일인 db.sqlite3 파일이 생성된다.
* 3.4.4. 지금까지 작업 확인하기
-
runserver 에 주소와 포트를 지정하지 않으면 기본으로 127.0.0.1 주소 및 8000 포트를 사용한다.
port 만 지정할 수도 있다.
0.0.0.0 은 현재 명령을 실행중인 서버의 IP 주소가 무엇으로 설정되어 있더라도 그와는 무관하게 웹 접속 요청을 받겠다는 의미. 즉, 외부에서 서버의 IP 주소를 브라우저의 주소창에 입력하여 접속하는 것을 가능하게 해준다.
마지막에 & 를 붙이면 자동으로 bg 에서 실행한다.
-
host/admin 으로 접근하면 admin page 로 이동한다.
user 생성은 아래와 같이 한다.
$ python manage.py createsuperuser
// user name, password 설정이 진행된다.
-
admin 화면에서 기본적으로 User 와 Groups 테이블이 보이는 것은 이미 settings.py 파일에 django.contrib.auth 앱이 등록되어 있기 때문이다.
< 3.5. 앱 개발하기 - 설계 >
< 3.6. 앱 개발하기 - Model 코딩 >
* 3.6.1. 데이터베이스 지정
-
장고는 디폴트로 SQLite3 을 사용한다.
만약 MySQL 이나 Oracle 등 다른 db 로 변경하고 싶다면 settings.py 를 수정해주면 된다.
-
settings.py 설정 파일은 프로젝트의 전반적인 사항들을 설정해주는 곳으로,
루트 디렉토리를 포함한 각종 디렉토리의 위치,
로그의 형식,
프로젝트에 포함된 앱 등이 지정되어 있어 그 내용에 익숙해지는 것이 좋다.
-
프로젝트에 포함되는 앱들은 모두 설정 파일에 지정되어야 한다.
INSTALLED_APPS 에 새로운 앱을 추가해주어야 한다.
-
TIME_ZONE 항목의 Default 는 UTC 인데 이를 바꿀 수 있다.
TIME_ZONE = ‘Asia/Seoul'
* 3.6.2. 테이블 정의
-
from django.db import models
class Question(models.Model): # question_id 라는 PK 가 자동으로 생긴다.
question_text = modesl.CharField(max_length=200) # varchar(200)
pub_date = models.DateTimeFiled(‘date published’) # datetime
class Choice(models.Model): # choice_id 라는 PK 가 자동으로 생긴다.
question = models.ForeignKey(Question) #question_id 로 접근
choice_text = models.CharField(max_length=200)
votes = models.IntegerField(default=0)
-
PK(Primary Key)는 클래스에 지정해주지 않아도, 장고는 항상 PK 에 대한 속성을 Not Null 및 Autoincrement 로, 변수 이름은 TableName_id 로 자동으로 만들어준다.
-
DateTimeField() 에 정의하는 문구는 column 에 대한 label 문구이다.
Admin 사이트에서 보인다.
-
FK(Foreign Key)는 항상 다른 테이블의 PK 에 연결되므로, 클래스 이름만 지정하면 된다.
-
__unicode()__ 함수는 객체를 스트링으로 표현할 때 사용하는 함수이다.
__unicode__() 함수를 정의하지 않으면 테이블명이 제대로 표시되지 않는다.
Python3 에서는 __str__() 함수로 변경되었다.
* 3.6.3. Admin 사이트에 테이블 반영
-
새로 생성한 테이블을 보이게 하려면 admin.py 에 해당 table 을 추가하면 된다.
admin.site.register() 함수를 사용하여 등록해주면 된다.
즉 테이블을 추가할 때는 models.py 와 admin.py 두개를 함께 수정한다는걸 기억해야 한다.
ex)
from django.contrib import admin
from polls.models import Question, Choice
admin.site.register(Question)
admin.site.register(Choice)
* 3.6.4. 데이터베이스 변경사항 반영
-
테이블의 신규 생성, 테이블의 정의 변경 등 db 변경이 필요한 사항이 있으면, 이를 db 에 실제 반영해주는 작업을 해야 한다.
이는 아래와 같이 한다.
$ python manage.py makemigrations # 앱dir/migrations 폴더가 생기고 그곳에 migration 코드가 생성된다.
$ python manage.py migrate
$ python manage.py sqlimigrate polls 0001 # migrate 명령으로 데이터베이스를 반영할 때 장고가 사용하는 SQL 문을 볼 수 있다.
* 3.6.5. 지금까지 작업 확인하기
< 3.7. 앱 개발하기 - View 및 Template 코딩 >
-
장고에서 URL 과 뷰는 항상 1:1 관계로 매핑이 된다.
URL/View Mapping 은 URLconf 라고 하며 urls.py 파일에 작성한다.
-
url -> view -> template
/polls/ -> index() -> index.html
/polls/5 -> detail() -> detail.html
/polls/5/vote/ -> vote() -> detail.html 에 있는 폼을 POST 방식으로 처리한다.
/polls/5/results/ -> results() -> results.html
from django.conf.urls import patterns, url
from polls import views
urlpatterns = patterns( '',
url(r'^polls/$', views.index, name='index'),
url(r'^polls/(?P<question_id>\d+)/$', views.detail, name='detail'),
url(r'^polls/(?P<question_id>\d+)/vote/$', views.vote, name='vote'),
url(r'^polls/(?P<question_id>\d+)/results/$', views.results, name='results'),
url(r'^admin/', include(admin.site.urls)),
)
* 3.7.1. URLconf 코딩
-
url( regex, view, kwargs=None, name=None, prefix=‘')
view : regex 인자에 매칭되면 호출하는 함수. 뷰 함수에는 HttpRequest와 regex 에서 추출한 항목을 인자로 넘겨준다.
kwargs : regex 인자에서 추출한 파라미터 외에 추가적인 인자를 파이썬 사전 타입의 키워드 인자로 넘겨준다.
name : URL 별로 이름을 붙여준다. 여기서 정해준 이름이 템플릿 파일에서 사용된다.
prefix : 뷰 함수에 대한 접두문자열이다.
-
url(r’^polls/(?P<question_id>\d+)/$’, views.detail, name=‘detail’)
위는 polls/5 와 같은 URL 이 들어오면 matching 되며,
?P<question_id> 라고 명시해준 것은 그 다음에 오는 것을 question_id=(\d+의 값) 형태로 전달한다.
즉, views.detail(request, question_id=‘5’) 형태로 호출이 된다.
-
settings.py 의 ROOT_URLCONF 항목에 정의된 urls.py 파일을 가장 먼저 분석하기 시작한다.
-
URLconf 를 코딩할 때 urls.py 에 직접 코딩할 수도 있고,
appName/urls.py 와 같이 분리한 후 include 시키는 방법도 있다.
url(‘r^polls/‘, include(‘polls.urls’, namespace=“polls”)), # namespace 는 중복을 막기 위함으로 polls:detail 과 같은 형태가 된다.
그리고 이 방식이 추천되는 방식이다.
* 3.7.2. 뷰 함수 index() 및 템플릿 작성
-
template 을 작성하는 위치는..
appDir/templates/appDir 에 .html 형태로 작성한다.
templates 아래 appDir 을 다시 생성하는 이유는..
앱의 갯수가 늘어나고 템플릿 파일이 많아질 경우 앱은 다르지만 템플릿 파일 이름이 같은 경우가 발생하기 때문에 사용하는 관습.
-
template 은 템플릿 언어인 {% if %}, {% for %} 태그 등을 사용한다.
-
views.py 에는 매핑 함수를 구현해준다.
ex)
def index(request):
latest_question_list = Question.objects.all().order_by(‘-pub_date’)[:5] # - 는 역순정렬
context = {‘latest_question_list’:latest_question_list}
return render(request, ‘polls/index.html’, context) # render 를 하면 HttpResponse 가 생긴다.
* 3.7.3. 뷰 함수 detail() 및 폼 템플릿 작성
-
<!-- detail.html -->
<h1>{{ question.question_text }}</h1>
{% if error_message %} <p><strong> {{ error_message }} </strong></p> {% endif %}
<form action="{% url 'polls:vote' question.id %}" method="post">
{% csrf_token %}
{% for choice in question.choice_set.all %}
<input type="radio" name="choice" id="choice{{ forloop.counter }}" value="{{ choice.id }}" />
<label for="choice{{ forloop.counter }}">{{ choice.choice_text }}</label><br />
{% endfor %}
<input type="submit" value="Vote" />
</form>
-
template 에서 사용하는 코드에 에러가 있다면 error_message 라는 변수에 값이 담긴다.
-
{% url %} 에 polls:vote 와 같이 명시해주면, polls 를 namespace 로 갖는 name 이 vote 인 URLconf 에 지정된 view 가 호출된다.
{% url ‘polls:vote’ question.id %} 와 같이 하면 결국 매핑된 형태는 /polls/{question.id}/vote 가 된다.
-
form 을 처리하는 경우, 보안 측면에서 CSRF( Cross Site Request Forgery ) 공격을 주의해야 한다.
장고는 이를 방지하기 위한 기능을 제공하는데, {% csrf_token %} 탬플릿 태그를 명시만 해주면 이를 방지해준다.
CSRF 는 사용자가 보안이 취약한 사이트에 로그인해 있는 상태에서, 악의적인 사이트를 접속하는 경우(혹은 그곳에서 어떤 작업을 할 경우) 로그인된 취약한 사이트에 어떤 작업( CRUD ) 을 하는 공격 방법.
대표적인 예가 로그인 상태를 쿠키에 보관하는 경우, 악의적인 사이트에서 해당 쿠키를 이용하는 것이다.
-
두 개의 테이블 관계가 1:N 관계이고, FK 로 연결되어 있다면..
1 테이블에 연결된 N 테이블의 항목들이라는 의미로, xxx_set 속성을 사용할 수 있다.
1table.ntable_set 이란 property 로 접근 가능하다.
ex) question.choice_set
-
# views.py
from django.shortcuts import get_object_or_404, render
from polls.models import Question
# ... index()
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 는 단축함수로,
첫번째 Model 로부터 뒤에 따라붙는 조건의 object 를 찾아오고, 찾지 못하면 Http404 Exception 이 발생한다.
-
대상 객체를 리스트로 가져오는 get_list_or_404() 단축함수도 있다.
* 3.7.4. 뷰 함수 vote() 및 리다이렉션 작성
-
# views.py
#... index()
# ... detail()
def vote(request, question_id):
p = get_object_or_404(Question, pk=question_id)
try:
selected_choice = p.choice_set.get(pk=request.POST['choice'])
except (KeyError, Choice.DoesNotExist):
return render(request, 'polls/detail.html', { 'question' : p, 'error_message' : "You didn't select a choice.", })
else:
selected_choice.votes += 1
selected_choice.save()
return HttpResponseRedirect(reverse('polls:results', args=(p, question_id,)))
-
request.POST 는 제출된 폼의 데이터를 담고 있는 객체이다.
dictionary 형태이다.
ex) request.POST[‘choice’] # 키가 없으면 KeyError Exception 이 발생한다.
-
get 함수는 해당하는 조건이 없으면 Model.DoesNotExist Exception 이 발생한다.
-
model 에 operation 한것을 db 저장하려면 save() 함수를 호출해준다.
-
POST 방식의 폼 데이터 처리의 경우, 그 결과를 보여줄 수 있는 페이지로 이동시키기 위해 HttpResponseRedirect 객체를 리턴하는 것일 일반적이다.
-
reverse() 함수는 URL 패턴으로부터 URL 스트링을 구할 수 있는 것이다.
(URLconf 는 URL 스트링으로부터 URL 패턴을 구한다.)
reverse(patternName, parameters)
reverse 는 URLconf 에 미리 정의된 URL 패턴에서 URL 스트링을 추출하는 방식이라 유연성이 좋다.
그러므로 하드코딩을 피하고 reverse 를 쓰는 것이 추천된다.
* 3.7.5. 뷰 함수 results() 및 템플릿 작성
-
{{ choice.votes | pluralize }} 와 같은 방식으로 사용하면, choice.votes 의 단수 복수 상태에 따라 s 를 붙이거나 안 붙이거나 한다.
-
템플릿의 url 태그는 view 의 reverse 와 동일한 역할을 한다.
* 3.7.6. 지금까지 작업 확인하기
'프로그래밍 놀이터 > Script(Python)' 카테고리의 다른 글
[Django] 파이썬 웹 프로그래밍 - Django 의 핵심 기능 #2 (0) | 2016.12.15 |
---|---|
[Django] 파이썬 웹 프로그래밍 - Django 의 핵심 기능 #1 (0) | 2016.12.13 |
[Django] 파이썬 웹 프로그래밍 - Django 웹 프레임워크 #1 (0) | 2016.12.09 |
[Django] 파이썬 웹 프로그래밍 - 파이썬 웹 표준 라이브러리 (0) | 2016.12.08 |
[Django] 파이썬 웹 프로그래밍 - 웹 프로그래밍의 이해 (0) | 2016.12.06 |
댓글