Google App Engineでone-to-one(1対1)
2010/04/27 09:43:00
いわゆる複数モデルの連携処理をCRUDで作ってみる。one-to-one(1対1)といっても実はone-to-many(1対多)にもなる。正確にいうと1対1に抑えているだけの話。
一般的には「ReferencePropertyによるモデルの相互参照」という手法を使います。
app.yaml
main.py
template/index.html
template/author/list.html
template/author/view.html
template/author/add.html
template/author/edit.html
template/book/list.html
template/book/view.html
template/book/add.html
template/book/edit.html
正直そのままだと実用性皆無ですが、複数モデル連携でのCRUD処理はどうするんだ?という部分で参考にしていただければ幸い。
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-oneサンプル
# http://www.jinlingren.com/
from google.appengine.ext import webapp, db
from google.appengine.ext.webapp import util, template
import os
import cgi
import logging
class Author(db.Model):
name = db.StringProperty(required=True)
modified = db.DateTimeProperty(auto_now=True)
created = db.DateTimeProperty(auto_now_add=True)
class Book(db.Model):
title = db.StringProperty(required=True)
author = db.ReferenceProperty(reference_class=Author)
modified = db.DateTimeProperty(auto_now=True)
created = db.DateTimeProperty(auto_now_add=True)
class Root(webapp.RequestHandler):
def get(self):
html = template.render(
os.path.join(os.path.dirname(__file__), 'template', 'index.html'),
{}
)
self.response.out.write(html)
class AuthorList(webapp.RequestHandler):
def get(self):
authors = Author.all().order('-created')
html = template.render(
os.path.join(os.path.dirname(__file__), 'template', 'author', 'list.html'),
{
'authors': authors
}
)
self.response.out.write(html)
class AuthorView(webapp.RequestHandler):
def get(self, key):
author = db.get(db.Key(key))
html = template.render(
os.path.join(os.path.dirname(__file__), 'template', 'author', 'view.html'),
{
'author': author
}
)
self.response.out.write(html)
class AuthorAdd(webapp.RequestHandler):
def get(self):
html = template.render(
os.path.join(os.path.dirname(__file__), 'template', 'author', 'add.html'),
{}
)
self.response.out.write(html)
def post(self):
try:
name = cgi.escape(self.request.get('name'))
author = Author(name=name)
author.put()
self.redirect('/author/list')
except:
self.redirect('/author/add')
class AuthorEdit(webapp.RequestHandler):
def get(self, key):
author = db.get(db.Key(key))
html = template.render(
os.path.join(os.path.dirname(__file__), 'template', 'author', 'edit.html'),
{
'author': author
}
)
self.response.out.write(html)
def post(self, key):
try:
author = db.get(db.Key(key))
author.name = cgi.escape(self.request.get('name'))
author.put()
self.redirect('/author/list')
except:
self.redirect('/author/edit/'+key)
class AuthorDelete(webapp.RequestHandler):
def get(self, key):
try:
author = db.get(db.Key(key))
if author:
books = author.book_set
if books is not None:
for book in books:
book.author = None
book.put()
author.delete()
self.redirect('/author/list')
except:
self.redirect('/author/list')
class BookList(webapp.RequestHandler):
def get(self):
books = Book.all().order('-created')
html = template.render(
os.path.join(os.path.dirname(__file__), 'template', 'book', 'list.html'),
{
'books': books
}
)
self.response.out.write(html)
class BookView(webapp.RequestHandler):
def get(self, key):
book = db.get(db.Key(key))
html = template.render(
os.path.join(os.path.dirname(__file__), 'template', 'book', 'view.html'),
{
'book': book
}
)
self.response.out.write(html)
class BookAdd(webapp.RequestHandler):
def get(self):
authors = Author.all().order('-created')
html = template.render(
os.path.join(os.path.dirname(__file__), 'template', 'book', 'add.html'),
{
'authors': authors
}
)
self.response.out.write(html)
def post(self):
try:
title = cgi.escape(self.request.get('title'))
book = Book(title=title)
author = self.request.get('author')
if author:
book.author = db.get(db.Key(author))
book.put()
self.redirect('/book/list')
except:
self.redirect('/book/add')
class BookEdit(webapp.RequestHandler):
def get(self, key):
authors = Author.all().order('-created')
book = db.get(db.Key(key))
html = template.render(
os.path.join(os.path.dirname(__file__), 'template', 'book', 'edit.html'),
{
'authors': authors,
'book': book
}
)
self.response.out.write(html)
def post(self, key):
try:
book = db.get(db.Key(key))
book.title = cgi.escape(self.request.get('title'))
author = self.request.get('author')
if author:
book.author = db.get(db.Key(author))
else:
book.author = None
book.put()
self.redirect('/book/list')
except:
self.redirect('/book/edit/'+key)
class BookDelete(webapp.RequestHandler):
def get(self, key):
try:
book = db.get(db.Key(key))
if book:
book.delete()
self.redirect('/book/list')
except:
self.redirect('/book/list')
def main():
application = webapp.WSGIApplication(
[
('/', Root),
('/author/list', AuthorList),
('/author/view/(\w+)', AuthorView),
('/author/add', AuthorAdd),
('/author/edit/(\w+)', AuthorEdit),
('/author/delete/(\w+)', AuthorDelete),
('/book/list', BookList),
('/book/view/(\w+)', BookView),
('/book/add', BookAdd),
('/book/edit/(\w+)', BookEdit),
('/book/delete/(\w+)', BookDelete)
],
debug=True)
util.run_wsgi_app(application)
if __name__ == '__main__':
main()
template/index.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-oneサンプル</title> </head> <body> > <a href="/author/list">著者一覧</a><br> > <a href="/book/list">著書一覧</a><br> </body> </html>
template/author/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-oneサンプル</title>
</head>
<body>
> <a href="/book/list">著書一覧</a><br><br>
<h1>AuthorList(著者一覧)</h1>
> <a href="/author/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 author in authors %}
<tr bgcolor="#FFFFFF">
<td><a href="/author/view/{{ author.key }}">詳細</a></td>
<td>{{ author.name }}</td>
<td><ul>{% for book in author.book_set %}<li>{{ book.title }}</li>{% endfor %}</ul></td>
<td><a href="/author/edit/{{ author.key }}">編集</a></td>
<td><a href="/author/delete/{{ author.key }}" onClick="if(confirm('本当に削除しますか?')){return true;}else{return false;}">削除</a></td>
</tr>
{% endfor %}
</table>
</body>
</html>template/author/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-oneサンプル</title>
</head>
<body>
> <a href="/book/list">著書一覧</a><br><br>
<h1>AuthorView(著者参照)</h1>
> <a href="/author/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">{{ author.name }}</td>
</tr>
<tr>
<td bgcolor="#EBEBEB">著書</td>
<td bgcolor="#FFFFFF"><ul>{% for book in author.book_set %}<li>{{ book.title }}</li>{% endfor %}</ul></td>
</tr>
</table>
</body>
</html>template/author/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-oneサンプル</title>
</head>
<body>
> <a href="/book/list">著書一覧</a><br><br>
<h1>AuthorAdd(著者追加)</h1>
> <a href="/author/list">著者一覧</a><br><br>
<form action="/author/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>
</table><br>
<input type="submit" value="追加する">
</form>
<br>
<font color="#FF0000">*</font>は必須項目
</body>
</html>template/author/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-oneサンプル</title>
</head>
<body>
> <a href="/book/list">著書一覧</a><br><br>
<h1>AuthorEdit(著者編集)</h1>
> <a href="/author/list">著者一覧</a><br><br>
<form action="/author/edit/{{ author.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="{{ author.name }}"></td>
</tr>
</table><br>
<input type="submit" value="変更する">
</form>
<br>
<font color="#FF0000">*</font>は必須項目
</body>
</html>template/book/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-oneサンプル</title>
</head>
<body>
> <a href="/author/list">著者一覧</a><br><br>
<h1>BookList(著書一覧)</h1>
> <a href="/book/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 book in books %}
<tr bgcolor="#FFFFFF">
<td><a href="/book/view/{{ book.key }}">詳細</a></td>
<td>{{ book.title }}</td>
<td>{{ book.author.name }}</td>
<td><a href="/book/edit/{{ book.key }}">編集</a></td>
<td><a href="/book/delete/{{ book.key }}" onClick="if(confirm('本当に削除しますか?')){return true;}else{return false;}">削除</a></td>
</tr>
{% endfor %}
</table>
</body>
</html>template/book/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-oneサンプル</title>
</head>
<body>
> <a href="/author/list">著者一覧</a><br><br>
<h1>BookView(著書参照)</h1>
> <a href="/book/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">{{ book.title }}</td>
</tr>
<tr>
<td bgcolor="#EBEBEB">著者</td>
<td bgcolor="#FFFFFF">{{ book.author.name }}</td>
</tr>
</table>
</body>
</html>template/book/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-oneサンプル</title>
</head>
<body>
> <a href="/author/list">著者一覧</a><br><br>
<h1>BookAdd(著書追加)</h1>
> <a href="/book/list">著書一覧</a><br><br>
<form action="/book/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="title" value=""></td>
</tr>
<tr>
<td bgcolor="#EBEBEB">著者</td>
<td bgcolor="#FFFFFF"><select name="author">
<option value="">-----</option>
{% for author in authors %}
<option value="{{ author.key }}">{{ author.name }}</option>
{% endfor %}
</select></td>
</tr>
</table><br>
<input type="submit" value="追加する">
</form>
<br>
<font color="#FF0000">*</font>は必須項目
</body>
</html>template/book/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-oneサンプル</title>
</head>
<body>
> <a href="/author/list">著者一覧</a><br><br>
<h1>BookEdit(著書編集)</h1>
> <a href="/book/list">著書一覧</a><br><br>
<form action="/book/edit/{{ book.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="title" value="{{ book.title }}"></td>
</tr>
<tr>
<td bgcolor="#EBEBEB">著者</td>
<td bgcolor="#FFFFFF"><select name="author">
<option value="">-----</option>
{% for author in authors %}
<option value="{{ author.key }}"{% ifequal author.key book.author.key %} selected{% endifequal %}>{{ author.name }}</option>
{% endfor %}
</select></td>
</tr>
</table><br>
<input type="submit" value="変更する">
</form>
<br>
<font color="#FF0000">*</font>は必須項目
</body>
</html>正直そのままだと実用性皆無ですが、複数モデル連携でのCRUD処理はどうするんだ?という部分で参考にしていただければ幸い。