web2py 소개
By SeukWon Kang
회사에서 세미나 용으로 작성한 글인데 쓰고 보니 이것 만으로도 web2py의 소개 용으로는 충분 할 것 같아서 그리고 블로그에 올려도 좋을 듯한 내용이어서 올려 봅니다.
원문은 http://web2py.com/books/default/chapter/29/03 이며 번역을 한 것이 아니고 업무에 필요한/필요할 것 같은 것들을 정리한 글이라 원문과 같이 놓고 보셔야 이해가 될 듯 합니다. 제가 맡은 부분인 Overview 부분 만 입니다. ^^
web2py 개요 - overview
** **
시작하기 - startup
web2py는 윈도우와 맥에선 실행 프로그램으로도 제공되기 때문에 따로 python 이 필요하지 않다.
물론 linux 에서와 같이 소스 형태로 제공되는 것을 사용할 수도 있다. 이 경우는 별도로 python의 설치가 필요하다.
소스형태로 사용하려면
python web2py.py
맥에선
open web2py.app
윈도우에선
web2py.exe
와 같이 실행한다.
실행 시 여러 인자들을 받을 수 있는데. 그 인자들은
-h 인자를 주고 실행해서 볼 수 있다.
기본적으로는
http://localhost:8000/
http://127.0.0.1:8000/
으로(만) 접근 가능한 형태로 실행된다. ip를 0.0.0.0 으로 설정하면 외부 에서 접근가능한 형태가 된다.
실행시 -a 옵션이나 GUI 창에 관리앱인 admin용 암호를 줄 수 있는데 이 암호로만 관리앱 (admin) 에 접근이 가능하다.
실행 시에 이 관리앱 암호가 주어 지지 않으면 admin앱은 disable 된다.
apache + mod_proxy 로 접근 되는 경우가 아니면 localhost에서 만 접근 가능하며
만일 proxy가 인지 되면 session cookie 는 secure로 설정되어 https가 아닌 경우 admin로그인이 불가능해진다.
즉 보안 상의 이슈로 admin 접근은 https이거나 로컬 머신으로부터이어야만 한다.
이 과정을 거쳐서 실행하면 자동으로 브라우저를 위의 URL로 실행해줄텐데 자동으로 안열리면 위의 URL을 입력하자.
브러우저 상의 administrative interface 버튼을 클릭해서 관리앱로그인을 하며 이때 실행시 입력한 암호를 입력한다.
이 관리앱 계정?은 1개 뿐이며 암호는 매 실행시 마다 물어본다. 싫다면 <recycle>을 사용해 이전 암호를 사용할수도 있다. 이 계정은 web2py 앱들의 인증시스템과는 별개로 작동되며 무관하다.
관리앱 (admin) 을 들어가면 설치된 어플리케이션들을 볼수 있는데
기본적으론 admin , examples , welcome 이 존재 할것이다.
admin 은 현재 들어가있는 web2py 관리용 앱이며
examples 은 온라인 문서 == 공식 web2py 홈페이지의 복제 이고
welcome은 web2py 앱의 기본 틀로 만들어진 것이며 최초 실행시 본 사이트이다.
사용 준비가 된 web2py 응용프로그램은 web2py appliance 라고 부른다.
( 너무 기니까 우리는 web2py 앱 이라고 부르자. )
admin 앱의 site 페이지에서 여러 기능을 사용할수 있는데.
web2py 앱의 배포 패키지를 설치 하거나 삭제
새로운 앱 생성.
앱을 배포 가능 형태로 패키징 해서 다운 로드.
임시 파일들을 정리
enable / disable : 외부 머신으로부터의 접근을 허용/차단 상태로 설정. localhost나 proxy 를 통할때 영향을 받지 않는다. 앱 폴더에 DISABLED 파일을 생성한다. http 503 error를 리턴.
앱을 편집
같은 기능이 있다.
admin을 사용해서 새로운 앱을 만들면 welcome 앱의 복제를 사용해서 만들기 때문에 당장 작동 가능한 형태의 여러 파일들이 있는 형태로 만들어진다.
그리고 wizard를 통해서 앱을 만들수도 있는데 이경우는 각종 다양한 기능을 선택해가면서 생성할수 있다.
간단한 예제들 - simple examples
new simple application을 통해서 myapp을 만든다.
접근은 http://localhost:8000/myapp
admin에서 edit을 선택
- models
- 데이터 모델링 : M
- controllers
- biz logic , workflow : C
- views
- date presentation : V
- languages
- 다국어 지원
- modules
- 앱용 공용 라이브러리 코드
static files : image, css, js 등
- plugins
- 앱용 플러그인 설치 공간.
web2py applications 폴더를 보면 같은 이름의 폴더가 생성되어 있다.
http url request는 controllers 내의 함수를 호출하게 되며 자동으로 appadmin.py 와 default.py 파일이 만들어져 있을 것이다.
appadmin은 db data 관리용.
default 가 사용자의 request를 받을 파일이다.
default.py에
def index(): return “Hello from MyApp”
를 정의하면
http://127.0.0.1:8000/myapp/default/index
에 대응하게 된다.
여기에 대응하는 view는
default/index.html
html 내에 {{=controll가 리턴한 파이선 object }}
형태로 사용 가능
즉 C 에서
def index(): return dict(message=“Hello from MyApp”)
V 에서
<html> <head></head> <body> <h1>{{=message}}</h1> </body> </html>
와 같이 사용.
C가 딕셔너리를 리턴하면 web2py는 해당 V 를 view 폴더내의
[controller]/[function].[extension]
에서 찾으려고 한다.
extension은 요청받은 확장자이며 default는 html 이다.
찾지 못한경우 generic.확장자 를 시도.
기본 지원 확장자는 json, jsonp, xml, rss, ics, pdf 이다.
production site에서는 generic 에 의존해서는 안된다. - 개발 중에만 쓸것.
view를 강제 지정하고싶으면
response.view = ‘default/something.html’
같이 쓸것.
디버깅 용으로
{{=response.toolbar()}}
을 사용할수 있다.
lets count - 세션의 예
C 내에서 session object를 사용할수 있는데.
def index(): session.counter = (session.counter or 0) + 1 return dict(message=“Hello from MyApp”, counter=session.counter)
같이 쓸수 있다.
V는
<html> <head></head> <body> <h1>{{=message}}</h1> <h2>Number of visits: {{=counter}}</h2> </body> </html>
처럼 쓰면 된다.
폼/인자를 받는 법
- C
- default.py
def first(): return dict() def second(): return dict()
- V
- default/first.html
{{extend ’layout.html’}} <h1>What is your name?</h1> <form action=“second”> <input name=“visitor_name” /> <input type=“submit” /> </form>
V:default/second.html
{{extend ’layout.html’}} <h1>Hello {{=request.vars.visitor_name}}</h1>
postbacks :자주 쓰이는 방법. 자신에게 post 해서 validation을 한후 redirect
def first(): if request.vars.visitor_name: session.visitor_name = request.vars.visitor_name redirect(URL(‘second’)) return dict() def second(): return dict()
V : default/first.html
{{extend ’layout.html’}} What is your name? <form> <input name=“visitor_name” /> <input type=“submit” /> </form>
- V:default/second.html
- session 으로 부터 받는다.
{{extend ’layout.html’}} <h1>Hello {{=session.visitor_name or “anonymous”}}</h1>
- C
- first 재작성 .
def first(): form = FORM(INPUT(_name=‘visitor_name’, requires=IS_NOT_EMPTY()), INPUT(_type=‘submit’)) if form.process().accepted: session.visitor_name = form.vars.visitor_name redirect(URL(‘second’)) return dict(form=form)
또 다른 더 좋은 방법
C
def first(): form = SQLFORM.factory(Field(‘visitor_name’, label=‘what is your name?’, requires=IS_NOT_EMPTY())) if form.process().accepted: session.visitor_name = form.vars.visitor_name redirect(URL(‘second’)) return dict(form=form)
- V
- default/first.html
{{extend ’layout.html’}} {{=form}}
- form.process()
- validation 후 form자신을 돌려줌. form.accepted 는 form이 처리,검증되면 True
session을 사용하지 않고 url에 넣는
redirect(URL(‘second’,vars=dict(name=name)))
방법도 있지만 보안상 비추천이다.
다국어 지원 - internationalization : I18N
C 에서
T(“What is your name?”)
V 에서
<h1>{{=T(“What is your name?”)}}</h1>
같이 사용한다.
또 다른 예 그림 블로그 - an image blog
admin에서 image 앱을 만들고.
models폴더(이후 M)의 db.py 를 편집하여 모델링 을 하자.
기존 내용이 있다면 충돌하지 않게 수정해서 마지막에 추가
아래 코드는 실행되면
테이블이 없으면 자동생성
존재하는 데 scheme 이 다르면 일치하도록 alter , type 이 다르면 convert
존재하고 같으면 아무일도 하지 않음.
# db : database connection
db = DAL(“sqlite://storage.sqlite”)
db.define_table(‘image’,
# field name , type, options Field(’title’, unique=True),
# name : file , type upload Field(‘file’, ‘upload’),
# record 가 문자로 표시될때 표시할 방법을 지정. record를 인자로 받는 함수도 사용 가능. format = ‘%(title)s’)
db.define_table(‘post’,
# image 테이블의 id 를 지정 Field(‘image_id’, ‘reference image’), Field(‘author’), Field(’email’), Field(‘body’, ’text’))
# validators db.image.title.requires = IS_NOT_IN_DB(db, db.image.title) db.post.image_id.requires = IS_IN_DB(db, db.image.id, ‘%(title)s’) db.post.author.requires = IS_NOT_EMPTY() db.post.email.requires = IS_EMAIL() db.post.body.requires = IS_NOT_EMPTY()
db.post.image_id.writable = db.post.image_id.readable = False
위 코드가 성공적으로 실행되었다면 web2py는 자동으로 db 관리 페이지를 생성한다.
http://127.0.0.1:8000/images/appadmin
을 통해서 db 관리를 할수 있다. ( 앞으론 앱관리자 라고 하자. )
M 밑의 sql.log 를 확인해서 실행된 sql 을 볼수 있다.
appadmin은 각 web2py 앱별로 별도 이므로 수정사항이 다른 앱에 영향을 주지 않는다.
이제 만들 것 3가지
index 페이지 - 제목으로 정렬된 image 목록을 표여주며 클릭시 상세 페이지로 이동
show/id 페이지 - 방문자에게 이미지 하나를 보여주고 덧글을 달수 있는 페이지
download/name - 업로드된 이미지를 다운 받을 수 있는 페이지.
C - default.py
def index(): images = db().select(db.image.ALL, orderby=db.image.title) return dict(images=images)
def show(): image = db.image(request.args(0,cast=int)) or redirect(URL(‘index’)) db.post.image_id.default = image.id form = SQLFORM(db.post) if form.process().accepted: response.flash = ‘your comment is posted’ comments = db(db.post.image_id==image.id).select() return dict(image=image, comments=comments, form=form)
def download(): return response.download(request, db)
V - default/index.html
{{extend ’layout.html’}} <h1>Current Images</h1> <ul> {{for image in images:}} {{=LI(A(image.title, _href=URL(“show”, args=image.id)))}} {{pass}} </ul>
V - default/show.html
{{extend ’layout.html’}} <h1>Image: {{=image.title}}</h1> <center> <img width=“200px” src="{{=URL(‘download’, args=image.file)}}" /> </center> {{if len(comments):}} <h2>Comments</h2><br /><p> {{for post in comments:}} <p>{{=post.author}} says <i>{{=post.body}}</i></p> {{pass}}</p> {{else:}} <h2>No comments posted yet</h2> {{pass}} <h2>Post a comment</h2> {{=form}}
등록된 사용자만 접근가능하게 제한 하기.
simple app 아니고 wizard로 생성된 앱을 기반으로 사용하여 앱을 만든다.
Auth 관련 기능이 기본으로 생성되고 그상태에서
C 의 제한 하고 싶은 함수앞에
@auth.requires_login()
를 추가한다.
SQLFORM.grid , SQLFORM.smartgrid , 그리고 그룹 관리
앱관리자를 사용해 manager 그룹을 만들고 사용자를 추가한후
@auth.requires_membership(‘manager’) def manage(): grid = SQLFORM.smartgrid(db.image,linked_tables=[‘post’]) return dict(grid=grid)
V - default/manage.html
{{extend ’layout.html’}} <h2>Management Interface</h2> {{=grid}}
아래 URL은 그룹에 속한 사용자만 접근할수 있게 된다.
http://127.0.0.1:8000/images/default/manage
기본 레이아웃은 view/layout.html을 사용하고
models/menu.py 를 통해서 몇몇 항목을 설정할수도 있다.
간단한 위키 - a simple wiki
모델링.
db = DAL(‘sqlite://storage.sqlite’)
from gluon.tools import * auth = Auth(db) auth.define_tables() crud = Crud(db)
db.define_table(‘page’, Field(’title’), Field(‘body’, ’text’), Field(‘created_on’, ‘datetime’, default=request.now), Field(‘created_by’, ‘reference auth_user’, default=auth.user_id), format=’%(title)s’)
db.define_table(‘post’, Field(‘page_id’, ‘reference page’), Field(‘body’, ’text’), Field(‘created_on’, ‘datetime’, default=request.now), Field(‘created_by’, ‘reference auth_user’, default=auth.user_id))
db.define_table(‘document’, Field(‘page_id’, ‘reference page’), Field(’name’), Field(‘file’, ‘upload’), Field(‘created_on’, ‘datetime’, default=request.now), Field(‘created_by’, ‘reference auth_user’, default=auth.user_id), format=’%(name)s’)
db.page.title.requires = IS_NOT_IN_DB(db, ‘page.title’) db.page.body.requires = IS_NOT_EMPTY() db.page.created_by.readable = db.page.created_by.writable = False db.page.created_on.readable = db.page.created_on.writable = False
db.post.body.requires = IS_NOT_EMPTY() db.post.page_id.readable = db.post.page_id.writable = False db.post.created_by.readable = db.post.created_by.writable = False db.post.created_on.readable = db.post.created_on.writable = False
db.document.name.requires = IS_NOT_IN_DB(db, ‘document.name’) db.document.page_id.readable = db.document.page_id.writable = False db.document.created_by.readable = db.document.created_by.writable = False db.document.created_on.readable = db.document.created_on.writable = False
다른 부분은 원문 참조.
def search(): “““an ajax wiki search page””” return dict(form=FORM(INPUT(_id=‘keyword’,_name=‘keyword’, _onkeyup=“ajax(‘callback’, [‘keyword’], ’target’);”)), target_div=DIV(_id=‘target’)) def callback(): “““an ajax callback that returns a <ul> of links to wiki pages””” query = db.page.title.contains(request.vars.keyword) pages = db(query).select(orderby=db.page.title) links = [A(p.title, _href=URL(‘show’,args=p.id)) for p in pages] return UL(*links)
사용자가 키를 뗄때마다 _onkeyup 의 ajax(‘callback’, [‘keyword’], ’target’); 가 수행된다.
ajax 는 web2py.js 에 정의되어 있으며 layout.html에서 자동으로 include 하고 있다.
V - default/search.html
{{extend ’layout.html’}} <h1>Search wiki pages</h1> [ {{=A(’listall’, _href=URL(‘index’))}}]<br /> {{=form}}<br />{{=target_div}}
V - default/show.html
{{extend ’layout.html’}} <h1>{{=page.title}}</h1> [ {{=A(’edit’, _href=URL(’edit’, args=request.args))}} | {{=A(‘documents’, _href=URL(‘documents’, args=request.args))}} ]<br /> {{=MARKMIN(page.body)}} <h2>Comments</h2> {{for post in comments:}} <p>{{=db.auth_user[post.created_by].first_name}} on {{=post.created_on}} says <i>{{=post.body}}</i></p> {{pass}} <h2>Post a comment</h2> {{=form}}
body 를 markdown 나 xml 로 만들고 싶으면
from gluon.contrib.markdown import WIKI as MARKDOWN
{{=XML(page.body)}}
{{=XML(page.body, sanitize=True)}}
형태로 사용.
RSS 지원
def news(): “““generates rss feed form the wiki pages””"
# view/generic.rss 를 쓰도록 response.generic_patterns = [’.rss’] pages = db().select(db.page.ALL, orderby=db.page.title) return dict( title = ‘mywiki rss feed’, link = ‘http://127.0.0.1:8000/mywiki/default/index’, description = ‘mywiki news’, created_on = request.now, items = [ dict(title = row.title, link = URL(‘show’, args=row.id), description = MARKMIN(row.body).xml(), created_on = row.created_on ) for row in pages])
RSS URL 은
http://127.0.0.1:8000/mywiki/default/news.rss
XML-RPC 지원 - 검색 api
service = Service()
@service.xmlrpc def find_by(keyword): “““finds pages that contain keyword for XML-RPC””” return db(db.page.title.contains(keyword)).select().as_list()
def call(): “““exposes all registered services, including XML-RPC””” return service()
사용 예제
>>> import xmlrpclib >>> server = xmlrpclib.ServerProxy( ‘http://127.0.0.1:8000/mywiki/default/call/xmlrpc’) >>> for item in server.find_by(‘wiki’): print item[‘created_on’], item[’title’]
date,datetime,time 처리 관련
DB내 , web2py 내부 , 문자열 표시
문자열 표시는 기본적으로 ISO 를 따르는데 %Y-%m-%d %H:%M:%S
admin - translation page 에서 바꿀수 있다.
web2py 내장 위키 - the built-in web2py wiki
web2py는 기본기능으로 미디어 파일첨부,태그,페이지 접근 권한,oembed , 컴포넌트(14장)을 지원하는 wiki 를 제공한다.
이 기능은 모든 web2py 앱에서 사용가능하며
C 에
def index(): return auth.wiki()
를 추가하는 것만으로 가능해진다.
위키를 사용하기위해선 페이지를 만들어야 하며 가입/로그인후 wiki-editor, wiki-author 그룹에 들어가야한다.
editor는 모든 페이지의 생성,편집,삭제
author 는 생성과 자신이 만든 페이지의 편집 권한 만 있다.
MARKMIN 설명
# This is a title ## this is a section title ### this is a subsection title
Text can be **bold**, ‘‘italic’’, ``code`` etc. Learn more at:
좀더 자세한 설명은 본문 참고.
( oembed protocol , components 등 )
관리에대한 추가사항들 - more on admin
내장 디버거 - python 2.6이상.
shell / crontab / error ticket / mercurial version control
multi user mode - 선생,학생
mobile admin
appadmin 에대해 좀더 이야기하면 - more on adppadmin
본문 참고.