파이썬으로 CGI 프로그램 작성하기

파이썬(Python)으로 CGI 프로그램 작성하기

이강성

2000.1.26

차례

여기 사용된 소스를 다운 받으려면 여기(WinZip 파일) 또는 여기(tar.gz 파일)를 클릭하세요!!!

 

왜 파이썬으로 CGI프로그램을 작성해야 하는가?

먼저, 파이썬(Python)은 범용 언어라는 것을 말해야겠다. 파이썬은 대부분의 웹 프로그래밍에 적당하지만, 단지 몇 개의 SSI(Server Side Include) 페이지가 필요하다면 PHP와 같은 언어가 더 적당할 것이다.

Perl은 CGI의 ‘왕’으로 알려져있다. Perl은 텍스트 스캐닝과 프로세싱에 잘 맞는 언어이고, 확장된 CGI 모듈은 잘 설계되었다. 하지만, 전문적인 Perl 프로그래머를 제외하고는 Perl을 이용하여 빠른 어플리케이션을 작성하기란 보통의 프로그래머로는 생각하기 어렵다. 펄의 잡음 같은 문법과 ‘어떤 일을 수행하는데는 한가지 이상의 방법이 존재한다’라는 철학은 관리하기 어려운 복잡한 코드를 만들어 낸다.

괜찮고 강력한 프로그램이 펄로 쓰여질 수 없다고 이야기하는 것은 아니다. 때때로 다른 사람의 복잡한 코드를 이해하기 위해서 언어 자체에 관한 지식이 필요하다. 만일 여러분이 처음부터 웹 프로그래밍을 시작하려 한다면 파이썬을 진지하게 고려해봐아야 할 것이다. ‘그 일을 하기에는 바로 이런 방법이 존재해!’ 와 같이 명백한 방법론을 제공하는 파이썬을 말이다.

파이썬은 다른 어떤 언어보다도 더 깔끔한 문법을 갖는다. 또한, 파이썬 자체가 객체지향을 위해서 만들어 졌다. 하지만 클래스를 사용하건 말건 그것은 사용자에 달렸다. 클래스를 사용하지 않더라도 모듈단위의 객체 개념을 사용할 수 있다. 파이썬은 깔끔하고, 읽기 좋은 구문 구조는 이 언어를 배우는 사람들에게 ‘축복’이라고까지 할 수 있다.

아파치 서버에서 프로그램을 개발하는 사람들은 mod_python 이나 pyApache 모듈이 아파치 안에 숨겨져서 펄 프로그램의 수행속도와 견줄 정도가 되는 것에 놀랄 것이다.

최근에 파이썬은 매우 확장적이고, 잘 정리된 다양한 유용한 포터블 라이브러리를 갖는 모듈이 개발되어왔다. 인터넷 관련 모듈은 상당히 흥미있어서, 파싱, URL검색부터 POP서버로부터 메일을 가져 오는 것까지, 그리고 그 사이의 모든 기능이 있다. GUI부터 데이터베이스까지 다루는 또 다른 모듈들도 존재한다.

 

첫 번째 CGI 프로그램

