[05] django의 model
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은 아래표의 값중 하나로 선택되어야 한다.
설정값(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.objects.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' 카테고리의 다른 글
[07] django의 form 처리 (0) | 2012.05.06 |
---|---|
[06] django admin site (3) | 2012.04.27 |
[04] django의 template (14) | 2012.04.05 |
[03] django 1.4의 view와 urlconfs (5) | 2012.04.04 |
[02] django 1.4 설치와 project 생성하기 (1) | 2012.03.30 |