파이썬에서 데이터베이스 사용하기
CGI 프로그래밍에서 마지막으로 배워야 할 부분이 어떻게 하면 데이터베이스 질의언어 (SQL)를 파이썬에서 사용할 수 있을까 하는 것이다. 파이썬은 데이테베이스와 작업을 하기위하여 표준 API(Application Programming Interface)를 제공한다. 이 인터페이스는 제공되는 모든 데이터베이스에서 동일하게 사용할 수 있다. 따라서 여러분은 프로그램을 수정하지 않고도 어떠한 데이터베이스를 사용할 수 있다(이론적으로는).
현재 Python DBI (Database Interface) 대부분의 일반적인 데이터베이스를 지원한다 - Gadfly, mSQL, MySQL, Oracle, PostgreSQL, Informix, Interbase, Sybase, Solid
표준적인 인터페이스를 지원하지만 다른 데이터베이스를 사용하기 위해서는 각 DB에 맞는 모듈이 있어야 하는 것을 잊지는 말아라(노파심에서 한마디).
더욱 자세한 정보를 원한다면 the Database SIG (Special Interest Group)에 가보라.
여기서는 MySQL을 이용한 아주 단순한 읽기 질의를 다룬다. 처음에 해야 할 일은 모듈을 import하는 것이다.
import MySQLdbMySQL DBI 모듈을 불렀으면, 데이터베이스를 연결함으로 초기화한다. 이 연결을 위해서 클래스 인스턴스를 생성해야 하는데 형식은 다음과 같다.
MySQLdb.connect(host, user, passwd, db, port, unix_socket, client_flag)
이 인터페이스는 키워드 인수를 통해서 필요한 값만 전달한다.
host - hostname (NULL : default)
user - username (NULL)
passwd - password (no password)
db - database name (NULL)
port - integer, TCP/IP port
unix_socket - TCP를 사용할 unix소켓의 위치
client_flag - integer, 필요할 경우 사용하기 위한 flag (0)사용자를 gslee 암호를 secretpassword라 할 때 localhost에서 guestbook이라는 데이터베이스를 사용하기 위한 초기화 예는 다음과 같다.
--------------------------------------------------------------------------------
import MySQLdb
connection = MySQLdb.connect(user='gslee', passwd='secretpassword', db='guestbook')
cursor = connection.cursor() # 커서 객체를 얻어온다.
.....
cursor.close() #사용이 종료되면 닫아준다. (직접 안하면 자동으로 닫아진다)
--------------------------------------------------------------------------------
마지막 문장은 커서(cursor)라고 불리는 것을 리턴했다. 이 객체의 이름을 '커서'라고 하는데, 모든 데이터베이스의 액션은 커서를 통해서 이루어진다. 프로그램과 데이터베이스를 연결 해 주는 것이 커서의 역할이라 할 수 있다. 커서 객체는 execute()와 fetchall()과 같은 몇 개의 메써드를 가지고 있다. execute()는 실제적으로 SQL 문을 수행하기 위해 사용되며 fetchall()은 execute()의 결과를 터플 형식으로 얻기 위해서 사용된다. 각 터플은 데이터베이스의 행(레코드)에 대응된다.
커서가 있다면, cursor.execute(statement)와 같은 형식으로 데이터베이스가 지원하는 어떠한 SQL 문도 수행할 수 있다. 전체 레코드를 얻는 간단한 예를 아래에 보인다.
--------------------------------------------------------------------------------
#모든 엔트리를 얻는다.
myquery = "SELECT * FROM gbook ORDER BY stamp"
cursor.execute(myquery) #질의 수행
Result = cursor.fetchall() # 결과 가져오기
total = len(Result) # 결과 레코드 수 얻기
entries = []
if total < 1:
print 'No guest book entries!'
else:
for record in range(total):
entry = {} # 공 사전
entry['gid'] = Result[record][0] # guestbook ID
entry['stamp'] = Result[record][1] # time stamp
entry['name'] = Result[record][2] # ...
entry['email'] = Result[record][3]
entries.append(entry) # 리스트에 정보 추가
for entry in entries:
print entry['name'] + ' email:' + entry['email'] + '...'
--------------------------------------------------------------------------------
결과가 Result에 저장되며, 질의 결과로 넘어온 전체 레코드 수는 total에 저장된다. Result에 저장된 결과는 터플의 리스트 형식이므로 각 필드에 맞추어서 정보를 얻기 위해서는 필드 이름과 위치 정보를 미리 알고 있어야 한다. 0번째 필드는 guestbook ID로 사용되고 있는 것을 아는 상태에서 위와 같이 정보를 추출 할 수 있다. 이와 같은 사전 정보 없이 각 필드의 이름과 데이터 형, 크기등을 알고 싶다면 execute를 수행한 후, description 특성을 이용하면 정보를 얻을 수 있다. 이에 관한 자세한 사항은 Python Database API 2.0 문서를 참고하기 바란다.
for문에서 각 레코드 단위로 정보가 사전 형식으로 entry에 저장되고, 그 사전은 다시 entries 리스트에 추가된다.
마지막 for문은 저장된 정보를 출력한다.
데이터베이스를 다루는 것에 대해서 충분하게 다루지는 못했지만 CGI와 연결해서 다룰 만큼의 기초는 충분하다고 생각된다. 앞으로 문서가 갱신된다면 좀더 자세하게 데이터베이스를 다루는 루틴을 소개하겠다.
Python Database API 2.0 커서 객체에 대해서 좀 더 자세히
이 API는 데이터베이스를 사용하는 유사한 파이썬 모듈간의 호환성을 유지하기 위해서 정의되었다. 이것을 만들고 사용함으로 다른 데이터베이스에 변경없이 쉽게 적용 가능한 이해하기 쉬운 코드를 생성할 수 있다. (이것은 커다란 장점이다!!)
커서 객체
커서 객체는 질의를 수행하고 실행된 결과를 가져오기 위해서 사용된다. 커서 객체를 만드는 방법은 앞절에서 기술하였지만 다음과 같이 connection 객체로부터 얻어올 수 있다.
import MySQLdb
connection = MySQLdb.connect(user='guest', db='test')
cursor = connection.cursor() # 커서 객체를 얻어온다.일단 커서 객체를 받았으면 execute 메써드를 이용하여 질의를 수행할 수 있다.
cursor.execute('select * from pet')아무 메시지 없이 다름 프롬프트가 나오면 성공한 것이다. 질의 결과로 얻어진 각 필드의 특성을 알고 싶으면 description 특성(attribute)을 이용하라.
print cursor.description
(('name', 253, 8, 20, 20, 0, 1), ('owner', 253, 6, 20, 20, 0, 1),
('species', 253, 7, 20, 20, 0, 1), ('sex', 254, 1, 1, 1, 0, 1),
('birth', 10, 10, 10, 10, 0, 1), ('death', 10, 10, 10, 10, 0, 1))
결과는 7개 시퀀스의 시퀀스(터플)인데, 7개 값의 의미는 다음과 같다.
(name, type_code, display_size, internal_size, precision, scale, null_ok)
(필드명, 데이터형_코드, 표시크기, 내부크기, 정확도, 비율, null가능여부)('name', 253, 8, 20, 20, 0, 1)을 예를 들어 설명하면 다음과 같다.
필드이름('name')-name, 데이터형(253)-VARCHAR, 표시크기(8)-8,
내부크기(20)-20 (VARCHAR(20)으로 선언되었다.),
정확도-20, 비율-0(스트링에서 별 의미 없음), null가능(1)-Yesdescription은 만일 execute()를 호출한 적이 없거나 검색 결과 행을 가지고 있지 않다면 None값을 갖는다.
rowcount는 검색(select) 결과 레코드의 수 혹은 (update, insert등으로) 변경된 레코드의 수를 알려준다.
print cursor.rowcount
11L질의 결과를 가져오기 위해서는 fetchone(), fetchmany(), 혹은 fetchall() 메써드를 사용할 수 있다.
res = cursor.fetchone() # 결과 한 개 가져오기
res = cursor.fetchall() # 결과 모두 가져오기
res = cursor.fetchmany(10) # 결과 10개 가져오기이름에서 알 수 있듯이 fetchone()은 하나의 레코드를 터플로 리턴하며, fetchall()은 모든 레코드를 터플의 리스트로 리턴한다.
fetchmany(n)은 n개 만큼의 레코드를 가져온다. 만일 숫자가 지정되지 않으면 (기본값을 사용하면) cursor.arraysize 만큼의 레코드를 가져온다. 만일 충분하지 않은 레코드가 남아있다면 그 남아있는 레코드가 넘어온다. 가능한 한 일정한 수의 레코드를 요구하는 것이 시스템 성능에 도움을 준다.
만일 execute()에서 아무 결과가 없었다면 예외가 발생한다.
res = cursor.fetchall()의 질의 결과는 터플의 리스트 형태로 저장된다. 하나의 터플은 하나의 행(레코드)를 나타낸다.
res = cursor.fetchall() # 결과 모두 가져오기
print res
[('Puffball', 'Diane', 'hamster', 'f', '1999-03-30', None), ('buffy', 'harold',
'dog', 'f', '1990-05-13', '0000-00-00'), ('Claws', 'Gwen', 'cat', 'm', '1994-03-
17', '0000-00-00'), ('whistler', 'gwen', 'bird', 'n', '1997-12-09', '0000-00-00'
), ('Fluffy', 'Harold', 'cat', 'f', '1993-02-04', None), ('Chirpy', 'Gwen', 'bir
d', 'f', '1998-09-11', None), ('Bowser', 'Diane', 'dog', 'm', '1989-08-31', '199
5-07-29'), ('fang', 'benny', 'dog', 'm', '1998-08-27', '0000-00-00'), ('slim', '
benny', 'snake', 'm', '1996-04-29', '0000-00-00'), ('fang', 'benny', 'dog', 'f',
'1998-09-22', '0000-00-00'), ('slim', 'benny', 'snak', 'm', '1996-04-29', '0000
-00-00')]
좀더 품위 있게 정보를 출력하려면 다음과 같은 코드를 이용할 수 있다.
res = cursor.fetchall() # 결과 모두 가져오기
print "%-10s %-8s %-8s %1s %11s %11s" % ('name', 'owner', 'species', 's', 'birth', 'death')
print
for record in res:
print "%-10s %-8s %-8s %1s %11s %11s" % record
name owner species s birth death
Puffball Diane hamster f 1999-03-30 None
buffy harold dog f 1990-05-13 0000-00-00
Claws Gwen cat m 1994-03-17 0000-00-00
whistler gwen bird n 1997-12-09 0000-00-00
Fluffy Harold cat f 1993-02-04 None
Chirpy Gwen bird f 1998-09-11 None
Bowser Diane dog m 1989-08-31 1995-07-29
fang benny dog m 1998-08-27 0000-00-00
slim benny snake m 1996-04-29 0000-00-00
fang benny dog f 1998-09-22 0000-00-00
slim benny snak m 1996-04-29 0000-00-00
또는 다음과 같이 하나씩 출력할 수도 있다. 위와 같은 출력을 얻을 것이다.
print "%-10s %-8s %-8s %1s %11s %11s" % ('name', 'owner', 'species', 's', 'birth', 'death')
print
while 1:
record = cursor.fetchone() # 결과 하나 가져오기
if record == None: break # 없으면 빠져나감
print "%-10s %-8s %-8s %1s %11s %11s" % record
|