여기서 우선, 여러분이 파이썬 실행 환경을 갖추고 있고 웹서버가 준비되어있다고 가정하겠다. (그렇지 않으면 더 진행할 수 없다.)  먼저 설명해야 할 것은 여러분이 아파치 서버를 사용하고 있다면 mod_python 혹은 pyApache를 사용하기 바란다( http://www.modpython.org/,http://www.bel-epa.com/pyapache/ ) 그 모듈은 인터프리터를 시작하고, 연결을 설정하는 시간을 절약해 줄 것이다.

여러분이 유닉스에서 아파치 서버를 사용하고 있다면, CGI가 기본적으로 위치해야 할 디렉토리는

      user_home/public_html/cgi-bin  (예: /home/gslee/public_html/cgi-bin)

이거나 그 서브디렉토리일 것이다. user_home은 여러분의 홈 디렉토리이다. (시스템 설정에 따라 달라지니 확인해 보아야 한다.) 다음에 여러분이 작성할 첫 번째 CGI 프로그램을 보인다. 파일명은 hello.py라 하자. 아무 텍스트 편집기를 사용하여 다음 파일을 만든다.


#!/usr/bin/python

# Tell the browser how to render the text
print "Content-Type: text/plain\n\n"

print "Hello, Python!"  # print a test string

Linux/Unix를 쓰고 있다면 파일을 만들고 난 후 반드시 chmod +x hello.py를 하여 실행모드를 주는 것을 잊지 말자!!! 첫 번째 라인

#!/usr/bin/python

은 Linux/Unix 시스템에만 필요하지만, 다른 운영체제에서는 문제를 일으키지 않는다. 파이썬 인터프리터를 자동적으로 호출하기 위한 경로명이다. 따라서 이 경로명은 정확해야 한다. /usr/local/bin/python 인 겅우도 있다. 윈도우즈나 매킨토시에서는 파이썬 파일을 실행하도록 다른 절차를 거쳐야 한다. 첫 번째 문자가 #이라면 주석(comment)문이다.

# Tell the browser how to render the text

이것 역시 주석(comment)문이다.

print "Content-Type: text/plain\n\n"

표준 출력으로 Content-Type: text/plain 와 두 개의 new-line코드를 출력한다. 표준 출력은 서버를 통해서 클라이언트(웹브라우저)로 전달된다. 이 첫 번째 출력 내용은 어떤 형식으로 문서가 구성되어 있다는 것을 나타낸다. text/plain은 일반 텍스트 문서임을 나타낸다. 나중에 text/html 문서를 보내는 방법을 배울 것이다. \n은 줄 바꾸기 키인 Enter키 코드에 해당한다. 이것은 CGI 문서 교환에 있어서 표준이다. 두 개의 new line이 필요하다.

print "Hello, Python!"  # print a test string

이 문은 단순히 Hello, Python!을 출력한다. 이 출력은 화면이 아닌 웹 브라우저로 전달된다.

자, 이제 웹 브라우저에서 이 CGI 모듈을 호출해 보자. hello.py가 gslee/public_html/cgi-bin 에 저장되어 있고, 아파치 서버가 cgiwrap을 이용하여 개인 사용자(gslee)에게 cgi 쓰기를 허용한다면,

http://sr.kwangwoon.ac.kr/cgi-bin/cgiwrap/~gslee/hello.py

와 같이 호출할 수 있다. 만일 루트의 권한이 있어서 /home/httpd/cgi-bin에 hello.py를 저장한다면, 다음과 같이 호출한다.

http://sr.kwangwoon.ac.kr/cgi-bin/cgiwrap/~gslee/hello.py

화면에 “Hello Python!”이 나온다면 성공!!!!

 

폼 태그와 연결하기

앞 예는 CGI의 동작을 보여주는 실용적인 예로서는 충분하지 않다. 여기서는 간단한 실제의 CGI 프로그램 예를 보여준다. 파이썬에 대해서 잘 모르면 설명서 윈도우를 올려놓고 함께 참고하는 것도 좋은 방법일 것이다.

먼저 알아야 할 것은, 어떻게 HTML 문서의 폼 태그로부터 정보를 얻어 오는 가이다. 파이썬은 이를 위한 훌령한 모듈을 제공한다. 그 모듈이름은 우연하게도 cgi이다(^^).  예를 보자.

HTML 폼에 있는 데이터는 input 요소의 이름으로 참조된다. 예를 들어 <INPUT TYPE=TEXT NAME=email SIZE=40> 는 email이란 이름의 40자 폭의 텍스트 박스를 만들어낸다.

이 정보는 CGI로 GET이나 POST 방법으로 전달된다. 하지만 신경쓸 것 없다. cgi 모듈은 이 방법의 차이를 여러분으로부터 숨기며, 파이썬의 기본 데이터형-사전-으로 값을 전달한다. 사전은 숫자가 아닌 내용으로 참조되는 배열과 유사하다. 파이썬 cgi모듈은 FieldStorage라 불리는 메써드를 제공한다. 이 메써드는 모든 폼 <INPUT>과 그에 대응하는 값을 저장한 사전을 리턴한다. 원하는 INPUT 요소의 이름을 알면 다음과 같이 쉽게 값을 얻을 수 있다.


import cgi
The_Form = cgi.FieldStorage()
print The_Form["email"].value

 
The_Form = cgi.FieldStorage()

는 폼의 input 이름과 값을 저장한 사전을 돌려주고,

print The_Form["email"].value

는 ’email’ 태그의 값을 출력한다. 물론

email = The_Form['email'].value

와 같이 값을 변수에 저장할 수도 있다. 만일 폼 태그로 넘어오는 전체 변수이름들을 알고 싶으면

print The_Form.keys()

값들 목록을 보고 싶으면

print The_Form.values()

한다. 단지 위의 순서는 보장이 안된다는 것을 명심하자. 만일 정의되지 않은 혹은 값이 없는 필드를 얻고자 하면 예외가 발생한다. 만일 이것을 여러분이 처리하지 않으면 스크립트는 중단된다. 그래서 “Internal Server Error”를 보게 될 것이다. 예외처리의 자세한 내용은 파이썬 매뉴얼을 참조하기 바란다. 간단히 처리하는 예를 여기서 보이면 다음과 같다.


import cgi
The_Form = cgi.FieldStorage()
try:
email = The_Form["email"].value
except (KeyError, TypeError):
email = ''
print email

종합해서 간단한 프로그램을 작성해 보자. HTML 폼이 제공하는 모든 변수를 출력하는 예를 작성해 보자. (prfield.py)


#!/usr/bin/python

import cgi   

print "Content-Type: text/plain\n\n"

The_Form = cgi.FieldStorage()

for name in The_Form.keys():
    print "Input: " + name + " value: " + The_Form[name].value + "
"

print "Finished!"

보는 바와 같이, 이 프로그램은 HTML 폼에서 제공하는 모든 변수의 값을 출력한다.

for name in The_Form.keys():

는 모든 변수의 이름에 대해서 반복하는 제어문이고, 각 변수이름에 대하여 다음 문을 수행한다.

    print "Input: " + name + " value: " + The_Form[name].value + "

여기서 name은 변수명이 되고, The_Form[name].value는 그에 해당하는 값이다. 문자열에 있어서 ‘+’ 연산은 두 개의 문자열을 붙인다.

HTML 문서에는 다음과 같은 폼을 만들 수 있다. (http://sr.kwangwoon.ac.kr/~gslee/prfield.html)

<form name=”form” method=”get” action=”http://sr.kwangwoon.ac.kr/cgi-bin/cgiwrap/~gslee/prfield.py”&gt;
<p>
email:<input type=”text” name=”email”>
name:<input type=”text” name=”name”>
<input type=”submit” value=”실행”>
</p>
</form>

폼 태그에서 action=”http://sr.kwangwoon.ac.kr/cgi-bin/cgiwrap/~gslee/prfield.py 는 호출할 cgi 프로그램의 URL을 기술한다.

 

Display 함수를 정의하자

앞의 예를 봐서 알겠지만, CGI란 생성된 정보를 갖는 새로운 문서를 만들어 주는 프로그램이다. 정보를 생성하는 것은 응용에 따라서 쉬울 수도 있고 어려울 수도 있다. 그 것은 일반 프로그래밍과 다르지 않다. 그러나 어떠한 경우에나 문서를 좀 더 멋지게 보이기 위해서는 HTML 문서 형식으로 출력해야 하는데, 이 것을 CGI프로그램상에서 하기에는 좀 번거롭다. 물론 많은 경우 그렇게 하기도 하지만, 프로그램이 커질수록 관리하기가 점점 더 어려워진다. 가장 좋은 해결 방법은 HTML문서의 틀은 HTML 문서 편집기로 편집한다(디자인). 그리고 그 틀 안의 원하는 위치에 CGI에서 생산된 정보를 배치시키면 디자인과 프로그램 작업을 분리할 수 있다.

다음의 예를 보자. 실제적인 HTML문서는 이것 보다는 훨씬 더 복잡하겠지만 기본적인 아이디어를 설명하기 위하여 가장 간단한 문서를 만들었다.

template.html

<html>
  <head>
    <META NAME="keywords" CONTENT="blah blah -- your ad here">
    <title>Python is Fun!</title>
  </head>
  <body>
                <!-- *** INSERT CONTENT HERE *** -->
  </body>
</html>

문서중에 <!– *** INSERT CONTENT HERE *** –> 부분은 CGI에서 생성된 정보로 대치될 부분이다. 파이썬 프로그램은 이 부분을 찾아서 다른 문자열로 대치한다. 이것을 수행하기 위해서 한 문자열을 다른 문자열로 대치하는 방법이 필요하다. 정규식(Regular Expression:RE)으로 이것이 가능하다. 파이썬의 PE는 펄의 것과 거의 동일하다. 하지만 이것이 언어의 일부로 설계된 것은 아니고 모듈(re)로서 제공된다. 정규식에 대해서 더 알고 싶으면  the Regular Expression HOWTO를 참고하기 바란다.

모듈 re는 subn 함수를 제공하는데 형식은 다음과 같다.

subn (pattern, repl, string)

string에서 pattern을 찾아서 repl로 바꾼다. 예를 들면 다음의 예는

res = re.subn('abcd', 'xyz', 'abcdefghi')
print res
('xyzefghi', 1)

의 출력을 낸다. ‘abcd’를 찾아서 ‘xyz’로 바꾼다. 출력은 터플인데 첫 번째 값은 치환된 스트링, 두 번째는 성공했으면 1, 실패했으면 0이다. 따라서 res[0] 은 ‘xyzefghi’이고 res[1]은 1이다.

Display() 함수는 하나의 인수(Content)를 받는데 이 것은 <!– *** INSERT CONTENT HERE *** –> 위치에 들어갈 문자열이다.

자 이제 함수 Display를 만들 모든 준비는 됐다.


import re  # 정규식 모듈 사용할 수 있게 import 한다

# 틀 파일을 지정한다
TemplateFile = "template.html"

# Display 함수 정의
# 하나의 인수를 받음 - 표시할 문자열임
def Display(Content):
    fp = open(TemplateFile, "r")  # 파일을 읽기모드로 연다
    # 전체 파일을 읽어 들인다
    TemplateInput = fp.read()     
    fp.close()                    # 파일을 닫는다

    # 틀 파일이 망가져서 예외가 발생했을 때 출력할 예외 문자열을 정의
    BadTemplateException = "틀 파일에 문제가 생겼어요!"

    SubResult = re.subn("<!-- *** INSERT CONTENT HERE *** -->",  
        Content,TemplateInput)
    if SubResult[1] == 0:
        raise BadTemplateException  # 예외를 발생시킨다

    print "Content-Type: text/html\n\n"
    print SubResult[0]

BadTemplateException은 틀 파일이 망가졌을 때 예외를 발생시키기 위해서 정의되었다. 만일 틀 파일에서 주석을 발견할 수 없을 때는 치명적인 에러를 발생시키고 프로그램을 중단한다. 또한 프로그램이 단순히 틀 파일을 open 할 수 없을 때에도 (unhandled, fatal) 예외를 발생시킨다.

앞서 설명했듯이 다음 문은 틀 파일 텍스트(TemplateInput) 의 <!– *** INSERT CONTENT HERE *** –>를 Content 변수에 저장된 문자열로 바꾼다.

    SubResult = re.subn("<!-- *** INSERT CONTENT HERE *** -->",  
        Content,TemplateInput)

subn 함수는 터플을 돌려주는데 첫 번째 값은 치환된 문자열이고 두 번째는 정수값으로 성공했으면 1, 실패했으면 0의 값을 갖는다.

여러분은 이제 자신의 CGI 프로그램에서 이 함수를 사용할 수 있다. 출력할 완전한 문자열이 준비되면 Display함수를 불러라.

 

폼을 생성하고 폼을 처리하는 하나의 CGI를 만들자

앞서의 예에서는 폼을 생성하는 HTML문서와 폼을 처리하는 CGI 프로그램이 별도로 작성되었다. 여기에서는 하나의 CGI 프로그램이 폼을 생성하고, 생성된 폼에서 요구하는 내용을 처리하는 프로그램을 작성하자. 먼저, 사용될 폼을 다음에 보인다.

form.html

    
<form method="post" action="sample1.py">       
      <INPUT TYPE=HIDDEN NAME="key" VALUE="process">
      Your name:<br>
      <input type="text" name="name" size="60">
      <br>
      Email: (optional)<br>
      <input type="text" name="email" size="60">
      <br>
      Favorite Color:<br>
      <input type="radio" name="color" value="blue" checked>Blue
      <input type="radio" name="color" value="yellow">No, Yellow...
      <input type="radio" name="color" value="swallow">What do you 
               mean, an African or European swallow?
      <p>
      Comments:<br>
      <textarea name="comment" rows="8" cols="60">      
      </textarea>
      <p>
      <input type="submit" value="Okay">
</form>

전체 프로그램은 다음에 보인다.

sample1.py:

#!/usr/bin/python 

import re
import cgi 

# 틀 파일 이름
TemplateFile = "template.html" 

# 사용자에게 보여줄 폼 파일 이름
FormFile = "form.html" 

# Display 함수.  출력할 문자열인 하나의 인수를 받는다.
def Display(Content):
    TemplateHandle = open(TemplateFile, "r")  # 파일 읽기모드로 오픈
    # 전체 파일을 읽는다
    TemplateInput = TemplateHandle.read()
    TemplateHandle.close()                    # 파일 클로우즈
    # 틀 파일이 잘못 되었을 때의 예외 정의
    BadTemplateException = "HTML 틀 파일에 문제가 생겼어요~" 

    SubResult = re.subn("<!-- *** INSERT CONTENT HERE *** -->", 
                Content,TemplateInput)
    if SubResult[1] == 0:
        raise BadTemplateException 
    print "Content-Type: text/html\n\n"
    print SubResult[0] 

### 틀을 보여주고, 그 것을 처리하는 두 개의 매인 행동 함수 정의

# 간단한 함수임.
def DisplayForm():
    FormHandle = open(FormFile, "r")
    FormInput = FormHandle.read()
    FormHandle.close()     
    Display(FormInput) 

def ProcessForm(form):    
    # 폼에서 정보를 쉽게 추출..
    try:
        name = form["name"].value
    except:
        # name 이 정의안되었으면 메시지 출력하고 종료
        Display("이름은 입력하셔야 합니다. 되돌아가 주세요")
        raise SystemExit
    try:
        email = form["email"].value
    except:
        email = None
    try:
        color = form["color"].value
    except:
        color = None
    try:
        comment = form["comment"].value
    except:
        comment = None 

    Output = ""  # 출력 버퍼 초기화
    Output = Output + "Hello, "     

    if email != None:
        Output = Output + "<A HREF="mailto:" + email + "">" +  name + "</A>.<P>"
    else:
        Output = Output + name + ".<P>"     

    if color == "swallow":
        Output = Output + "You must be a Monty Python fan.<P>"
    elif color != None:
        Output = Output + "Your favorite color was " + color + "<P>"
    else:
        Output = Output + "You cheated!  You didn't specify a color!<P>"     
    if comment != None:
        Output = Output + "In addition, you said:<BR>" + comment + "<P>"     

    Display(Output) 

###
### 실제 스크립트 여기서 시작
### 

### CGI 요구 평가
form = cgi.FieldStorage() 

### "key" 는 폼에서 숨겨진 요소임.
try:
    key = form["key"].value
except:
    key = None 
if key == "process":
    ProcessForm(form)
else:
    DisplayForm()

Display 함수는 앞서 설명했으므로 넘어가기로 하자.  파이썬은 동적 해석 언어이므로 함수는 사용되기 전에 먼저 정의되어야 한다.

DisplayForm() 함수는 form.html을 출력하는 함수이다. 즉, 사용자에게 입력을 요구하는 문서를 제공한다.

ProcessForm() 함수는 DisplayForm()함수에 의해서 출력된 폼으로 사용자가 요구를 제출(submit)했을 때 처리하는 함수이다. 처리는 두부분으로 구분해 볼 수 있다. 변수 값을 꺼내는 부분과, 꺼내진 값을 조합하여 출력할 내용을 만드는 부분이다. email과 color값을 기반으로 출력할 내용을 변수 Output에 저장한다.  그리고 최후에 만들어진 결과를 표시하기 위해 Display()함수를 호출한다.

ProcessForm() 이후의 마지막 부분이 실제 코드의 실행이 시작되는 부분이다. 폼의 key값이 process이면 ProcessForm()을 호출하고, 처음 호출할 때와 같이 값이 설정되어 있지 않으면 DisplayForm()을 호출한다.

 

 

파이썬에서 데이터베이스 사용하기

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 MySQLdb

MySQL 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)-Yes

description은 만일 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
Advertisements

답글 남기기

아래 항목을 채우거나 오른쪽 아이콘 중 하나를 클릭하여 로그 인 하세요:

WordPress.com 로고

WordPress.com의 계정을 사용하여 댓글을 남깁니다. 로그아웃 / 변경 )

Twitter 사진

Twitter의 계정을 사용하여 댓글을 남깁니다. 로그아웃 / 변경 )

Facebook 사진

Facebook의 계정을 사용하여 댓글을 남깁니다. 로그아웃 / 변경 )

Google+ photo

Google+의 계정을 사용하여 댓글을 남깁니다. 로그아웃 / 변경 )

%s에 연결하는 중