Google App Engineでone-to-many(1対多)2kindとReferenceProperty()で
2010/04/29 09:30:37
2つのkind(モデル)の連携を一般的な「ReferenceProperty()」を用いてone-to-many(1対多)のCRUDを作成してみる。ケースは人物が複数のメールアドレスを持っている関係を表すこととする。
さらに今回は今後のためをふまえて機能にそってpyファイルを分けて構築してみました。
app.yaml
main.py
person.py
model.py
template/person/list.html
template/person/view.html
template/person/add.html
template/person/edit.html




まだまだGAEにおける構築の定石はつかめないけど、こうやって機能や処理によってpyファイルをわけてモジュール化し必要に応じて読み込み使う形を取るのが、規模が大きくなっても恐らく対応できる方法ではないかと今は思っている。
application: jinling-ren version: 1 runtime: python api_version: 1 handlers: - url: .* script: main.py
main.py
#!/usr/bin/env python
#!-*- coding:utf-8 -*-
# one-to-manyサンプル
# http://www.jinlingren.com/
from google.appengine.ext import webapp
from google.appengine.ext.webapp.util import run_wsgi_app
import person
class Root(webapp.RequestHandler):
def get(self):
self.redirect('/person/list')
application = webapp.WSGIApplication([
('/', Root),
('/person/list', person.PersonList),
('/person/view/(\w+)', person.PersonView),
('/person/add', person.PersonAdd),
('/person/edit/(\w+)', person.PersonEdit),
('/person/delete/(\w+)', person.PersonDelete)
],debug=True)
def main():
run_wsgi_app(application)
if __name__ == '__main__':
main()
person.py
#!/usr/bin/env python
#!-*- coding:utf-8 -*-
# one-to-manyサンプル
# http://www.jinlingren.com/
from google.appengine.ext import webapp, db
from google.appengine.ext.webapp import template
import os
import cgi
import logging
from model import Person, Email
class PersonList(webapp.RequestHandler):
def get(self):
persons = Person.all().order('name')
html = template.render(
os.path.join(os.path.dirname(__file__), 'template', 'person', 'list.html'),
{
'persons': persons
}
)
self.response.out.write(html)
class PersonView(webapp.RequestHandler):
def get(self, key):
person = db.get(db.Key(key))
html = template.render(
os.path.join(os.path.dirname(__file__), 'template', 'person', 'view.html'),
{
'person': person
}
)
self.response.out.write(html)
class PersonAdd(webapp.RequestHandler):
def get(self):
html = template.render(
os.path.join(os.path.dirname(__file__), 'template', 'person', 'add.html'),
{}
)
self.response.out.write(html)
def post(self):
try:
name = cgi.escape(self.request.get('name'))
person = Person(name=name)
person.put()
mailaddress = cgi.escape(self.request.get('mailaddress'))
if mailaddress:
email = Email(mailaddress=mailaddress)
email.person = person
email.put()
self.redirect('/person/list')
except:
self.redirect('/person/add')
class PersonEdit(webapp.RequestHandler):
def get(self, key):
person = db.get(db.Key(key))
html = template.render(
os.path.join(os.path.dirname(__file__), 'template', 'person', 'edit.html'),
{
'person': person
}
)
self.response.out.write(html)
def post(self, key):
try:
person = db.get(db.Key(key))
person.name = cgi.escape(self.request.get('name'))
person.put()
for email in person.email_set:
post_mailaddress = cgi.escape(self.request.get('mailaddress_'+str(email.key())))
if post_mailaddress:
if email.mailaddress != post_mailaddress:
email.mailaddress = post_mailaddress
email.put()
else:
email.delete()
mailaddress_new = cgi.escape(self.request.get('mailaddress_new'))
if mailaddress_new:
email = Email(mailaddress=mailaddress_new)
email.person = person
email.put()
self.redirect('/person/list')
except:
self.redirect('/person/edit/'+key)
class PersonDelete(webapp.RequestHandler):
def get(self, key):
try:
person = db.get(db.Key(key))
if person:
for email in person.email_set:
email.delete()
person.delete()
self.redirect('/person/list')
except:
self.redirect('/person/list')
model.py
#!/usr/bin/env python #!-*- coding:utf-8 -*- # one-to-manyサンプル # http://www.jinlingren.com/ from google.appengine.ext import db class Person(db.Model): name = db.StringProperty(required=True) modified = db.DateTimeProperty(auto_now=True) created = db.DateTimeProperty(auto_now_add=True) class Email(db.Model): mailaddress = db.StringProperty(required=True) person = db.ReferenceProperty(reference_class=Person) modified = db.DateTimeProperty(auto_now=True) created = db.DateTimeProperty(auto_now_add=True)
template/person/list.html
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="ja" lang="ja">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<title>one-to-manyサンプル</title>
</head>
<body>
<h1>PersonList(人物一覧)</h1>
> <a href="/person/add">人物追加</a><br><br>
<table cellspacing="1" cellpadding="8" border="0" bgcolor="#999999">
<tr bgcolor="#EBEBEB">
<td> </td>
<td align="center">名前</td>
<td align="center">メールアドレス</td>
<td> </td>
<td> </td>
</tr>
{% for person in persons %}
<tr bgcolor="#FFFFFF">
<td><a href="/person/view/{{ person.key }}">詳細</a></td>
<td>{{ person.name }}</td>
<td><ul>{% for email in person.email_set %}<li>{{ email.mailaddress }}</li>{% endfor %}</ul></td>
<td><a href="/person/edit/{{ person.key }}">編集</a></td>
<td><a href="/person/delete/{{ person.key }}" onClick="if(confirm('本当に削除しますか?')){return true;}else{return false;}">削除</a></td>
</tr>
{% endfor %}
</table>
</body>
</html>
template/person/view.html
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="ja" lang="ja">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<title>one-to-manyサンプル</title>
</head>
<body>
<h1>PersonView(人物参照)</h1>
> <a href="/person/list">人物一覧</a><br><br>
<table cellspacing="1" cellpadding="8" border="0" bgcolor="#999999">
<tr>
<td width="100" bgcolor="#EBEBEB">名前</td>
<td width="250" bgcolor="#FFFFFF">{{ person.name }}</td>
</tr>
<tr>
<td bgcolor="#EBEBEB">メールアドレス</td>
<td bgcolor="#FFFFFF"><ul>{% for email in person.email_set %}<li>{{ email.mailaddress }}</li>{% endfor %}</ul></td>
</tr>
</table>
</body>
</html>
template/person/add.html
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="ja" lang="ja">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<title>one-to-manyサンプル</title>
</head>
<body>
<h1>PersonAdd(人物追加)</h1>
> <a href="/person/list">人物一覧</a><br><br>
<form action="/person/add" method="post">
<table border="0" cellspacing="1" cellpadding="8" border="0" bgcolor="#999999">
<tr>
<td width="100" bgcolor="#EBEBEB">名前 <sup><font color="#FF0000">*</font></sup></td>
<td width="250" bgcolor="#FFFFFF"><input type="text" name="name" value=""></td>
</tr>
<tr>
<td bgcolor="#EBEBEB">メールアドレス</td>
<td bgcolor="#FFFFFF"><input type="text" name="mailaddress" value=""></td>
</tr>
</table><br>
<input type="submit" value="追加する">
</form>
<br>
<font color="#FF0000">*</font>は必須項目
</body>
</html>
template/person/edit.html
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="ja" lang="ja">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<title>one-to-manyサンプル</title>
</head>
<body>
<h1>PersonEdit(人物編集)</h1>
> <a href="/person/list">人物一覧</a><br><br>
<form action="/person/edit/{{ person.key }}" method="post">
<table cellspacing="1" cellpadding="8" border="0" bgcolor="#999999">
<tr>
<td width="100" bgcolor="#EBEBEB">名前 <sup><font color="#FF0000">*</font></sup></td>
<td width="250" bgcolor="#FFFFFF"><input type="text" name="name" value="{{ person.name }}"></td>
</tr>
<tr>
<td bgcolor="#EBEBEB">メールアドレス</td>
<td bgcolor="#FFFFFF">
{% for email in person.email_set %}
<input type="text" name="mailaddress_{{ email.key }}" value="{{ email.mailaddress }}"><br>
{% endfor %}
<input type="text" name="mailaddress_new" value="">
</td>
</tr>
</table><br>
<input type="submit" value="変更する">
</form>
<br>
<font color="#FF0000">*</font>は必須項目
</body>
</html>




まだまだGAEにおける構築の定石はつかめないけど、こうやって機能や処理によってpyファイルをわけてモジュール化し必要に応じて読み込み使う形を取るのが、規模が大きくなっても恐らく対応できる方法ではないかと今は思っている。