HOME > ブログ > Google App Engineでone-to-one(1対1)

Google App Engineでone-to-one(1対1)

いわゆる複数モデルの連携処理をCRUDで作ってみる。
one-to-one(1対1)といっても実はone-to-many(1対多)にもなる。正確にいうと1対1に抑えているだけの話。

一般的には「ReferencePropertyによるモデルの相互参照」という手法を使います。




app.yaml
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>&nbsp;</td>
    <td align="center">著者</td>
    <td align="center">著書</td>
    <td>&nbsp;</td>
    <td>&nbsp;</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>&nbsp;</td>
    <td align="center">タイトル</td>
    <td align="center">著者</td>
    <td>&nbsp;</td>
    <td>&nbsp;</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処理はどうするんだ?という部分で参考にしていただければ幸い。


| Google App Engine | Comment:0 |
コメント投稿












画像リロード
*半角の小英字、数字で構成されています