와 같이 fileversion을 구하는 함수를 공유합니다.
fileversion은 숫자가 4개로 구성되는데, 각 숫자값을 전달해 줍니다.
// version.lib import
#include <assert.h>
#include <winver.h>
DWORD GetVersion(IN LPCTSTR lpszFilePath, OUT PWORD pwVer1, OUT PWORD pwVer2, OUT PWORD pwVer3, OUT PWORD pwVer4)
{
DWORD dwRtnValue = ERROR_SUCCESS;
DWORD dwHandle = 0;
DWORD dwDataSize = 0;
LPBYTE bufVersionData = NULL;
TCHAR szValue[MAX_PATH] = {0,};
LPVOID pData = NULL;
UINT nQuerySize = 0;
VS_FIXEDFILEINFO* pVsffi = NULL;
if ((NULL == lpszFilePath) || (NULL == pwVer1) || (NULL == pwVer2) || (NULL == pwVer3) || (NULL == pwVer4))
{
dwRtnValue = ERROR_INVALID_PARAMETER;
assert(FALSE);
goto FINAL;
}
// Clear output
*pwVer1 = 0;
*pwVer2 = 0;
*pwVer3 = 0;
*pwVer4 = 0;
dwDataSize = ::GetFileVersionInfoSize(lpszFilePath, &dwHandle);
if (0 == dwDataSize)
{
dwRtnValue = ::GetLastError();
assert(FALSE);
goto FINAL;
}
bufVersionData = new BYTE[dwDataSize];
if (NULL == bufVersionData)
{
dwRtnValue = ERROR_NOT_ENOUGH_MEMORY;
assert(FALSE);
goto FINAL;
}
ZeroMemory(bufVersionData, sizeof(BYTE)*dwDataSize);
if (FALSE == ::GetFileVersionInfo(lpszFilePath,
dwHandle,
dwDataSize,
bufVersionData))
{
dwRtnValue = ::GetLastError();
assert(FALSE);
goto FINAL;
}
if (FALSE == ::VerQueryValue(bufVersionData,
TEXT("\\"),
(LPVOID*)&pVsffi,
&nQuerySize))
{
dwRtnValue = ::GetLastError();
assert(FALSE);
goto FINAL;
}
*pwVer1 = HIWORD(pVsffi->dwFileVersionMS);
*pwVer2 = LOWORD(pVsffi->dwFileVersionMS);
*pwVer3 = HIWORD(pVsffi->dwFileVersionLS);
*pwVer4 = LOWORD(pVsffi->dwFileVersionLS);
FINAL:
if (NULL != bufVersionData)
{
delete [] bufVersionData;
bufVersionData = NULL;
}
return dwRtnValue;
}
'프로그래밍 > Let's Share it' 카테고리의 다른 글
| PE 포맷 바이너리 파일의 버전을 구하는 함수 (0) | 2012/04/30 |
|---|---|
| 프로세스 생성 종결자 (ShellExecute / CreateProcess) (0) | 2012/03/23 |
| 손쉽게 File을 나열할 수 있는 class 공유(FindFirstFile/FindNextFile Wrapper) (0) | 2012/03/23 |
| VC9.0에서 crypto++ library를 이용하여 SHA256(sha2) 구하기 (0) | 2011/12/14 |
| 비동기(OVERLAPPED I/O, IOCP, Thread Pool) IPC PIPE Class 공유 (0) | 2011/12/14 |
| [ATL/MFC/C++] Ini Parser(ini 파서) (0) | 2011/11/30 |
참고 : http://www.djangobook.com/en/2.0/chapter06/
web site의 특정 계층을 위한 admin interface(관리자 인터페이스)는 infrastructure의 필수 부분으로 자리잡았다. 이는 web 기반의 interface이고, 신뢰된 site 관리자로 제한하며, 그는 site content를 추가, 편집 그리고 삭제가 가능하다. 몇몇 일반적인 예로, blog에 글을 post 할때 쓰이는 interface, 적절한 사용자 생성의 댓글을 사용하는데 쓰이는 백엔드 site 관리자, 그리고 당신의 고객이 생성한 web site를 릴리즈 업데이트하는데 사용되는 도구등이 해당된다.
admin interface는 만드는데 지루하다는 문제가 있다. web 개발은 공개적인 기능을 개발할 때 즐거운데, admin interface는 늘 같은 내용이다. 사용자 인증, form을 표시하고 처리하기, 입력 검증, 기타 등등을 해야 한다. 이는 지겹고 반복적이다.
그래서 django에서는 이러한 지루하고 반복잡업에 대해 어떻게 접근할까? 단지 두세줄의 code 혹은 없이도 가능하게 한다. django로 admin interface를 만드는것은 해결된 문제이다.
본 장은 django의 자동 admin interface에 대해 다루기로 한다. model에 있는 metadata를 읽어 기능이 동작하게 되는데, 이는 site 관리자가 즉각 시작할 수 있는 강력하고 생산성이 준비된 interface를 제공하기 위함이다. 여기서 우리는 이 기능을 어떻게 활성화, 사용, 그리고 커스텀할지를 알려준다.
우리는 당신이 django의 admin site를 사용하지 않더라도 본 chapter를 읽기를 권장한다. 왜냐하면, admin-site 사용법과 상관없이 django의 모든것을 적용할 수 있는 몇몇 개념이 소개되기 때문이다.
django.contrib package
django의 자동 admin은 django.contrib이라 불리는 django 기능의 한 부분인데, 이는 code framework에 많은 유용한 add-on이 포함되어 있다. django.contrib을 python 표준 라이브러리와 동일하게 생각하면 된다. 당신의 application에서 다시 개발할 필요가 없도록 django에서 묶어놓은 것이다.
admin site는 django.contrib의 첫 부분인데, 이 책에서 다루고 있다. 기술적으로 django.contrib.admin으로 불려진다. django.contrib의 다른 가능한 기능은 사용자 인증 시스템(django.contrib.auth), 세션 지원(django.contrib.sessions), 그리고 사용자 코멘트(django.contrib.comments)등을 지원한다. 다양한 django.contrib 기능을 알고 있다는것은 당신을 django 전문가로 만들게 할 것이고, chapter 16에서 자세히 설명할 것이다. 지금은 django는 많은 훌륭한 add-on이 있고, 그들은 django.contrib에 있다는 것만 알고 있자.
admin interface 활성화 하기
django admin site는 옵션사항인데, 단지 몇개의 특정 종류의 site만 사용하기 때문이다. 이는 당신 project에 그것을 활성화하는데 단지 몇개의 과정만 필요하다는것을 의미한다.
처음으로, setting 파일에 다음 사항을 반영한다.
- INSTALLED_APPS에 'django.contrib.admin'를 추가한다. (추가할때, 그 순서는 중요하지 않으나, 알파벳 순서를 지키는것이 가독성에 좋다.)
- INSTALLED_APPS에 'django.contrib.auth', 'django.contrib.contenttypes', 그리고 'django.contrib.sessions'가 있는지 확인한다. django admin site는 이 3개의 package를 필요로한다.
(이후에 설명될 "admin site에 model 추가하기"를 위해 'mysite.books'가 필요할 지 모른다) - MIDDLEWARE_CLASSES에 'django.middleware.common.CommonMiddleware', 'django.contrib.sessions.middleware.SessionMiddleware', 'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware'가 있는지 확인한다.
settings.py는 대략 아래와 같을 것이다.
...
MIDDLEWARE_CLASSES = (
'django.middleware.common.CommonMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
# Uncomment the next line for simple clickjacking protection:
# 'django.middleware.clickjacking.XFrameOptionsMiddleware',
)
...
INSTALLED_APPS = (
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
#'django.contrib.sites',
#'django.contrib.messages',
#'django.contrib.staticfiles',
# Uncomment the next line to enable the admin:
#'django.contrib.admin',
# Uncomment the next line to enable admin documentation:
# 'django.contrib.admindocs', # 아래 "admin site에 model 추가하기"에 의해 추가됨
#'mysite.books'
)
...두번째로 python manage.py syncdb를 실행한다. 이 step은 admin interface가 사용할 database table을 추가적으로 설치한다. syncdb를 INSTALLED_APPS에 있는 'django.contrib.auth'로 실행하면 superuser 생성에 대해 질문을 받게 된다. 만약 이를 수행하지 않는다면, admin user 계정을 생성하기 위해 python manage.py createsuperuser를 실행해야 한다. 그렇지 안핟면, admin site에 로그인할 수 없다.
|
C:\mydjango\mysite>python manage.py syncdb You just installed Django's auth system, which means you don't have any superuse C:\mydjango\mysite |
세번째로, URLconf(urls.py)에 admin site를 추가한다. 디폴트로 django-admin.py startproject는 django admin을 위해 주석 처리한 code가 있다, 그리고, 이를 주석 해제하면 된다. 다음과 같이 수정한다.
from django.conf.urls import patterns, include, url
# Uncomment the next two lines to enable the admin:
from django.contrib import admin
admin.autodiscover()
urlpatterns = patterns('',
...
# Uncomment the next line to enable the admin:
url(r'^admin/', include(admin.site.urls)),
...
)위 설정의 일부로 django의 admin site를 볼 수 있다. python manage.py runserver를 실행한뒤, http://127.0.0.1:8000/admin/에 들어가 보면 된다.
admin site 이용하기
admin site는 기술자가 아닌 사람들에의해 사용될 것을 예상하여 설계되었다. 그럼에도 불구하고 기초적인 기능은 익히는것이 좋다.
처음 들어가보면, 아래와 같이 로그인화면을 볼 수 있다.
username과 password로 로그인하면 superuser를 추가한 것이다. 만약 로그인할 수 없다면, 이미 superuser를 생성을 확인해 봐야 한다. python manage.py createsuperuser를 실행해 보기 바란다.
일단 로그인하였다면, admin 홈페이지를 보게될 것이다. 이 page는 admin site에서 편집할 수 있는 모든 종류의data를 나열한다. 이때, 아직 model을 활성화하지 않았기 때문에, 목록은 비어있다. 단지 Groups and Users만 있는데, 이는 기본으로 포함된 편집가능한 model들이다.
django admin site에 있는 data의 각 type은 변경 내역(change list)과 편집창(edit form)을 가진다. change list는 database에 있는 모든 가능한 object를 보여주고, edit form은 database에 있는 특정 record에 대한 추가/변경/삭제를 가능하게 한다.
(Note : admin-site의 언어 선택은 추후 chapter 19에서 다룬다)
"Users"에 있는 "Change" 링크를 선택하면, user에 대한 변경사항을 보여준다.
이 page는 database의 모든 사용자를 나열한다. SELECT * FROM auth_user; SQL query문에 대한 web 버전이라고 생각하면 된다. 만약 이후에 진행될 예제를 통해 사용자 추가등을 실행했다면, 더만은 user를 보게될 것이고 필터링, 정렬 그리고 검색등을 수행할 수 있다.
당신이 추가했던 username(여기서는 greenfish)를 선택하면, 다음과 같은 edit form을 보게 된다.
이 page는 user의 속성(성, 이름, 기타 권한등등)을 변경하도록 해준다. 또다른 주요 사항으로는 다른 type의 field는 다른 위젯으로 나타난다는 것인데, 예를 들어, date/time field는 calendar control, boolean field는 checkbox, character field는 간단한 text input으로 구성되어 있다.
화면 하단의 delete button을 통해 record를 삭제할 수 있다. 이는 다시 확인창을 발생시키는데, 함께 삭제되는 의존성이 있는 object도 표기된다. 예를 들어, publisher를 삭제한다면, publisher를 가진 모든 book은 함께 사제될 것이다.
"Add" button을 통해 record를 추가할 수 있다. 이는 당신이 값을 채워 넣어야할 내용을 비워서 보여준다.
당신이 입력한 값에 대한 검증또한 수행해 준다. 필수 항목을 누락한채 저장하려면 다음과 같은 오류창이 발생한다.
만약 기존에 있는 object를 편집했다면, History link를 실행할 수 있다. admin interface를 통한 변경은 저장되며 History link를 통해 확인할 수 있다.
만약 user의 e-mail을 수정했으면,
와 같이 되고, 그에 따른 History는 다음과 같다.
admin site에 model 추가하기
아직 결정적인 부분을 소개하지 않았다. admin site에 우리가 만든 model을 추가할 수 있는데, 이러한 interface를 이용하여 손쉽게 object를 추가/변경/삭제할 수 있다. chater 5의 books 예제를를 적용해 보도록 하자.
books directory(mysite/books)에서 admin.py를 입력하고 다음 code를 기입한다.
from django.contrib import admin from mysite.books.models import Publisher, Author, Book admin.site.register(Publisher) admin.site.register(Author) admin.site.register(Book)이 code에 의해 django admin site는 각각의 model을 제공받는다.
일단, 이를 수행하였다면, web browser를 통해 확인 바라며, "Books" section에 들어가 보기 바란다.
(잘하면, runserver를 다시 시작해야 할 지 모른다.)
각각의 3개 model마다 admin inteface가 모두 지원되었다. 그것도 아주쉽게...
record 추가나 수정을 시도하여 database에 몇몇 data를 생산하기 바란다. 만약 이전의 chapter 5 예제를 실행하였다면, Publisher object에 이미 몇개의 data가 들어 있을 것이다.
가치있는 기능중 하나로, Book model에 대해 many-to-many relationship과 foreign key 처리이다. Book model은 다음과 같을 것이다.
class Book(models.Model): title = models.CharField(max_length=100) authors = models.ManyToManyField(Author) publisher = models.ForeignKey(Publisher) publication_date = models.DateField() def __unicode__(self): return self.title"Add book" page에서, publisher(Foreign key)는 select box로 표현되고 authors field(ManyToManyField)는 다중 select box로 구성된다.
두개의 field 모두 녹색의 + 버튼이 있는데, 이는 연관 records를 추가하도록 도와준다.
admin site 동작 방식
어떻게 admin site가 동작할까? 그다지 복잡하지는 않다.
server 시작시 urls.py로 부터 URLconf를 로드하였다면, admin을 활성화를 위한 기능으로 추가된 admin.autodiscover()를 실행한다. 이 함수는 INSTALLED_APP를 조사하고 각각의 installed app에 있는 admin.py를 호출한다. 주어진 app에 admin.py가 있다면, 해당 file에 있는 code를 실행한다.
books app에 있는 admin.py에서 각각 admin.site.register()를 단순히 호출하는데, admin으로 주어진 model이 등록(register)된 경우에만 그러하다. admin site는 명시적으로 등록된 model에 대해 edit/change interface를 표시만 하게 된다.
django.contrib.auth는 고유의 admin.py를 포함하는데, 이는 admin에 있는 Users와 Groups를 자동으로 표기하기 위함이다. 다른 django.contrib.redirects와 같은 django.contrib app은 역시 admin을 추가하는데, web으로 부터 download한 많은 3rd party django application을 수행한다.
그 밖에, django admin site는 단지 django application이고, 그것은 자체 model, template, view 그리고 URLpattern을 가직고 있다. 당신의 URLconf로 추가할 수 있고, 당신 고유의 view에 연결할 수 있다. template, view, 그리고 URLpattern을 검사할 수 있는데, 이는 django codebase의 당신 복사본에 django/contrib/admin을 찾는것에 의해서 가능하다. 그러나 admin site 동작 방식을 custom할 수 있는 많은 방법이 있더라도 직접 수정하는 유혹에 빠지지는 말자. (django admin application을 뒤지기로 결정했다면, model에 대해 metadata를 읽음으로 꽤나 정제된 행위를 할 수 있음을 명시하고, 그래서 code를 읽고 이해하는데 들어가는 비용을 줄일 수 있을 것이다.)
field optional 만들기
잠시동안 admin site를 알아봤는데, 제약에 대해 주목해야 한다. edit forms은 모든 field가 채워져야 하는데, 대부분 optional field로 처리되길 바랄 것이다. 예를 들어, Author model의 email field를 optional하게 처리하는 것이다. 다시 말해, 비어있는 문자열을 허락하도록 하는 것이다. 현실 세계에서 모든 author가 email 주소를 가지는것이 아니기 때문이다.
email field가 optional임을 지정하기 위해 Book model(chapter 5에서 mysite/books/models.py에 있음)을 수정해야 한다. 다음과 같이 blank=True를 email field에 추가하면 된다.
class Author(models.Model): first_name = models.CharField(max_length=30) last_name = models.CharField(max_length=40) email = models.EmailField(blank=True)이는 author의 email address가 빈값을 허락하라는 뜻이다. 기본값으로 blank=False인데, 빈값을 허락하지 않는다.
여기에서 재미있는 것이 있는데, __unicode()__ method를 제외하고 우리의 model은 database table의 정의대로 CREATE TABLE sql statement의 표현 형태로 서비스되었다. blank=True를 추가하여 database table의 형상을 간단히 정의한 것을 넘어 우리의 model을 확장하기 시작한 것이다. 이제 우리의 model class는 Author object가 무었인지 그리고 무었을 할 수 있는지에 대한 지식의 집합을 좀더 풍부하게 다루기 시작한 것이다. VARCHAR column으로 database에 email field를 표현되도록 한것 뿐만 아니라, django admin site에 optional field로 선언하였다.
blank=True로 선언하였다면, "Add author"(http://127.0.0.1:8000/admin/books/author/add/)를 다시 읽고 "Email" 글자가 bold체가 아님을 확인할 수 있다. 이는 요구하는 값이 아님을 나타나는 뜻으로 email 주소 없이 추가할 수 있다.
date와 numeric field optional 만들기
blank=True와 연관되어 date와 numeric field에도 적용되리라 생각할 수 있는데, 이는 배경 설명이 추가로 필요하다.
SQL은 빈값을 지정하는 고유의 방법으로, NULL을 사용한다. NULL은 "모름(unknown)"을 의미하거나 "무효한(invalid)" 혹은 기타 다른 정의로 사용되기도 한다.
SQL에서 NULL 값은 비어있는 문자열과 다른 의미인데, 이는 python object인 None이 비어있는 문자 ""와 다른것과 마찬가지 이다. 이는 특정 문자 field(예, VARCHAR)가 NULL 값과 비어있는 문자열 두개 모두 가능하다는 것이다.
이는 애매함과 혼란을 야기한다 : "왜 이 record는 NULL을 가지고 있고, 다른것은 비어있는 문자열이지? 그들간의 차이점이나 data 불일치(inconsistency)가 발생하지 않을까?"이다. 그리고 : "어떻게 비어있는 값을 가진 모든 record를 구할 것인지? 혹은 NULL record나 비어있는 문자열에 대해서도 혹은 비어있는 문자로 하나를 고르기만 한다면?"
이러한 애매함을 없애기 위해, django의 자동 생성된 CREATE TABLE statement(chapter 5에서 언급)는 NOT NULL을 각 column 정의부에 명시적으로 추가할 수 있다. 예를 들어, chapter 5로 부터의 Author model에을 위해 생성된 statement이다.
CREATE TABLE "books_author" (
"id" serial NOT NULL PRIMARY KEY,
"first_name" varchar(30) NOT NULL,
"last_name" varchar(40) NOT NULL,
"email" varchar(75) NOT NULL
)
;대부분 이것의 기본 동작은 optional이며 data 불일치는 발생하지 않을 것이다. 그리고 admin site와 같은 django의 나머지 부분에도 잘 동작하는데, 만약 field를 비운채 저장하게 된다면, NULL 값이 아닌 빈 문자를 추가하게 된다.그러나 date, time, 그리고 number와 같은 비어있는 문자열을 유효한 값으로 받아들일 수 없는 databas column type이 있다. 만약 date나 integer column에 비어있는 문자열로 추가하려고 한다면, 사용하는 database 종류에 따라(PostgreSQL은 엄격하게 예외를 발생함. MySQL은 받아줄 수도 없을 수도 있음), database 오류를 받게 된다. 이런 경우 NULL이 비어있는 값을 정의하는데 유일하게 사용된다. django model에서 null=True를 field에 추가하여 NULL을 허용할 수 있도록 한다.
다음을 언급하기 위해 많이 돌아왔다 : 만약 data field(예, DateField, TimeField, DateTimeField) 혹은 numeric field(예, IntegerField, DecimalField, FloatField)에 blank 값을 허용하려한다면, null=True와 blank=True를 모두 사용해야 한다.
예들 들어, Book model을 변경하여 publication_date에 blank를 가능하게 만들어보자.
class Book(models.Model): title = models.CharField(max_length=100) authors = models.ManyToManyField(Author) publisher = models.ForeignKey(Publisher) publication_date = models.DateField(blank=True, null=True)null=True를 추가는것은 blank=True를 추가하는 것보다 더 복잡한데, null=True는 database의 의미(semantic)를 변경시키기 때문이고 그러므로, publication_date field로 부터 NOT NULL을 지우기 위해 CREATE TABLE statement를 변경한다.
이러한 몇몇 이유로 django는 database schema를 자동으로 변경하지 않는다. 그래서 model을 변경할 때 마다 적합한 ALTER TABLE statement를 실행해야 하는 책임이 있다. manage.py dbshell를 실행하여 database server의 shell에 들어가자. 다음이 NOT NULL을 제거하는 방법이다.
| C:\mydjango\mysite>python manage.py dbshell SQLite version 3.7.11 2012-03-20 11:35:50 Enter ".help" for instructions Enter SQL statements terminated with a ";" sqlite> ALTER TABLE books_book ALTER COLUMN publication_date DROP NOT NULL; Error: near "ALTER": syntax error sqlite> |
이러한 shema 변경은 chapter 10에서 보다 자세히 다룰 것이다.
field label 수정(customizing) 하기
admin site의 edit form에서 각 field label은 field 이름으로 부터 생성된다. 그 알고리즘은 간단한데, django는 '_'를 ' '로, 앞 문자를 대문자로 변경한다. 예를 들어, publication_date는 "Publication date"로 변경된다.
그러나, field name은 field label로 사용하기 어려울 때도 있는데, 이런 경우에 label을 수정할 수 있다. verbose_name을 model field에 지정하여 가능해 진다.
예를 들어, Author.email field를 "e-mail"로 변경하고자 한다면 다음을 참고한다.
class Author(models.Model): first_name = models.CharField(max_length=30) last_name = models.CharField(max_length=40) email = models.EmailField(blank=True, verbose_name='e-mail')변경하고 server를 다시 시작하면, edit form에 label이 변경된것을 확인할 수 있다.
verbose_name을 사용하면 첫 글자는 항상 대문자로 표시된다.
마지막으로 verbose_name을 위치가 있는 argument로 전달할 수 있는데, 이는 보다 간편한 문법이다. 아래는 그 예이다.
class Author(models.Model):
first_name = models.CharField(max_length=30)
last_name = models.CharField(max_length=40)
email = models.EmailField('e-mail', blank=True)이는 ManyToManyField나 ForeignKey field는 동작하지 않는데, 이는 첫 argument에 model class가 들어가기 때문이다. 이런 경우에는 verbose_name을 명시적으로 지정해야 한다.ModelAdmin class 수정하기
여태껏 우리가 알아본 blank=True, null=True 그리고 verbose_name은 model-level 수정으로, 정확히 얘기하자면 admin-level 수정은 아니다. 이는 이러한 수정 사항들은 model의 한 부분이며 admin site에 의해 사용될 뿐이다. admin-specific은 아니다.
django admin은 admin site가 특정 model에 어떻게 동작하는지를 수정(customize)할 수 있는 option을 제공한다. 이러한 option은 ModelAdmin class에 있으며, 이는 특정 admin site instance에 있는 특정 model을 위한 설정을 포함하고 있는 class이다.
수정 목록(change list) 변경(custom)하기
Author model에 표시되는 수정 목록을 변경해 보도록 하자. 기본적으로 수정 목록은 각 object에 대해 __unicode__()의 결과를 표시한다. chapter 5에서 __unicode()__ method를 정의하였다.
class Author(models.Model):
first_name = models.CharField(max_length=30)
last_name = models.CharField(max_length=40)
email = models.EmailField('e-mail', blank=True)
def __unicode__(self):
return u'%s %s' % (self.first_name, self.last_name)결과적으로 Author object의 수정 목록은 각각의 성과 이름을 함께 표시한다.변경 내역에 다른 field를 추가하여 기본 동작을 향상시킬 수 있다. 예를 들어, author의 e-mail 주소를 표기하고, 성과 이름을 정렬가능토록 한다.
이를 해결하기 위해 Author model에 ModelAdmin class를 정의한다. 이 class는 admin을 수정하는 키가 되는데 변경 내역 page를 표시하기 위한 field의 목록을 지정하도록 해주는 가장 기본적인 것중 하나이다. 다음과 같이 admin.py를 수정하면 된다.
from django.contrib import admin
from mysite.books.models import Publisher, Author, Book
class AuthorAdmin(admin.ModelAdmin):
list_display = ('first_name', 'last_name', 'email')
admin.site.register(Publisher)
admin.site.register(Author, AuthorAdmin)
admin.site.register(Book)여기에서 우리가 한것을 정리하면 다음과 같다.- AuthorAdmin class를 정의하였다. 이 class는 django.contrib.admin.ModelAdmin을 상속받았고, admin model에 변경할 설정을 가지고 있다. "list_display"라는 단 하나의 수정을 지정하였는데 수정 목록에 표시할 field 이름을 정리한 것이다. 이 field 이름들은 model에 반드시 존재해야 한다.
- admin.site.register() 호출을 Autor 다음에 AuthorAdmin을 추가하여 변경하였는데, 이는 다음과 같이 해석할 수 있다. : "AuthorAdmin option으로 Author model을 등록하라."
admin.site.register() 함수는 ModelAdmin subclass를 두번째 argument로 option 처리하여 받아들인다. 만약 두번째 argument를 정의하지 않았다면, django는 해당 model에 대해 기본 admin option을 사용할 것이다.
이러한 변경된 사항을 가지고 해당 page를 다시 불러보자. 그리고 3개의 column으로 표시됨 확인할 수 있다. 덧붙여 column header를 클릭하면 정렬이 실행된다.
다음으로 간단한 검색 바(search bar)를 추가해 보자. search_fields를 AuthorAdmin에 추가한다.
class AuthorAdmin(admin.ModelAdmin):
list_display = ('first_name', 'last_name', 'email')
search_fields = ('first_name', 'last_name')해당 page를 다시 읽으면 화면 상단에 검색 바가 뜨게 된다. 우리는 단지 admin 수정 목록에 first_name과 last_name field로 검색할 수 있는 검색 바를 포함하도록 알려줬을 뿐이다. 기대한 것처럼 대소문자 구별이 없으며, "bar"라고 검색하면 Barney 성과 Hobarson 이름을 찾게 된다.다음으로, 수정 모록에 date filter를 추가해 보도록 하자.
from django.contrib import admin
from mysite.books.models import Publisher, Author, Book
class AuthorAdmin(admin.ModelAdmin):
list_display = ('first_name', 'last_name', 'email')
search_fields = ('first_name', 'last_name')
class BookAdmin(admin.ModelAdmin):
list_display = ('title', 'publisher', 'publication_date')
list_filter = ('publication_date',)
admin.site.register(Publisher)
admin.site.register(Author, AuthorAdmin)
admin.site.register(Book, BookAdmin)다른 종류의 option을 다루기때문에, BookAdmin이라는 구별된 ModelAdmin class를 생성하였다. 처음으로 list_display를 수정하였으며, 그다음 lists_filter를 사용하였는데, 이는 수정 목록 page의 오른족에 사용할 filter를 생성할수 있는 tuple을 지정하도록 한다. date field는 "오늘", "과거 7일", "이번달", 그리고 "올해"에 대한 filter를 제공한다. 다음과 같이 표시된다.list_filter는 역시 DateField 이외의 다른 type의 field에서도 작동한다. 최소 2개 이상이 선택되었을때 표시된다,
date filter를 제공하는 다른 방법으로 date-hierarchy admin option을 사용하는 것이다.
class BookAdmin(admin.ModelAdmin):
list_display = ('title', 'publisher', 'publication_date')
list_filter = ('publication_date',)
date_hierarchy = 'publication_date'이것으로 수정 목록 page는 목록 상단에 navigation bar가 나타난다. 가능한 년도와 함께 시작되며, 달과 개별 일에 대해 더 상세히 들어갈 수 있다.date_hierarchy는 tuple이 아닌 문자열을 받아들인다. 이는 하나의 date field만 받아들이기 위해서이다.
마지막으로 기본 정렬을 변경하는 것으로, 수정 목록 page가 표시될 때 항상 publication date에 의해 내림차순으로 정렬되도록 하는 것이다. 기본으로는 수정 목록은 class Meta(chapter 5) 범위 내의 model의 정렬을 따르게 되는데, ordering 값을 지정하지 않았다면, 정렬의 순서는 정의되지 않은 것이다.
class BookAdmin(admin.ModelAdmin):
list_display = ('title', 'publisher', 'publication_date')
list_filter = ('publication_date',)
date_hierarchy = 'publication_date'
ordering = ('-publication_date',)이 admin ordering option은 model들의 class Meta에 ordering을 따르도록 되어 있는데, list에 있는 첫 field name을 사용하도록 되어 있다. field 이름의 tuple 혹은 list를 전달하고, 내림차순 정렬인 경우 앞에 '-'를 붙여 주면 된다.해당 page를 다시 읽으면 다음과 같다. "Publication date" header는 이제 작은 화살표 표시가 포함되고 관련 record들은 그에 따라 정렬되어 있다.
수정 목록을 변경하는 주요한 내용을 다뤘다. 이러한 option을 사용하면서 강력하고 생산적인 data 편집 interface를 단 몇줄로 만들 수 있다.
edit form 수정하기(customizing)
수정 목록을 수정한것 처럼 edit form도 많은 방법으로 변경이 가능하다.
처음으로 field의 순서를 수정해 보자. 기본으로 field의 순서는 model에서 정의한 순서를 따르도록 되어 있다. ModelAdmin 상속 class의 fields option을 이용하여 순서를 변경할 수 있다.
class BookAdmin(admin.ModelAdmin):
list_display = ('title', 'publisher', 'publication_date')
list_filter = ('publication_date',)
date_hierarchy = 'publication_date'
ordering = ('-publication_date',)
fields = ('title', 'authors', 'publication_date', 'publisher')
이렇게 되면 edit form에서 field의 순서가 변경되게 된다.다른 유용한 fields 옵션으로 전체가 편집되는 것으로 부터 특정 field를 제외할 수 있도록 한다. 단지 제외할 field(들)을 비워두면 된다. 만약 당신의 admin user들이 당신 data의 특정 부분을 편집하는데 신뢰된다던지 외부에 의해 field의 일부가 변경되는 경우 사용하면 된다. 예를 들어, book database에서 publication_date field를 편집하지 못하도록 할 수 있다.
class BookAdmin(admin.ModelAdmin):
list_display = ('title', 'publisher', 'publication_date')
list_filter = ('publication_date',)
date_hierarchy = 'publication_date'
ordering = ('-publication_date',)
fields = ('title', 'authors', 'publisher')
결과적으로 books를 위한 edit form은 publication date를 지정할 방법을 제공하지 않는다. 이는 분명 유용한데, 다시 말해, 만약 publication date를 미루지 않는 author를 선호하는 편집자라면 그렇다.
user가 새로운 book을 추가할 때 이러한 불완전한 form을 사용한다면, django는 간단히 publication_date를 None으로 간주한다. 이는 즉 해당 field는 null=True가 되어 있어야 한다.
다른 것으로 many-to-many field에 대한 수정이 있다. book에 대한 edit form을 보았는데, admin site는 각각 ManyToManyField를 multiple-select box로 표현하는데, 이는 대부분의 논리적인 HTML 입력 widget이다. 그러나 multiple-select box는 사용하기가 불편하다. 만약 여러개의 item을 선택하려 한다면, CTRL 키를 누른채 선택해야 한다. admin site는 이것을 쉽게 설명하는 text를 추가할 수 있도록 해준다. 그러나 여전이 수백개를 포함하는 경우에는 다루기가 불편하다.
admin site의 결론은 filter_horizontal이다. BookAdmin을 추가하고 확인해보자.
class BookAdmin(admin.ModelAdmin):
list_display = ('title', 'publisher', 'publication_date')
list_filter = ('publication_date',)
date_hierarchy = 'publication_date'
ordering = ('-publication_date',)
filter_horizontal = ('authors',)(만약 여기까지 따라왔다면, 앞선 fields option을 제거했다)다시 page를 읽어 들이면, "Authors" section을 보게 되고, 그것은 동적으로 option을 통해 검색을 가능케하고 "Available authors"로 부터 "Chosen authors" box로 혹은 그 역으로 특정 author를 이동할 수 있도록 하는 장식된 JavaScript filter를 사용한다.
10개 이상의 item을 가지는 ManyToManyField는 filter_horizental를 사용할 것을 권장한다. 이전의 간단한 multiple-select widget 보다 사용하기 편리하기 때문이다. 또한 filter_horizontal을 다중 filed에도 사용할 수 있다. 단지 tuple에 각 이름을 지정하면 된다.
ModelAdmin class는 역시 filter_vertical option도 지원한다. 이는 filter_horizontal과 동일하게 작동하나, JavaScript interface 결과를 수직대신 수평으로 쌓게 한다. 이는 단지 개인의 취향이다.
filter_horizontal과 filter_vertical은 ManyToManyField fields에만 동작하고, ForeignKey field는 동작하지 않는다. 기본으로 admin site는 간단한 <select> box를 ForeignKey field를 사용하나 ManyToManyField는 가끔 drop-down으로 표시되는 관계있는 object를 선택하는데 여러 부하가 발생하는 것이 발생되지 않길 원할 것이다. 예를 들어, 만약 book database에서 publisher가 수천개가 이를 정도로 커진다면, "Add book" form은 load하는데도 오래 걸릴 것이다. 왜냐하면 모든 publisher를 load해야 <select> box에 표시할 수 있기 때문이다.
이를 해결하기 위해 raw_id_fields를 사용하면 된다. ForeignKey field 이름으로 tuple 값을 세팅하고 이 fields는 간단한 text input box로 표시될 것이다.
class BookAdmin(admin.ModelAdmin):
list_display = ('title', 'publisher', 'publication_date')
list_filter = ('publication_date',)
date_hierarchy = 'publication_date'
ordering = ('-publication_date',)
filter_horizontal = ('authors',)
raw_id_fields = ('publisher',)
input box에는 그럼 무었을 입력하는가? publisher에 해당되는 database id가 그 답이다. 보통 사람은 database id를 기억하지 못하므로, 옆의 돋보기 버튼을 통해서 선택할 수 있다.
Users, Groups, 그리고 Permissions
supseruser로 로그하였기 때문에, 당신은 임의의 object에 대해 생성, 편집, 그리고 삭제할 수 있다. 다른 환경은 다른 permission 시스템을 요구하기도 하는데, 모든이가 할 수 없도록 한다던지, superuser 이여야 한다던지가 해당된다. django의 admin site는 permission 시스템을 사용하는데, 그것은 그들이 필요한 interface의 단지 일부분만 특정 사용자가 접근 가능하게 해준다.
이러한 user 계정은 admin interface의 외부에서도 일반적으로 필요하다는 의미인데, 여기서는 admin user 계정에 한해서만 다루도록 한다. chapter 14에서 당신 site의 그 나머지에서 user 계정을 통합하는 것을 다루도록 한다. (admin site 뿐만 아니라)
당신은 다른 object와 유사한 admin interrface를 통해 user와 permission을 편집할 수 있다. 우리는 본 chapter 일찍이 Admin의 User와 Group section을 둘러보았다. User object는 표준 username, password, e-mail, 그리고 실명 field를 가지고 있으며 덧붙여 그 user에게 어떤 admin interface를 허락할지를 정의한 field도 있다. 우선, 다음과 같이 3개의 boolean flag를 가지고 있다.
- "active" flag는 user의 활성화 여부를 조절한다. 만약 이 flag가 off라면 해당 user는 password가 맞더라도 log-in 할 수 없다.
- "staff" flag는 admin interface레 log-in을 허락할지를 조절한다. public user와 administrator를 구별하는데 사용되는데, 자세한건 chapter 14에서 다룬다.
- "superuser" flag는 추가할 admin interface에서 item을 생성하고 삭제하는 모든 access를 준다. 만약 해당 flag가 on이라면 모든 일반적인 permission은 무시된다.
"일반"적인 admin user, 다시 말해 active, non-superuser staff는 할당된 permission을 통해 admin 접근이 허가된다. admin interface를 통해 편집가능한 각 object는 3개의 permission을 가진다 : create permission, edit permission, 그리고 delete permission. permission을 user에게 할당하는 것은 이러한 permission에 의해 설명된 것들에 대한 접근을 허가한다.
만약 user를 생성할 때, 그 user에게 permission이 없고 이는 user 지정 permission을 부여하는 것에 달려있다. 예를 들어 publisher를 추가하고 변경하는 permission을 임의의 user에게 부여했으나 삭제할 permission을 주지 않았다면, 이러한 permission은 model 당 정의되어 있고, object 당 정의된 것이 아니다. "John은 어떤 책이라도 변경할 수 있다"이지 "John은 Apress에 의해 publish된 어떤 책을 변경할 수 있다"는 아니다. 뒷 부분 즉 object당 permission은 조금 복잡하고 이 책의 범위를 벗어난다. 그러나 django document에서 다루고 있다.
역시 user를 group에 할당할 수 있다. group은 해당 group의 모든 멤버에게 적용할 permission의 집합이라고 생각하면 된다. group은 user 일부에게 동일한 permission을 부여하는데 유용하다.
언제 그리고 왜 admin interface를 이용해야 하는가? 그리고 언제 사용하면 안되는가?
본 chapter를 통해 django의 admin site를 사용하는 좋은 아이디어가 있을지 모른다. 그러나 언제 그리고 왜 사용하는지를 다루고, 그리고 언제 사용하면 안되는지도 확인해 보자.
django의 admin site는 특별히 빛나는데, 이는 비-기술자 user가 data에 접근할 때 이다. django가 처음 개발되었을 때 일상적인 온라인 기능의 개발은 다음과 같이 어디론가 가버렸다라고 신문에서 언급다.
- project에 책임있는 reporter는 개발자중 한명을 만나고 가능한 data를 설명한다.
- 개발자는 해당 data에 적합한 django model을 설계하고 reporter에게 admin site를 개설한다.
- reporter는 누락되거나 관계없는 field를 찾기 위해 admin site를 검사하여 좀더 좋게 만든다. 개발자는 model을 반복하여 변경한다.
- model에 동의되었다면, reporter는 admin site를 사용하여 data에 들어가보기 시작한다. 그와 동시에 프로그래머는 공개적으로 접근가능한 view와 template를 개발하는데 촛점을 맞출수 있다.
다른말로, django admin interface의 존재 이유는 컨텐츠 생산자와 프로그래머의 작업을 연속적으로 작업을 가능하게 한다.
그러나 admin site는 다른 몇가지 경우에 의해서도 유용하다.
- data model 검사(inspect) : 몇몇 model을 정의하였다면, admin interface에서 호출하고 몇몇 dummy daat를 입력하는것등이 꽤나 유용할 때가 있다. 몇가지 경우에서 이는 data-modeling 실수 혹은 model에서 발생하는 다른 문제를 드러내 주기도 한다.
- 습득(aquire)된 data 관리 : 외부 source(사용자 혹은 web crawler)로 부터 들어오는 data에 의존하는 application을 위해 admin site는 data의 검사 혹은 편집을 쉽게 하는 방법을 제공한다. database의 command-line utility 보다는 powerful 하지 않지만 훨씬 더 편리하다는 생각이 들 것이다.
- 빠르고 더러운(quick and dirty) data 관리 app : admin site를 data 관리 app으로 스스로 만들어 사용할 수 있다. 다시 말해 비용을 추적한다는 뜻이다. 만약 공개하지 않고 당신 고유의 필요에의해 무엇인가 만든다면 admin site는 먼길을 제시한다. 이런 의미에서 당신은 그것을 보강된 것으로 생각할 수 있고 spreadsheet의 유사한 버전으로 간주된다.
명확한 한가지는 다음과 같다. : admin site는 가장 중요한 것은 아니다. 몇년 동안 의도적으로 서비스하지 않는 기능을 제공하기 위해 수정하고 자르는것을 봐왔다. 그것은 data에 대한 공개적인 interface로 의도되지 않았거나 data의 정제된 정렬과 검색을 허락하는 의도로 만들어졌다. 본 chapter 앞부분에서 알린바와 같이, 신뢰된 site 관리자를 위한 것이다. 이것을 잘 염두해둬야 admin site 사용을 효과적으로 사용할 수 있는 key가 된다.
'django > the django book study' 카테고리의 다른 글
| [06] django admin site (2) | 2012/04/27 |
|---|---|
| [05] django의 model (1) | 2012/04/12 |
| [04] django의 template (2) | 2012/04/05 |
| [03] django 1.4의 view와 urlconfs (0) | 2012/04/04 |
| [02] django 1.4 설치와 project 생성하기 (0) | 2012/03/30 |
| [01] django 소개 (0) | 2012/03/28 |
chapter 3에서 django의 view와 URLconf를 이용하여 동적 web site를 구축하는 기초적인 방법을 알아보았다. view는 몇몇 임의의 logic을 책임진다고 설명하였고, response를 전달하게 된다. 그 예중 하나는 현재의 시각을 계산하는 logic이었다.
최근의 web application에서 임의의 logic은 종종 database와 연동된다. 알게 모르게 database 중심의 web site(database-driven web site)는 database server에 접속하고 data를 전달 받은뒤 web page에 표시한다. 그 site는 방문자를 위해 그들 고유의 database를 창출하도록 기능을 제공하기도 한다.
많은 복잡한 web site는 그 두개를 조합하기도 한다. 예를 들어, amazon.com은 database 기반 site의 휼륭한 본보기가 된다. 각 제품 page는 amazon 제품의 database의 query 결과를 가지고 HTML 형태로 format되고, 고객 review를 전송할때 review의 database로 insert한다.
django는 database 중심의 web site를 만들기에 적합하게 설계되었다. 왜냐하면 python을 이용하면 database query 수행을 위한 강략한 도구가 함께 따르기 때문이다. 본 chapter에서는 django의 database 계층읠 기능에 대해 설명한다.
view에서 database query를 수행하는 "멍청한" 방법
chapter 3에서 view에서 hard-code된 text를 직접 사용했던 "멍청한" 방법처럼, view에서 database로 부터 직접 data를 전달받는 "멍청한" 방법도 있다. 그것은 간단한데, sql query를 수행하기 위한 python library를 호출하고 그 결과를 이용하는 것이다.
이러한 예제 view에서는, MySQLdb library를 이용할 것인데, MySQL database에 접속하고 record를 구해 template에 전달하여 web page로 출력하게 된다.
이러한 접근법으로 동작하긴 하나, 몇몇 문제가 즉각 눈에 띄게 된다.
- database 접속 parameter가 hard-coding되어 있다. 이상적으로는 이런 parameter등은 django configuration에 저장되어야 한다.
- 약간의 보일러판과 같은 코드가 포함되어 있다. 이는 접속, cursor 생성, statement 실행, 그리고 connection을 닫는것을 의미한다. 이상적으로 우리가 할 일이란 우리가 원하는 어떠한 결과를 구할지를 지정하는 것이다.
- MySQL에 묶여 있다. 만약 MySQL에서 PostgreSQL로 변경하는 경우, database adaptor(psycopg, MySQLdb)가 달라지게 된다. 그러면 접속 parameter는 변경하게 되고, SQL statement의 본질에 의존하게 되어 SQL를 다시 써야할 지도 모른다. 이상적으로 사용할 database 서버는 추상적(abstracted)이어야 하고, 그래서 같은 곳에서 database server는 변경가능해야 한다.
본 code에 대해서는 추후 설명할 것이다. 우선 느껴보기 바란다.
MTV(혹은 MVC) 개발 패턴
code를 뒤지기 전에 우선 database 중심의 django web application의 design에 대한 전반적인 설명이 필요하다.
이전 chapter에서 언급하였듯이, django는 loose coupling을 장려하도록 설계되었고, code 조각의 구별이 엄격하게 이뤄지도록 한다. 만약 이러한 철학을 따른다면, 특정 조각은 다른 조각의 영향없이 변경이 쉽게 이뤄지도록 해야 한다는 것이다. 예들 들어 view 함수에서 template system을 사용함으로써 표현 logic으로 부터 bussiness logic을 분리하는 것이 중요하다고 논하였다. database 계층으로 data 접근 logic에서도 동일한 철학을 적용하도록 한다.
data 접근 logic, bussiness logic, 그리고 표현 logic과 같은 3개의 조각은 함께 software architecture에서 Model-View-Controller(MVC) 패턴으로 불려진다. 이 패턴에서, "Model"은 data 접근 계층, "View"는 무었을 어떻게 표시할지를 결정하는 시스템의 일부, 그리고 "Controller"는 사용자 필요시 model을 접근하고 사용자 input에 의존하여 어떤 view를 사용할지를 결정하는 시스템의 일부를 언급한다.
django는 MVC 패턴을 MVC framwork이라고 불리는 것으로 근접하게 따르고 있다. M, V, 그리고 C를 나누어 간략히 설명하면 다음과 같다.
- M, data 접근 부분으로, 본 chapter에서 설명된 django의 database 계층에 의해 처리된다.
- V, 어떤 그리고 어떻게 출력할지에 대한 부분으로 view와 template에 의해 처리된다.
- C, 사용자 input에 의존하여 view를 선택하는 부분으로, URLconf에 의해 따르고 주어진 URL에 대해 적당한 python 함수를 호출하는 것으로 framework에 의해 처리된다.
"C"는 framework 그 자체에 의해 처리되기 때문에, django에서 대부분의 신나는 일은 model, template, 그리고 view에서 이뤄진다. 그래서 MTV framework이라 불리기도 한다. MTV 개발 패턴은 다음과 같다.
- M은 "Model"을 의미하는 것으로, data 접근 계층이다. 이것은 data의 모든것을 다룬다.
; 어떻게 접근할지, 어떻게 입증(validate)할지, 어떤 행동을 가질지, 그리고 data간의 관계에 대해서. - T는 "Template"를 의미하는 것으로, 표현(presentation) 계층이다. 이것은 표현과 관계된 결정을 다룬다.
; web page 혹은 다른 종류의 문서를 어떻게 표현할 것인지. - V는 "View"를 의미하는 것으로, bussiness logic 계층을 의미하는 것으로, model 접근을 포함하고 저합한 template(들)을 따른다. model과 template간의 중간 다리 역할이라고 생각하면 된다.
Ruby on Rails와 같은 MVC web 개발 framework에 친숙하다면, django view를 "controller", django template를 "view"라고 간주할 수 있다. 이와 같이 MVC의 다른 해석에 따른 혼란은 불행한 일이다. django의 MVC에 대한 해석은, "view"는 사용자에게게 표현될 data를 기술(describe)하고, 어떤 data가 표현되더라도 어떻게 그 data가 보여질지는 꼭 필요하지는 않다. 이에 비해 Ruby on Rails나 비슷한 framwork에서 controller의 작업은 어떤 data가 사용자에게 표현될지 결정을 포함하는데, 어떤 data가 표현될지를 결정하기 보다는 view가 어떻게 보여지는지를 엄격히 한다.
어떤 해석도 다른것보다 더 "정확"할 순 없다. 그 이면의 개념을 이해하는 것이 가장 중요하다.
database 설정하기
앞선 철학에 대해 염두한채, django의 database 계층에 대해 알아보자. 우선 몇몇 초기 설정에 대한 주의가 필요하다. 우리는 어떤 database server를 사용하고 접속할지에 대해 django에 알려줘야 한다.
우리는 database server를 설치, 활성화하고 CREATE DATABASE statement등으로 그것 내부에 database를 생성했다고 가정한다. 만일 SQLite를 사용하고 있다면, data 저장을 filesystem의 단일 file로 하기 때문에, 설치과정은 필요없다.
이전장의 TEMPLATE_DIRS 처럼, database 설정은 settings.py에서 기본적으로 할 수 있다. 해당 file을 편집해보면 아랫부분을 찾을 수 있다.
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.', # Add 'postgresql_psycopg2', 'mysql', 'sqlite3' or 'oracle'.
'NAME': '', # Or path to database file if using sqlite3.
'USER': '', # Not used with sqlite3.
'PASSWORD': '', # Not used with sqlite3.
'HOST': '', # Set to empty string for localhost. Not used with sqlite3.
'PORT': '', # Set to empty string for default. Not used with sqlite3.
}
}이에 대한 설명은 아래와 같다.
- (역자주) django 1.2 부터 multiple database connection(상세정보)가 추가되었는데, 'default' : 는 이에 해당되는 것으로, 기본 database 설정을 의미한다. (원본글은 django 1.0인데, 이때의 django는 1 project에 1 database 였다)
- DATABASE_ENGINE은 django에서 사용할 database 종류를 기술한다. 만약 django로 database를 사용하고자 한다면, DATABASE_ENGINE은 아래표의 값중 하나로 선택되어야 한다.
당신이 어떤 database를 사용하더라도 그에 해당되는 adapter를 다운로드, 그리고 설치해야 한다. 각각은 web을 통해 무료로 설치할 수 있다. 만약 linux 시스템이라면, package 배포 도구는 좀더 편한 package를 설치하도록 유도할 것이다.설정값(django.db.backends.*) Database 필요한 adapter postgresql PostgreSQL psycopg version 1.x postgresql_psycopg2 PostgreSQL psycopy_verison 2.x mysql MySQL MySQLdb sqlite3 SQLite (python 2.4 이하)pysqlite oracle Oracle cx_Oracle
예)
DATABASE_ENGINE='postgresql_psycopg2' - DATABASE_NAME은 django에서 사용될 database 이름을 기술한다.
예)
DATABASE_NAME='mydb'
만일 SQLite를 사용한다면, database가 사용하는 filesystem 상의 fullpath를 기술하면 된다.
예)
DATABASE_NAME='/home/django/mydata.db'
만일 SQLite database를 사용한다면, 위 예에서는 /home/django 경로를 사용할 것이다.
참고로, sqlite3를 사용한다면, settings.py에,
import os ... SITE_ROOT = os.path.dirname(os.path.realpath(__file__)) ... 'NAME': os.path.join(SITE_ROOT, 'db') + '/development.db', ...
- 와 같으면, project 경로 하부의 db 경로의 development.db 파일을 남긴다. 단, 만일 db 경로가 없다면 오류를 리턴한다.
- DATABASE_USER는 django로 하여금 database에 접속할 username을 기술하도록 한다. 만약 SQLite를 사용한다면 비워두면 된다.
- DATABASE_PASSWORD는 database에 접속할때 사용할 비밀번호를 의미한다. 만약 SQLite 혹은 비밀번호가 비어있는 경우, 본 값을 비워두면 된다.
- DATABASE_HOST는 database에 접속할 host이름을 기술한다. 만약 django가 설치된 시스템인 경우 혹은 SQLite를 사용하는 경우 비워두면 된다.
MySQL은 좀더 특별한데, MySQL을 사용하고 본 값이 '/'로 시작한다면, 지정된 socket으로 unix socket을 경유하여 접속할 것이다. 그 예는 아래와 같다.
DATABASE_HOST='/var/run/mysql'
만약 settings.py를 통해 관련 설정을 저장하였다면 test해보는 것이 좋다. 이를 위해 python manage.py shell를 mysite의 manage.py가 있는 경우에서 실행한다.
shell에서 다음과 같이 명령하여 test할 수 있다.
| C:\django.work\study\mysite>python manage.py shell Python 2.5.4 (r254:67916, Dec 23 2008, 15:10:54) [MSC v.1310 32 bit (Intel)] on win32 Type "help", "copyright", "credits" or "license" for more information. (InteractiveConsole) >>> from django.db import connection >>> cursor = connection.cursor() >>> |
| 에러 메시지 | 해결 방법 |
| Please supply the ENGINE value. | DATABASE_ENGINE setting을 비워두지 말자. 지원되는 database 값을 넣는다. |
| Environment variable DJANGO_SETTINGS_MODULE is undefined. | python manage.py shell을 이용하여 들어간다. |
| Error loading ... module: No module named .... | psycopg 혹은 MySQLdb와 같은 적합한 adapter를 찾지 못하였다. 이러한 adapter는 django와 함께 설치되지 않기 때문에, 직접 다운로드하고 설치해야 한다. |
| ... isn't an available database backend | DATABASE_ENGINE setting에 유효한 값을 넣는다. 혹시 오타가 있지 않았는가? |
| database ... does not exist | DATABASE_NAME setting에 유효한 database 이름을 넣는다. 혹은 CREATE DATABASE statement을 실행한다. |
| role ... does not exist | DATABASE_USER setting값을 변경하던지, database에 user를 해당 user를 추가한다. |
| could not connect server | DATABASE_HOST와 DATABASE_PORT가 정확한지 확인한다. 그리고 해당 server가 동작중인지도 확인한다. |
첫번째 app
이제 접속하는데까지 확인되었다면, django app을 생성할 차례가 왔다. 이는 단일 python package 내에 존재하는 model과 view code를 포함하는데, 이를 full django application이라 한다.
이제 이 용어에 대해 설명할 것인데, 초보자를 위해서이다. chapter 2에서 이미 project를 생성하였다면, project와 app의 차이를 알 수 있겠는가? 그 차이점은 설정 대 code로 설명될 수 있다.
- project는 app에 대한 설정이 추가된, django app의 특정 집합이다.
기술적으로, project의 유일한 요구조건이 setting file을 지원하는 것인데, 이는 database 접속 정보, 설치된 app list, 그리고 TEMPLATE_DIR등이 정의되어 있다. - app은 django 기능의 휴대하기(portable) 쉬운 집합이다. 이는 단일 python package에 함께 존재하는 model과 view를 포함하고 있다.
예를 들어, django는 댓글 시스템이나 자동화된 관리자 interface와 같은 수많은 app을 포함하고 있다. 이러한 app들의 가장 주목해야할 점은 여러 project를 통해 휴대성과 재사용성이 좋다는 것이다.
이러한 제도안으로 당신의 django code를 어떻게 만족시키느냐에 대한 규칙은 그리 엄격하지 않다. 만약 단순한 web site를 구축한다면, 당신은 단일 app을 사용할 것이다. 만약 전자상거래 및 게시판 같은 몇개의 서로 관계가 없는 몇몇의 조각으로 구성된 복잡한 web site를 구성한다면, 미래에 재사용을 위해 app들을 구별하여 분리하기를 원할 것이다. C:\mydjango\mysite>python manage.py startapp books C:\mydjango\mysite>
정말, 이전까지 봐왔던 예제와 같이 전혀 app을 생성할 필요성을 못느낄지도 모른다. 이러한 경우, views.py라 불리는 file을 생성하고 URLconf에서 이들을 지명하면 된다. 더이상 필요한 "app"은 없었다.
그러나, app 관습(convention)에 따른 요구조건이 하나 있다. : 만약 django database 계층(model)을 사용한다면, django app을 반드시 생성해야 한다. model은 app 내부에 존재한다. 그러므로 model을 작성하려면, 새로운 app을 만들줄 알아야 한다.
"python manage.py startapp books"
이 명령은 더이상의 화면 출력은 없으나, books 경로 밑으로 몇몇 파일들을 생성한다.
이 파일들은 app을 위한 models과 views를 가지고 있다.
|
역자주) C:\mydjango\mysite\mysite> |
python에서 model 정의하기
이전 chapter에서 "MTV"의 "M"은 "Model"을 의미한다고 하였다. django의 model은 database의 data 설명(description)을 python code로 도식화한 것이다. 그것은 data의 layout로서 SQL의 CREATE TABLE statement와 동일한데 SQL 대신 python으로 되어 있다는 차이가 있다. 그리고 database column 정의 그 이상을 포함하고 있다. django model은 무대뒤에서 SQL code를 실행하며, database table의 row를 python data structure로 전달하여 준다. django는 SQL이 처리할 수 없는 고 수준의 개념을 표현하기 위해 model을 사용한다.
database에 익숙하다면, 즉시 생각나는것이, SQL 대신 python으로 구성된 model을 정의하는 것이 중복된 일이지 않느냐?일 것이다. django가 그렇게 동작하는 이유는 다음과 같다.
- 인트로스펙션(자기성찰, introspection)은 부하를 유발하고 불완전하다. 편리한 data 접근 api를 제공하기 위해, django는 어떻하든지 database 계층을 알 필요가 있고 이를 달성하기 위한 2가지 방법을 만들었다. 첫번째로 명시적으로 database를 python으로 설명하고자 함이고, 두번째로 data의 model을 결정하는데 실시간으로 database를 자기성찰하기 위함이다.
두번째 방법은 깔끔해 보이는데, table에 대한 metadata는 한 곳에서만 존재하나, 이는 몇가지 문제를 야기한다. 첫번재로 실시간으로 database를 인트로스펙션하는데 부하가 걸린다. 만약 framework이 request때 마다혹은 web server가 초기화될때 마다 database에 대해 인트로스펙션한다면, 받아들일 수 없을 정도의 부하가 발생하게 된다(이러한 부하를 받아들일 수 있다고 몇몇은 믿을 수 있으나, django 개발자는 가능한 줄이기를 목표로 한다). 둘째로 몇몇 MySQL의 옛날 버전과 같은 몇몇 database에서 완벽한 인트로스펙션을 위한 정확한 metadata를 저장하지 않는 경우도 있다. - python을 작성하는 것은 재미있는 일이고 모든것을 모든 것을 python으로 유지하는 것은 당신 두뇌가 "context switch"되는 횟수를 제한한다. 가능한 단일 programming 환경과 사고방식으로 지속하면 그만큼 생산성이 높아진다. SQL을 작성하고, pytho을 작성한뒤 다시 SQL을 작성하는 것이 업무에 차질을 잃으키는 요소가 된다.
- database보다 code로 저장된 model을 갖는것이 버전 관리를 보다 쉽게 만든다. 이 방법으로, data layout의 변경 사항에 대해 쉽게 추적할 수 있다.
- SQL은 data layout에 대해 meta data의 특정 수준만을 허락한다. 대부분의 database system에서 예를 들어, email address나 URL등의 표현을 위한 특수한 data type을 지원하지 않는다. 이에 반해 django는 지원한다. 높은 수준의 data type의 장점은 높은 생산성과 code의 가독성을 높인다.
- SQL은 database system 별로 일관적이지 못하다. 만약 당신이 web application을 배포하려고 한다면, 예를 들어, MySQL, PostgreSQL, 그리고 SQLite를 위한 구별된 CREATE TABLE statement의 집합 보다도 당신의 data layout을 설명하는 python module을 배포하는것이 보다 실용적이다.
이러한 접근의 단점은 python code가 database에 실제로 무엇으로 동기화가 이뤄지는제 대해 그 책임을 회피한다는 점이다. django model에 변경을 가하였다면, database를 일관적(consistent)으로 유지하기 위해 database 안으로도 변경이 필요하게 된다. 우리는 이러한 문제를 처리하는데 몇가지 전략을 본 chapter에서 다룰 것이다.
마지막으로 기존 database를 인트로스펙팅함에 의해 model을 생성하는 도구를 django가 포함한다는 사실에 주목해야 한다. 이는 이전 과거(legacy) data를 가지고 일어서서 달려나가는데 유용하다. 이는 chapter 18에서 다룰 것이다.
첫 model
본 chapter와 다음 chapter에서 진행되는 예제로서, 우리는 기본적인 책(book)/저자(author)/출판사(publisher) layout에 집중할 것이다. 책, 저자, 그리고 출판일간의 개념적 관계(conceptual relationship)은 익히 잘 알려져있고 SQL 관련 서적에서 일반적으로 소개되는 layout이기 때문에 이 예를 사용할 것이다. 당신 역시 출판사에 의해 생산되고 저자에 의해 씌어진 책을 읽고 있다.
우리는 다음과 같이 개념, 필드, 그리고 관계(relation)에 대해 가정한다.
- 저자는 성, 이름, 그리고 email 주소를 가진다.
- 출판사는 이름, 거리 주소, 도시, 도, 국가, 그리고 web site를 가진다.
- 책은 제목과 출판일을 가진다. 그리고 하나 이상의 저자(many-to-many relationship)를 가지고 하나의 출판사(one-to-many relationship)를 가진다.
django를 이용한 이러한 database layout을 만드는데 첫번째 할 일은 그것을 python code로 표현하는 일이다. startapp command에 의해 생성된 models.py에 다음과 같이 기입한다.
from django.db import models class Publisher(models.Model): name = models.CharField(max_length=30) address = models.CharField(max_length=50) city = models.CharField(max_length=60) state_province = models.CharField(max_length=30) country = models.CharField(max_length=50) website = models.URLField() class Author(models.Model): first_name = models.CharField(max_length=30) last_name = models.CharField(max_length=40) email = models.EmailField() class Book(models.Model): title = models.CharField(max_length=100) authors = models.ManyToManyField(Author) publisher = models.ForeignKey(Publisher) publication_date = models.DateField()기초 닦기 위해 빠른 설명부터 진행한다. 각 model은 django.db.models.Model의 subclass에 의해 표현된다. Model이라는 부모 class는 database와 연동하기 위한 object를 만드는데 필요한 모든 기계적인 부분을 가지고 있다. 그리고 간결한 문법으로 필드 정의를 유도한다. 믿거나 말거나, django로 기본적인 data 접근을 위한 모든 코드가 표기되었다.
각 model은 일반적으로 단일 database table에 상응한다고 보면 된다. 그리고 model에 각 속성(attribute)는 column의 이름에 해당된다. 그리고 필드의 type(예, CharField)은 database의 column type(예, varchar)에 대응된다. 예를 들어, Publisher model은 다음과 같은 table과 동일한다. (postgreSQL Create Table 문법)
BEGIN;
CREATE TABLE "books_publisher" (
"id" serial NOT NULL PRIMARY KEY,
"name" varchar(30) NOT NULL,
"address" varchar(50) NOT NULL,
"city" varchar(60) NOT NULL,
"state_province" varchar(30) NOT NULL,
"country" varchar(50) NOT NULL,
"website" varchar(200) NOT NULL
)
;
CREATE TABLE "books_author" (
"id" serial NOT NULL PRIMARY KEY,
"first_name" varchar(30) NOT NULL,
"last_name" varchar(40) NOT NULL,
"email" varchar(75) NOT NULL
)
;
CREATE TABLE "books_book" (
"id" serial NOT NULL PRIMARY KEY,
"title" varchar(100) NOT NULL,
"publisher_id" integer NOT NULL REFERENCES "books_publisher" ("id") DEFERRABLE INITIALLY DEFERRED,
"publication_date" date NOT NULL
)
;
CREATE TABLE "books_book_authors" (
"id" serial NOT NULL PRIMARY KEY,
"book_id" integer NOT NULL REFERENCES "books_book" ("id") DEFERRABLE INITIALLY DEFERRED,
"author_id" integer NOT NULL REFERENCES "books_author" ("id") DEFERRABLE INITIALLY DEFERRED,
UNIQUE ("book_id", "author_id")
)
;
CREATE INDEX "books_book_publisher_id" ON "books_book" ("publisher_id");
COMMIT;정말, django는 CREATE TABLE를 자동으로 생성하며 곧 바로 그것을 보게될 것이다.database table당 하나의 class(one-class-per-database-table) 규칙의 예외는 many-to-many relationship인 경우이다. 위 예에서 Book은 authors라 불리는 ManyToManyField를 가지는데, 이러한 하나의 책이 다수의 저자를 기호로 표기한 것이다. 그러나 Book database table은 authors column을 가지지 않는다. django는 추가적인 책에서 저자로 매핑을 처리하는 table(즉, many-to-many 'join table")을 생성한다.
모든 필드의 datatype과 문법은 부록 B를 참고 바란다.
마지막으로 우리는 각 model에 대해 명시적인 primary key를 정의하지 않았다. 만약 그것을 알려주지 않았다면, django는 자동으로 id라 불리는 자동으로 증가하는 (auto-incrementing) primary key 필드를 부여한다.
model 설치하기
우리는 여태껏 code를 작성하였는데, 이제 database의 table을 생성해 보도록 하자. 그러기 위해서는 첫번째로 django project에 이러한 model을 활성화시켜야 한다. 이는 setting 파일에 있는 "installed apps" 리스트에 books app을 추가하는 것으로 해결된다.
settings.py 파일로 돌어와서, INSTALLED_APPS 세팅을 찾아 보자. INSTALLED_APPS는 주어진 project에 어떤 app을 활성화할 것인지를 알려준다. 기본으로 다음과 같은 모양을 가진다.
INSTALLED_APPS = (
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.sites',
'django.contrib.messages',
'django.contrib.staticfiles',
# Uncomment the next line to enable the admin:
# 'django.contrib.admin',
# Uncomment the next line to enable admin documentation:
# 'django.contrib.admindocs',
)임시적으로 위 모든 줄에 대해 #를 추가하여 주석 처리 한다. (이것들은 일반적으로 사용되기 때문에 기본으로 포함되었으나, 이후 chapter에서 설명하도록 한다.) 그릭고, MIDDLEWARE_CLASSES setting도 역시 주석 처리해야 한다. MIDDLEEWARE_CLASSES에 있는 기본값들은 우리가 앞서 주석 처리한 것에 의존성을 가지기 때문이다. 즉, 다음과 같이 수정한다.MIDDLEWARE_CLASSES = (
#'django.middleware.common.CommonMiddleware',
#'django.contrib.sessions.middleware.SessionMiddleware',
#'django.middleware.csrf.CsrfViewMiddleware',
#'django.contrib.auth.middleware.AuthenticationMiddleware',
#'django.contrib.messages.middleware.MessageMiddleware',
# Uncomment the next line for simple clickjacking protection:
# 'django.middleware.clickjacking.XFrameOptionsMiddleware',
)
INSTALLED_APPS = (
#'django.contrib.auth',
#'django.contrib.contenttypes',
#'django.contrib.sessions',
#'django.contrib.sites',
#'django.contrib.messages',
#'django.contrib.staticfiles',
# Uncomment the next line to enable the admin:
# 'django.contrib.admin',
# Uncomment the next line to enable admin documentation:
# 'django.contrib.admindocs',
'mysite.books'
)(지난 chapter에서 TEMPLATE_DIRS를 세팅할때 언급했던거 처럼, tuple로 되어 있기 때문에 끝에 ','로 끝을 내야 한다. 덧붙여 말해서 책 저자들은 보통 tuple의 모든 element 뒤에 ','를 붙이기를 선호하는데, 물론 단일 element일 때에도 마찬가지 이다. 이는 ','를 누락시키는 실수를 방지하며, 있더라도 다른 부작용은 없다.)'mysite.books'는 우리가 작동시키려고 하는 books app을 참고한다. INSTALLED_APPS에 있는 각각의 app은 full python path로 표현되어 진다. 그 말인 즉, '.'으로 구별된 packages의 경로로 app package를 선두로 한다.
이제 django app은 setting 파일에 의해 활성화 되었고, database table을 생성시켜 보자. 우선, 첫번째로 아래를 실행하여 model을 검증(validate)해 보자.
|
C:\mydjango\mysite>python manage.py validate C:\mydjango\mysite> |
model 작업시 문제가 있다고 생각된다면, 언제든지 python manage.py validate를 호출하기 바란다. 그것은 통상적인 model 문제를 잡아낼 것이다.
만일 model에 문제가 없다면, 다음을 실행하여 django로 하여금 books app에 있는 model를 위해 CREATE TABLE statement를 실행하도록 한다. 해당 명령을 실행하면 아래와 같은 결과를 얻을 수 있다.
|
C:\mydjango\mysite>python manage.py sqlall books C:\mydjango\mysite> |
- table 이름은 자동으로 생성되고, 이는 app 이름(books)과 소문자 model 이름(publisher, book, 그리고 author)의 조합으로 이뤄진다. 이런 동작을 무시하려면 부록 B를 참고하시오.
- 이전에 언급했듯이, django는 각 table에 대해 id field라는 자동으로 pimary key를 추가한다. 이또한 무시할 수 있다.
- 관례에 따라, django는 "_id"가 추가된 foreign key를 추가한다. 이것은 무시할 수 있다.
- foreign key 관계는 명시적인 REFERENCES statement를 이용한다.
- CREATE TABLE statement는 사용하는 database에 딱 맞춰 돌아가게 되는데, database 지정 field type(database-specific field type), 예를 들어 auto_increment(MySQL), serial(PostgreSQL) 혹은 integer primary key(SQLite)등이 자동으로 처리되어 진다. column 이름의 따옴표 처리도 그러하다(' 혹은 "). 위 예는 SQLite의 예제이다.
sqlall 명령은 실제로는 database를 건드리는 table 생성이나 기타 다른것들을 실행하진 않는다. 단지 django에서 어떤 SQL를 실행할지에 대해 출력할 뿐이다. 원한다면, 이 SQL을 database client에 copy-paste할 수 있고, 혹은 unix pipe로 직접 호출할 수 있다(예, python manage.py sqlall books | psql mydb). 그러나, django는 SQL을 database에 commit하는 보다 쉬운 방법을 syncdb 명령을 이용하여 제공한다.
| C:\mydjango\mysite>python manage.py syncdb Creating tables ... Creating table books_publisher Creating table books_author Creating table books_book_authors Creating table books_book Installing custom SQL ... Installing indexes ... Installed 0 object(s) from 0 fixture(s) |
만약 python manage.py syncdb를 다시 실행한다면, 어떤일도 발생하지 않는데, INSTALLED_APPS에 어떤 app도 추가되거나 books app에 어떤 model도 추가되지 않았기 때문이다. 그러므로, python manage.py syncdb는 항상 안전하다. 즉, 어떠한 손실도 잃으키지 않는다.
| C:\mydjango\mysite>python manage.py syncdb Creating tables ... Installing custom SQL ... Installing indexes ... Installed 0 object(s) from 0 fixture(s) |
|
C:\mydjango\mysite>python manage.py dbshell C:\mydjango\mysite>python manage.py dbshell |
기본적인 data 접근
일단 model을 생성하였다면, django는 자동으로 이러한 model을 가지고 작업할 높은 수준의 python api를 자동으로 제공한다. python manage.py shell을 통해 다음을 실행해 보자.
| C:\mydjango\mysite>python manage.py shell Python 2.5.4 (r254:67916, Dec 23 2008, 15:10:54) [MSC v.1310 32 bit (Intel)] on win32 Type "help", "copyright", "credits" or "license" for more information. (InteractiveConsole) >>> from mysite.books.models import Publisher >>> p1 = Publisher(name='Apress', address='2855 Telegraph Avenue', ... city='Berkeley', state_province='CA', country='U.S.A.', ... website='http://www.apress.com/') >>> p1.save() >>> p2 = Publisher(name="O'Reilly", address='10 Fawcett St.', ... city='Cambridge', state_province='MA', country='U.S.A.', ... website='http://www.oreilly.com/') >>> p2.save() >>> publisher_list = Publisher.object.all() >>> publisher_list [<Publisher: Publisher object>, <Publisher: Publisher object>] >>> |
- 우선, Publisher model class를 import한다. 이는 publisher가 포함하는 database table과 상호 동작을 가능하게 한다.
- Publisher object를 생성하고, name, address, ...과 같은 값으로 초기화한다.
- database에 object를 저장하기 위해, save() method를 호출한다. django에서는 INSERT statement를 내부적으로 실행한다.
- database로 부터 publisher를 구하기 위해 Publisher.object 속성을 이용한다. 이는 publisher의 모든 집합이라고 생각하면 된다. 모든 Publisher object의 list를 가져오기 위해, Publisher.objects.all()을 사용하였다. django는 내부적으로 SELECT statement를 사용한다.
django model api를 사용하여 object를 생성한다면, django는 save() method를 호출할 때 까지 dattabase에 object를 저장하지 않는다.
만약 database에 한번의 step으로 object를 생성하고 바로 저장하려면, objects.create() method를 사용한다. 다음은 그 예이다.
| >>> p1 = Publisher.objects.create(name='Apress', ... address='2855 Telegraph Avenue', ... city='Berkeley', state_province='CA', country='U.S.A.', ... website='http://www.apress.com/') >>> p2 = Publisher.objects.create(name="O'Reilly", ... address='10 Fawcett St.', city='Cambridge', ... state_province='MA', country='U.S.A.', ... website='http://www.oreilly.com/') >>> publisher_list = Publisher.objects.all() >>> publisher_list |
model의 문자열 표현 추가하기
앞서 publisher의 list를 출력할 때, 다음과 같이 알아내기 힘든 형태로 표시되었다.
| [<Publisher: Publisher object>, <Publisher: Publisher object>] |
from django.db import models class Publisher(models.Model): name = models.CharField(max_length=30) address = models.CharField(max_length=50) city = models.CharField(max_length=60) state_province = models.CharField(max_length=30) country = models.CharField(max_length=50) website = models.URLField() def __unicode__(self): return self.name class Author(models.Model): first_name = models.CharField(max_length=30) last_name = models.CharField(max_length=40) email = models.EmailField() def __unicode__(self): return u'%s %s' % (self.first_name, self.last_name) class Book(models.Model): title = models.CharField(max_length=100) authors = models.ManyToManyField(Author) publisher = models.ForeignKey(Publisher) publication_date = models.DateField() def __unicode__(self): return self.title위에서 볼 수 있듯이, __unicode__() method는 한 object의 표현을 전달하기 위해 필요한 것들을 수행한다. 여기서 Publisher와 Book을 위한 __unicode__() method는 단지 object의 name과 title을 각각 전달한다. 단, Author는 조금 복잡한데, 공백으로 구별된 first_name과 last_name을 함께 표기한다.
__unicode__()의 유일한 요구사항은, Unicode object를 리턴해야 한다는 점이다. 만약 __unicode__()가 Unicode object를 전달하지 않는다면(예를 들어, 정수), python은 "coercing to Unicode: need string or buffer, int found" 형태의 TypeError 메시지의 예외가 발생하게 된다.
| Unicode object Unicode object는 뭘까? 당신은 Unicode object를 Latin 계열의 억양이 표시된 문자에서부터 동그랗게 말린 따움표와 잘 알려지지 않은 기호에 까지 수백만개 이상의 다른 종류의 문자를 를 처리할 수 있는 python string으로 생각할 수 있다. 보통(Normal) python string은 인코드(encode)되어 있는데, 이는 ASCII, ISO-8859-1 혹은 UTF-8과 같은 encode를 사용한다고 보면 된다. 만약 당신이 0-9, A-Z와 같은 표준 128 ASCII 문자등을 저장하려 한다면, 당신 string이 사용하는 encoding을 사용하는지에 대해 함께 기록해야 한다. 그렇지 않다면, 출력할 때 얼그러지게 나타나게 된다. 임의의 encoding으로 저장한 data를 다른 encoding으로 조합을 시도할 때 혹은 특정 encoding을 가정한 application에서 출력을 시도할 때 문제가 발생하게 된다. 우리 모두 "??? ??????" 혹은 이상한 문자로 채워진 e-mail과 web page를 본적이 있을 것이다. 이는 encoding 문제가 있음을 시사한다. Unicode object는 그자체가 encoding이 없다. 즉, 알려진 "Unicode"로 불리는 일정하고 전세계적인 문자열 집합을 사용한다. python으로 unicode object를 사용하게 되면 encoding 이슈없이 안전하게 섞거나 매치할 수 있다. django는 Unicode object를 framework 구석 구석 사용한다. Model object는 Unicode object를 전달받고, Unicode data로 view는 상호동작하게 되고, template는 Unicode로 render한다. 일반적으로 당신의 encoding이 맞다고 확신하는데 아무런 걱정을 할 필요는 없다. 이는 매우 높은 수준이고, Unicode object의 개념을 너무 단순화시겼다. 그리고 여기를 통해 좀더 자세히 학습할 수 있다. |
__unicode__() 추가로 인해 변경 사항을 적용하려면, python shell을 다시 시작해야 한다. 이제 Publisher object는 훨씬 이해하기가 쉽다.
| C:\mydjango\mysite>python manage.py shell Python 2.5.4 (r254:67916, Dec 23 2008, 15:10:54) [MSC v.1310 32 bit (Intel)] on win32 Type "help", "copyright", "credits" or "license" for more information. (InteractiveConsole) >>> from books.models import Publisher >>> publisher_list = Publisher.objects.all() >>> publisher_list [<Publisher: Apress>, <Publisher: O'Reilly>] >>> |
마지막으로 __unicode__()는 model에게 행위(behavior)를 추가하는 좋은 예제가 된다. django model은 하나의 object에 대해 database table layout 그 이상을 기술한다. 또한 그것을 어떻게 할 것인지에 대한 기능도 기술할 수 있다. __unicode__()는 이러한 기능에 대한 예인데, 이는 그 자신을 어떻게 출력할 것인지를 알게 한다.
data 삽입과 업데이트
여기까지 봐 왔다면, database에 row를 삽입하기 위해서, model instance를 argument를 이용하여 생성하였다.
| >>> p = Publisher(name='Apress', ... address='2855 Telegraph Ave.', ... city='Berkeley', ... state_province='CA', ... country='U.S.A.', ... website='http://www.apress.com/') |
| >>> p.save() |
INSERT INTO books_publisher
(name, address, city, state_province, country, website)
VALUES
('Apress', '2855 Telegraph Ave.', 'Berkeley', 'CA',
'U.S.A.', 'http://www.apress.com/');Publisher model은 id라는 자동으로 증가하는 primary key를 사용하기 때문에, save() 호출은 더 많은 일을 수행하게 된다. 해당 record를 위해 primary key 값을 계산하고 instance에 id 속성을 세팅한다.| >>> p = Publisher(name='Apress', ... address='2855 Telegraph Ave.', ... city='Berkeley', ... state_province='CA', ... country='U.S.A.', ... website='http://www.apress.com/') >>> p.id >>> p.save() >>> p.id 5 >>> p.save() >>> p.id 5 >>> |
| >>> p.name = 'Apress Publishing' >>> p.save() |
UPDATE books_publisher SET
name = 'Apress Publishing',
address = '2855 Telegraph Ave.',
city = 'Berkeley',
state_province = 'CA',
country = 'U.S.A.',
website = 'http://www.apress.com'
WHERE id = 52;field의 모든것이 updated되는데, 변경된 하나만 시도 되는것이 아니다. application에 따라 이는 race condition을 유발할 수 있다. 아래 "하나의 statement로 여러개의 object 업데이트하기"에서 조금 다른 다음과 같은 query를 하는 방법이 설명된다.UPDATE books_publisher SET
name = 'Apress Publishing'
WHERE id=52;Object 선택하기database record를 생성하고 업데이트하는 방법을 아는 것은 필수적이지만, 당신이 만들 web application은 새로운 하나를 만드는것 보다 기존의 object의 query가 훨씬더 자주있을 것이다. 여태껏 주어진 model에 대해 모든 record를 받는것만 확인해왔다.
| >>> Publisher.objects.all() [<Publisher: Apress>, <Publisher: O'Reilly>] |
SELECT id, name, address, city, state_province, country, website FROM books_publisher;
| 알림 django는 모든 field를 명시적으로 list할 때 SELECT * 를 사용하지 않는다. 이는 설계에 의해서 이다. 특정 상황에서 SELECT * 는 느리고 더 중요한건 python의 "명시적인것이 암묵적인것 보다 좋다(Explicit is better than implicit.)"라는 교리(tenent of the zen of python)를 보다 더 따르기 위해서 이다. python의 선(zen)은 import this를 입력해 보기 바란다. |
- 처음으로 우리가 Publisher로 정의한 model을 사용한다. 여기서 놀랄것은 없다. data를 찾기 원한다면, 해당 data를 위해 model을 사용하면 된다.
- 다음으로 objects 속성을 사용한다. 이를 관리자(manager)라고 불려진다. 관리자는 추후 chapter 10에서 다뤄질 예정이다. 여기에서는, 당신이 알기 원하는것은, 관리자가 중요한것을 포함하는 data에 모든 "table-level" 동작을 처리하는가, 일것이다.
모든 model은 자동으로 objects 관리자를 가지고 있다. model instance를 찾기 원할 때 마다 언제든 사용할 수 있다. - 마지막으로 all()이다. 이는 objects 관리자에서 database의 모든 row를 전달해라는 method이다. 비록 이것이 list 처럼 보일지라도 실제로는 QuerySet이다. 이는 database로 부터 row의 특정 집합을 표현한 object이다. 이 chapter의 나머지에서 list처럼 다루는 방법을 볼 것이다.
어떠한 database 검색도 이러한 패턴을 가진다. query를 위해 model에 첨부된 manager의 mehtod를 호출한다.
data 필터링하기
database로 부터 모든것을 선택(select)하는 경우는 매우 드문 일이다. 대부분 data의 일부를 다루길 원할 것이다. django에서 filter() method를 이용하여 data를 필터할 수 있다.
| >>> Publisher.objects.filter(name='Apress') [<Publisher: Apress>] |
SELECT id, name, address, city, state_province, country, website FROM books_publisher WHERE name = 'Apress';다음과 같이 여러개의 argument를 전달할 수 있다.
| >>> Publisher.objects.filter(country="U.S.A.", state_province="CA") [<Publisher: Apress>] |
SELECT id, name, address, city, state_province, country, website FROM books_publisher WHERE country = 'U.S.A.' AND state_province = 'CA';기본 검색은 SQL의 = 연산자이다. 다른 검색(lookup) type은 다음과 같다.
| >>> Publisher.objects.filter(name__contains="press") [<Publisher: Apress>] |
SELECT id, name, address, city, state_province, country, website FROM books_publisher WHERE name LIKE '%press%';많은 형태의 lookup type이 가능하며, icontains(대소문자 구별 없는 LIKE), startswith와 endswith, 그리고 range(SQ의 BETWEEN)등이 있다. 부록 C에 관련 사항들이 정리되어 있다.
단일 object 구하기
filter() 예제는 list처럼 사용할 수 있는 QuerySet을 전달받아 사용한다. 만약 list와 반대로 object 한개만 가져올 수 있다면 편리할 때가 있다. 이는 get() method를 이용한다.
| >>> Publisher.objects.get(name="Apress") <Publisher: Apress> |
| >>> Publisher.objects.get(country="U.S.A.") Traceback (most recent call last): ... MultipleObjectsReturned: get() returned more than one Publisher -- it returned 2! Lookup parameters were {'country': 'U.S.A.'} |
| >>> Publisher.objects.get(name="Penguin") Traceback (most recent call last): ... DoesNotExist: Publisher matching query does not exist. |
try: p = Publisher.objects.get(name='Apress') except Publisher.DoesNotExist: print "Apress isn't in the database yet." else: print "Apress is in the database."data 정렬(ordering)하기
여태까지의 예제를 보면, 꼭 random 순서로 object를 전달 받은 것처럼 보였을 것이다. 어떤것을 상상하지는 말라. database가 어떤 순서로 결과를 보고하는지에 대해 아직 얘기하지 않아서, database에 의해 선택된 몇몇 전용 순서로 봤을 뿐이다.
django application에서 특정 값에 따라 결과 순서를 정렬하고 싶을 때가 있다(예를 들어, 알파벳 순서). 이를 위해 order_by() method를 사용한다.
| >>> Publisher.objects.order_by("name") [<Publisher: Apress>, <Publisher: O'Reilly>] |
SELECT id, name, address, city, state_province, country, website FROM books_publisher ORDER BY name;당신이 원하는 어떤 field도 순서를 정할 수 있다.
|
>>> Publisher.objects.order_by("address") >>> Publisher.objects.order_by("state_province") |
| >>> Publisher.objects.order_by("state_province", "address") [<Publisher: Apress>, <Publisher: O'Reilly>] |
| >>> Publisher.objects.order_by("-name") [<Publisher: O'Reilly>, <Publisher: Apress>] |
class Publisher(models.Model): name = models.CharField(max_length=30) address = models.CharField(max_length=50) city = models.CharField(max_length=60) state_province = models.CharField(max_length=30) country = models.CharField(max_length=50) website = models.URLField() def __unicode__(self): return self.name class Meta: ordering = ['name']이제 새로운 개념인 class Meta: 를 소개한다. 이는 Publisher class 정의(class Publisher의 편집 Tab) 내부에 포함되어 있다. 다양한 model에서 지정할 수 있는 옵션을 결정하기 위해 사용된다. Meta의 모든 참조는 부록 B에서 구할 수 있으며, 여기에서는 ordering 옵션만 다루도록 한다. 만약 이 값을 지정한다면, django로 하여금 order_by()가 지정되지 않았을 때 모든 Publisher objects는 name field에 의해 정렬되도록 알려준다.
검색(lookup) 연쇄처리
data를 필터하는 방법을 알아봤는데, 이제 그것을 정렬해 보도록 한다. 이는 검색과 함께 "연결"하면 된다.
| >>> Publisher.objects.filter(country="U.S.A.").order_by("-name") [<Publisher: O'Reilly>, <Publisher: Apress>] |
SELECT id, name, address, city, state_province, country, website FROM books_publisher WHERE country = 'U.S.A' ORDER BY name DESC;data 자르기(slicing)
또다른 통상적인 요구사항은 고정된 크기의 row를 검색하는 일이다. database에 수천개의 publisher가 있는데, 첫번째 하나만 구하기를 원할 떄이다. 이는 python의 표준 list 자르기 문법을 사용하면 된다.
| >>> Publisher.objects.order_by('name')[0] <Publisher: Apress> |
SELECT id, name, address, city, state_province, country, website FROM books_publisher ORDER BY name LIMIT 1;유사하게 다음과 같이 범위 지정이 가능하다.
| >>> Publisher.objects.order_by('name')[0:2] |
SELECT id, name, address, city, state_province, country, website FROM books_publisher ORDER BY name OFFSET 0 LIMIT 2;음수값으로 자르기는 지원되지 않는다.
| >>> Publisher.objects.order_by('name')[-1] Traceback (most recent call last): ... AssertionError: Negative indexing is not supported. |
| >>> Publisher.objects.order_by('-name')[0] |
단일 statement로 여러개의 object를 업데이트하기
"data 삽입과 업데이트" 부분에서 봤듯이 save() method로 row의 모든 column을 업데이트할 수 있다. application에 따라 column의 부분집합만 update하기를 원할 수 있다.
예를 들어, Apress Publisher를 'Apress'에서 'Apress Publishing'으로 업데이트하고자 한다면, 아래와 같이 save()를 이용한다.
| >>> p = Publisher.objects.get(name='Apress') >>> p.name = 'Apress Publishing' >>> p.save() |
SELECT id, name, address, city, state_province, country, website
FROM books_publisher
WHERE name = 'Apress';
UPDATE books_publisher SET
name = 'Apress Publishing',
address = '2855 Telegraph Ave.',
city = 'Berkeley',
state_province = 'CA',
country = 'U.S.A.',
website = 'http://www.apress.com'
WHERE id = 52;(publisher id가 52인 경우로 가정함)이 예를 통해 django의 save() method는 cloumn의 모든 것을 세팅하는데, name column만 하는 것이 아니다. 만약 다른 column이 다른 process에서 변경이 가능한 환경이라면, 변경하고자 하는 column만 변경하는것이 보다 올바르다. 이를 위해 QuerySet object의 update() method를 사용한다. 다음은 그 예이다.
| >>> Publisher.objects.filter(id=52).update(name='Apress Publishing') |
UPDATE books_publisher SET name = 'Apress Publishing' WHERE id = 52;update() method는 어떤 QuerySet에서도 동작하며, 이는 규모가 있는 여러개의 record를 편집가능하다는 뜻이다. 아래는 'U.S.A'로 된 country를 'USA'로 변경하는 예제이다.
| >>> Publisher.objects.filter(country="U.S.A").update(country="USA") 2 |
object 삭제하기
database로 부터 object를 삭제하기 위해, object의 delete() method를 호출한다.
| >>> p = Publisher.objects.get(name="O'Reilly") >>> p.delete() >>> Publisher.objects.all() [<Publisher: Apress Publishing>] |
| >>> Publisher.objects.filter(country='USA').delete() >>> Publisher.objects.all().delete() >>> Publisher.objects.all() [] |
| >>> Publisher.objects.delete() Traceback (most recent call last): File "<console>", line 1, in <module> AttributeError: 'Manager' object has no attribute 'delete' |
| >>> Publisher.objects.all().delete() |
| >>> Publisher.objects.filter(country='USA').delete() |
'django > the django book study' 카테고리의 다른 글
| [06] django admin site (2) | 2012/04/27 |
|---|---|
| [05] django의 model (1) | 2012/04/12 |
| [04] django의 template (2) | 2012/04/05 |
| [03] django 1.4의 view와 urlconfs (0) | 2012/04/04 |
| [02] django 1.4 설치와 project 생성하기 (0) | 2012/03/30 |
| [01] django 소개 (0) | 2012/03/28 |

Prev
Rss Feed