예제로 배우는 python 프로그래밍 (Django 기초: http://pythonstudy.xyz/Python/Django)
Django 소개
-----------
Django는 웹어플프레임웍(web app framework)의 하나이다. 파이썬으로 개발되었고 오픈소스이다.
가상 환경 구축
----------------
Django 설치
-----------
>pip install django
>pip install django=1.8 #특정버전 지정
Django 프로젝트 생성과 실행
---------------
>django-admin startproject myweb #새 프로젝 만듬
>python manage.py runserver #서버 실행
서버실행 시 127.0.0.1:8000 또는 8080에 접속.
Django App
----------
프로젝 하에 위치하며, 하나의 프로젝은 여러개 App을 포함할 수 있다. 잘 모듈화된 App은 여러 프로젝에서 재 사용 가능.
>manage.py startapp home # home이라는 새 App생성
views.py는 html 웹을 호출하는 역할을 한다.
간단하게 화면에 글자를 출력해 보자.
from django.shortcuts import render
from django.http import HttpResponse
# Create your views here.
def index(request):
return HttpResponse("Hello, World!")
01.settings.py: INSTALLED_APPS 리스트에 App 추가
02.urls.py: urlpatterns 리스트에 사용할 URL 패턴 추가. 또 ^는 시작, $는 끝이므로 빈 문자열, 즉, 루트를 가리킴.
Django 뷰
---------
보통 GUI는 MVC framework인데 여기서는 MTV이다. (Model,View, Controller) == (Model, Templates, View)
장고 뷰는 HTTP Request를 받아, HTTP Response를 리턴.
Request에는 (문서) Model 데이터가 들어 있으며, Response에서는 Template html을 생성하거나 호출.
Django 템플릿
-------------
View에서 이쪽으로 전달된 데이터를 받아, 동적인 웹페이지를 만든다.
App폴더 아래에 templates 폴더 만들고 그아래 html을 위치.
여러개의 App이 한 프로젝 아래에 있는 경우에는
"App폴더/templates/App명/템플릿파일"
로 한다. 복수의 App들이 동일 이름 템플릿을 가진 경우에 대비: 템플릿을 찾을 때, 자신 App아래 템플릿을 먼저 찾는 것이 아니라, 전체 App들 템플릿 폴더들을 처음부터 순서대로 찾는다.
from django.shortcuts import render
def index(request):
msg = 'My Message'
return render(request, 'index.html', {'message': msg})
render함수는 index.html을 호출하며, 세번째 파라메터는 dict형태로 넘겨줌.
(템플릿 언어)
.변수: {{ }}로 둘러싸이면 변수 값이 이 위치에 치환.
.템플릿 태그: {%...%}. if, for와 같은 python명령이 내부에 온다.
{% if count > 0 %}
Data Count = {{ count }}
{% else %}
No Data
{% endif %}
{% for item in dataList %}
<li>{{ item.name }}</li>
{% endfor %}
{% csrf_token %} # 해킹 공격에 대응
.템플릿 필터: 변수값을 특정 포맷으로 변형.
날짜 포맷 지정
{{ createDate|date:"Y-m-d" }}
소문자로 변경
{{ lastName|lower }}
.템플릿에서 주석
싱글 라인: {#...#}
여러 라인: {% comment %} ... {% endcomment %}
Django 모델
-----------
필드 타입: CharField, TextField, FileField, ...
ImageField(FileField의 파생클래스로 이미지 파일인지 체크)
필드 옵션
설명
.null (Field.null): Empty 값을 DB에 NULL로 저장. DB에 Null 허용.
예: models.IntegerField(null=True)
.blank (Field.blank): blank=False(Required 필드). blank=True(Optional 필드).
예: models.DateTimeField(blank=True)
.primary_key(Field.primary_key): 해당 필드가 Primary Key.
예: models.CharField(max_length=10, primary_key=True)
.unique (Field.unique): 해당 필드가 테이블에서 Unique함 표시. 해당 컬럼에 대해 Unique Index를 생성.
예: models.IntegerField(unique=True)
.default (Field.default): 필드 디폴트값을 지정.
예: models.CharField(max_length=2, default="WA")
.db_column (Field.db_column): 컬럼명은 디폴트로 필드명을 사용하는데, 만약 다르게 쓸 경우 지정한다.
DB 설정과 Migration
-------------------
Model클래스 생성 후, 해당 모델 테이블(틀)을 DB에 생성 가능.
> manage.py makemigrations # migration을 준비하는 과정. 스키마 생성 또는 수정. App내에 migrations서브폴더 생성.
> manage.py migrate # migration을 DB에 적용하는 과정
migrate후에 생기는 파일 명: App명_ModelClass명
DB 관리 쉘
> manage.py dbshell #테이블, 컬럼정보, 테이블 내용 확인 가능
sqlite> .tables #테이블 리스트 출력
sqlite> PRAGMA table_info(리스트중하나) # 테이블 컬럼정보
sqlite> select * from 리스트중하나 # (이미 입력 저장된) 테이블 데이터
Django 모델 API
---------------
일단 모델 클래스가 정의되면 장고는 데이터추가, 갱신에 관련된 다양한 API를 제공
(INSERT)
from feedback.models import * # feedback(App명)/models.py
from datetime import datetime
# Feedback 객체 생성
fb = Feedback(name = 'Kim', email = 'kim@test.com', comment='Hi', createDate=datetime.now())
# 새 객체 INSERT
fb.save()
(SELECT)
장고는 모델 클래스에 objects라는 관리 객체를 자동 추가해 줌. 이를 통해 여러 제어 가능.
"모델클래스명.objects"로 사용함.
all(): 테이블 데이터 전부 가져옴
for f in Feedback.objects.all():
s += str(f.id) + ' : ' + f.name + '\n'
get(): 하나의 row만 가져옴. pk(primary key)를 사용.
row = Feedback.objects.get(pk=1)
print(row.name)
filter(): 특정조건에 맞는 row가져옴
rows = Feedback.objects.filter(name='Kim')
exclude(): 특정 조건을 제외한 나머지 row가져옴
rows = Feedback.objects.exclude(name='Kim')
count(): 데이터 갯수
n = Feedback.objects.count()
order_by(): 키를 따라 정렬. -는 내림차순.
rows = Feedback.objects.order_by('id', '-createData') #id를 기준으로 올림차순, createData를 기준으로 내림차순 정렬
distinct(): 중복 시 하나만 표시.
rows = Feedback.objects.distinct('name')
first(): 처음 row만 리턴.
rows = Feedback.objects.order_by('name').first() #정렬 후 첫 요소 리턴
last(): 마지막 row 리턴
rows = Feedback.objects.order_by('name').last()
체인연결 가능
row = Feedback.objects.filter(name='Kim').order_by('-id').first()
(UPDATE)
수정할 객체 얻고 변경 필드 수정. 다시 저장.
fb = Feedback.objects.get(pk=1)
fb.name = 'Park'
fb.save()
(DELETE)
대상 row얻은 후 delete호출
fb = Feedback.objects.get(pk=2)
fb.delete()
URL 매핑
--------
어떤 프로젝 아래에 App이 여러개일 때, proj 아래 urls.py 파일에 각 App의 urls.py를 매핑하면 편리.
myWeb/urls.py
urlpatterns = [
...
url(r'^feedback/', include('feedback.urls')), #'feedback/': App별 기준 url위치.
url(r'^home/', include('home.urls')),
]
# home/urls.py
from django.conf.urls import url
from home import views
urlpatterns = [
url(r'^contact', views.contact), #url: /home/contact, 함수:home.views.contact
url(r'^about', views.about),
]
# feedback/urls.py
from django.conf.urls import url
from feedback import views
urlpatterns = [
url(r'^list', views.list), #url: /feedback/list, 함수:feedback.views.list
url(r'^add', views.add),
url(r'^update', views.update),
]
RegEx사용. r'정규표현식'형식이고, 앞에 r(raw) prepix붙임. ^로 시작.
(..): Capture 표현. 일부 문자열 추출.
url(r'^blogs/([0-9]{4})/([0-9]{2})/$$', views.blogs_month) #4개/2개 추출
만약 입력 URL이 "/blogs/2015/12" 이면, 이 URL은 views.blogs_month(request, '2015', '12') 과 같이 함수를 호출
Named Group: (?P<그룹이름>pattern). 캡춰한 것에 이름붙여 전달.
url(r'^blogs/(?P<year>[0-9]{4})/(?P<month>[0-9]{2})/$', views.blogs_month)
입력 URL이 "/blogs/2015/12" 이면, 이 URL은 views.blogs_month(request, year='2015', month='12') 과 같이 함수를 호출
템플릿 확장
-----------
아래 두 파일 경우, 일단 base.html이라는 파일을 생성하고, 이 내부에서 block위치를 찾는다. 그리고, 그 위치에 index.html에 있는
<h1>{{message}}</h1>
을 치환한다.
(base.html)
<!DOCTYPE html>
<html lang="en">
<head>
...
</head>
<body>
....
{% block content %} # 삭제 후 index.html에 있는
{% endblock content %} # <h1>{{message}}</h1> 부분 대입
</body>
</html>
(index.html)
{% extends "base.html" %} #첫 라인에 base.html 추가
{% block content %}
<h1>{{message}}</h1>
{% endblock content %}
Django 폼(form)
---------------
모델 클래스로부터 폼을 자동으로 생성하는 기능.
from django.forms import ModelForm
from .models import Feedback
class FeedbackForm(ModelForm):
class Meta:
model = Feedback
fields = ['id', 'name','email','comment'] #모델 필드중 일부만 폼에서 사용.
이렇게 정의 후, 뷰에서 이 폼을 사용한다.
from django.shortcuts import render, redirect
from .models import *
from .forms import FeedbackForm
def create(request):
if request.method=='POST': # 뭐가 전달되는지 모르지만 어떤 객체가 전달된 경우를 표현
form = FeedbackForm(request.POST) #사용자가 입력한 것을 인자로 넘겨줘서 폼 생성.
if form.is_valid():
form.save()
return redirect('/feedback/list') #urls.py의 urlpatterns을 통해 view함수 호출.
else:
form = FeedbackForm()
return render(request, 'feedback.html', {'form': form}) #세번째 인자는 dict()
위 코드는
. 데이터를 입력받을 폼을 보여주는 부분
. 사용자가 데이터 입력하여 저장 버턴 클릭 시, 이를 DB에 저장하는 부분.
. view에서 html로 form을 전달하였다. 그래서, 아래 html에서 이를 랜드링.
{% extends "base.html" %}
{% block content %}
<p>
<a href="{% url 'list' %}">Goto Feedback List</a>
</p>
<div>
<form method="POST"> # 뷰에서 form을 넘겨주었었다.
{% csrf_token %}
{{ form.as_p }} #p태그(paragraph), 즉 새 문단으로 form 연다.
<button type="submit">저장</button>
</form>
</div>
{% endblock content %}
Static 파일
-----------
장고홈(settings.py 내 BASE_DIR) 아래에 static 폴더를 만들어 js, css, image 등 웹에서 사용하는 파일 저장:
STATIC_URL = '/static/' #웹에서 주소로 살펴볼 때
STATICFILES_DIRS=[os.path.join(BASE_DIR, 'static'),]
.html에서 static 파일 사용 시는 템플릿 상단에
{% load staticfiles%}
를 명시하고, 실제 static file을 가리키기 위해서는 link태그로 지정.
{% static 'bootstrap/css/bootstrap.min.css' %}
.배포(deploy) 시에는 각 App의 static 폴더 아래 파일을 그대로 복사하므로 이름 충돌을 피하기 위해 다음 처럼 한다:
-App명/static/App명 #각 App하의 static 폴더명 지정 시.
-settings.py에 다음 지정.
STATIC_ROOT = '/var/www/myweb_static' #배포용 web 경로하의 static 폴더.
STATICFILES_FINDERS = (
'django.contrib.staticfiles.finders.FileSystemFinder',
'django.contrib.staticfiles.finders.AppDirectoriesFinder',
)
.collectstatic
배포 시 흩어져 있는 static 파일 들을 모아 특정 폴더로 옮기는데, 이 때,
'manage.py collectstatic' 실행.
샘플 Feedback App
-----------
Site Deployment (Nginx)
-----------
Site Deployment (Apache)
-----------
Django 디버깅
[참고]
1.
Django 기초.