GAEpでiPhone向けWebアプリを作る (5)GAEp 上にモデルを定義する

前回の続きです。

GAEp 上にモデルを定義する

ようやくGAEpのソースコードが出てきます。

モデルの整理

今回の ToDo アプリで必要な要件を整理します。
* ひとつの ToDo を表すアイテム(ToDo アイテム)が存在する
* ひとつの ToDo アイテムは、必ず誰かのアイテムである(必ず誰かが所有する)
* ひとつの ToDo アイテムには、いつまでに何をなすべきか、が指定されている
非常に簡単です。

モデルを定義する

以下のようにモデル(カインド)を定義します。

models.py内

from google.appengine.ext import db

class ToDoItem(db.Model):
  owner = db.UserProperty(required=True)
  title = db.StringProperty(required=True)
  detail = db.TextProperty()
  reg_date = db.DateTimeProperty(auto_now_add=True)
  deadline = db.DateTimeProperty(required=True)

GAEp のデータストアに格納するために、Model クラスを継承します。このクラスは、google.appengine.ext.db パッケージに格納されています。

ToDoItem クラスには、5つのプロパティが定義されています。

owner
ひとつ ToDo アイテムの所有者。必須
title
ToDo 内容。通常ここに何をすべきかを記載する。必須
detail
ToDo 内容の詳細。
reg_date
この ToDo アイテムの登録日
deadline
この ToDo アイテムの締め切り。必須

各データ項目は、Property クラスを継承した各クラスで定義しています。どのようなプロパティが GAEp で定義されているかは下記で確かめられます。

また、Property のコンストラクタに required 引数と auto_now_add 引数を使用しています。
required 引数は Properyty クラスのコンストラクタ引数です。これによりプロパティに None を入れるとエラーが返ります。他にもコンストラクタ引数があります。下記参照。

auto_now_add 引数は DateTimeProperty クラス固有のプロパティで、インスタンス作成時にその時点の時刻を自動で代入します。下記を参照してください。

deadline については、仕様的には DateProperty でも充分です。

モデルインスタンスの作成、変更、保存、削除

上記のように作成したモデルのインスタンス(エンティティ)のライフサイクルに関する操作を簡単に記載します。

エンティティの作成は、以下のようにします。

item = ToDoItem(owner=user, title=s_title, deadline=dt_deadline)

required なプロパティはコンストラクタ引数で初期化しています。これらを初期化しなければ、エラーとなります。

item = ToDoItem() # 必須なプロパティが初期化されていないのでエラー

コンストラクタ引数なしでエンティティ作成したい場合は、プロパティのコンストラクタ引数に default を None 以外の値で指定しておく

変更は以下のようにします。

item.detail = 'この ToDo アイテムの詳細はこれこれです'

required なプロパティには、None を代入できない点にだけ注意。

変更したら、保存は以下のようにします。

item.put()

コンストラクタで作成しただけではデータは保存されていません。保存は必ず put() メソッドで。

最後に、いらなくなったエンティティの削除は以下です。

item.delete()

今回はここまで。拍子抜けするくらい簡単です。

参考

Google App Engine については、基本的に Google が公開しているデベロッパーガイドを見ればだいたいのことはわかります。内容もわかりやすいです。

特に、Google App Engine Python については、以下です。

基本的にはこれらのサイトを見ればほとんどのことはわかりますが、その他に僕は以下の本を参考にしています。

Programming Google App Engine

Programming Google App Engine

和訳も出ているようです。

プログラミング Google App Engine

プログラミング Google App Engine

GAEpでiPhone向けWebアプリを作る (4)GAEpプロジェクトとそのファイル構成

前回の続きです。

GAEpプロジェクトとそのファイル構成

Google App Engine Pythonプロジェクトの作成

プロジェクト作成方法は、スタートガイドあたりを参考に適当に。ここでは割愛。

プロジェクト内のファイル構成

以下のようなファイル構成にしました。

<project dir>/
+--app.yaml
+--index.yaml
+--cron.yaml
+--main.py
+--todo/
   +--__init__.py
   +--handlers.py       # RequestHandlerクラス群
   +--models.py         # DataStoreクラス群
   +--tasks.py          # cronタスククラス群
   |
   +--staticdir/        # 静的ファイル格納
   |  +--views.html     # メインビュー
   |  +--bye.html
   |  +--todo.manifest  # 現在未使用
   |  +--css/           # css格納
   |  |  +--iphone.css
   |  |  +--todo.css
   |  +--js/            # javascript格納
   |  |  +--jquery-1.4.4.min.js
   |  |  +--default.js
   |  +--img/           # 画像リソース格納
   |     +--button.png
   |     +--back_button.png
   |
   +--templates/        # テンプレートファイル格納
      +--views.tpl@

今回作ってみて、この構成に対して以下の感想を持っています。

  • 静的ファイルstaticdir/とテンプレートファイルtemplates/は明確に分けるべき
  • staticdir/を設けることでpythonスクリプトと静的ファイルを明確に分離できる
  • staticdir/内には実際のサービス時と同一の相対パスで html/css/javascript を配置
  • staticdir/の中で html/css/javascriptデバッグがしやすい
  • 本当は、静的ファイル群(staticdir/)とスクリプト(*.pyやテンプレートファイル群)は同一のディレクトリ階層に配置するのはあまりよくないかもしれない。が、近いほうが安心するので今回はこのようにした

なお、特に静的ファイルとテンプレートファイルの混同には、充分注意すべきです。同一パスのファイルを静的ファイルとしてもテンプレートファイルとしても利用するのは GAEp 上禁止されています。GAEp アプリは、サーバにアップロードされる際、静的ファイルは StaticFileServer に、テンプレートファイルは ApplicationServer にアップロードされるようです。つまりアップロード先が異なります。
どうしても静的ファイルとテンプレートファイルを同一にしたい場合には、シンボリックリンクを作成します。例えば、上記のファイル構成図で、templates/views.tpl ファイルは、staticdir/views.html ファイルのシンボリックリンクにしてあります。このようにしておくと開発機上では実体ファイルをひとつにしつつ、アップロード時に両方のサーバにアップロードされます。

app.yamlの記述 (ユーザ認証なし)

app.yaml の内容を下に示します。ただし、この段階ではまだユーザ認証は設定していません。

app.yaml

application: penguinwatcher
version: 1
runtime: python
api_version: 1

builtins:
- remote_api: on


handlers:

- url: /todo/js
  static_dir: todo/staticdir/js

- url: /todo/css
  static_dir: todo/staticdir/css

- url: /todo/img
  static_dir: todo/staticdir/img

- url: /todo/(.*\.html)
  static_files: todo/staticdir/\1
  upload: todo/staticdir/(.*\.html)

- url: /todo/tasks/.*
  script: main.py
  login: admin

- url: /todo/.*
  script: main.py

js/, css/, img/, *.htmlは、todo/直下のパスにマッピングすることにします。
つまり、

(URL)  http://penguinwatcher.appspot.com/todo/views.html
↓
(ファイルパス) ./todo/staticdir/views.html

また、同様に、todo/直下のパスに リクエストハンドラをマッピングします。


本日はここまで。次回はモデル(models.py)およびリクエストハンドラ(handlers.py)を書いていきます。

参考

Google App Engine については、基本的に Google が公開しているデベロッパーガイドを見ればだいたいのことはわかります。内容もわかりやすいです。

特に、Google App Engine Python については、以下です。

基本的にはこれらのサイトを見ればほとんどのことはわかりますが、その他に僕は以下の本を参考にしています。

Programming Google App Engine

Programming Google App Engine

和訳も出ているようです。

プログラミング Google App Engine

プログラミング Google App Engine

GAEpでiPhone向けWebアプリを作る (3)アーキテクチャについて

時間があきましたが、前回の続きです。

アーキテクチャについて

アーキテクチャと呼ぶほどたいしたものではないですが、概要を説明しておきます。

構成の概要とシーケンス

下図のようにしています。


  1. まずユーザが、URLからビューとなるHTMLファイルをリクエスト。
  2. HTMLファイルがブラウザにロードされる。
    • 同時にHTMLファイル内に指定されているcssファイルとjavascriptファイルもブラウザにロードされる。
    • この時点で、ビューとコントローラはすべてブラウザにロードされます
  3. javascript からサーバにデータをリクエス
  4. ToDo のデータがJSON形式でサーバからブラウザにロードされる
  5. javascript で、ロードされたJSONデータをHTMLにバインドする
構成の特徴

以下のような特徴でコーディングするよう自分に制限を課しました。

  • ビューは html/css のみ
    • html/css には純粋にビューのレイアウト情報だけ定義します
  • モデルとして、サーバサイドに WebAPI を定義
    • GAEp の RequestHandler として簡単な WebAPI を定義します
    • モデルは JSON 形式でサーバからブラウザに送信します
  • コントローラは javascript のみ
    • ビューのコントロールはすべて javascript で実現します
    • サーバサイドでビューとモデルを結合することをしません
    • つまり、テンプレートエンジンでのビューとモデルの結合をやめます
    • ビューとモデルの結合は jquery で実現します

これにより以下のような効果を期待をします。

  • ブラウザ上にロードされた html/css/javascript の塊は、ブラウザ上で動作するクライアントアプリとしての役割を果たす。
  • クライアントコンポーネントの html/css/javascript は、最初にすべてブラウザにロードしておく。これによりビューの操作感が向上する
  • つまり、クリックするたびの html/css 読み込みによる遅延を抑制する(JSON データのロード遅延のみ)
  • これにより、ビューの柔軟性を高める

今回は以上です。

備忘:次回は GAEp プロジェクトとそのファイル構成についての予定。

GAEpでiPhone向けWebアプリを作る (2)iPhoneっぽくなるスタイルシートを書く

前回の続きです。

iPhoneっぽくなるスタイルシートを書く

ネイティブアプリのように見せる部品ごとのスタイルシートを書いていきます。

以下を説明します。

  • NavigationBar のように見せる部品スタイル設定
  • BarButtonItem のように見せる部品スタイル設定
  • TableView のように見せる部品スタイル設定
テスト用 html を用意する

今回、最終的に作りたい画面はこんな感じです。

特徴は以下です。

  • ナビゲーションバーがある(最上部のタイトルを表示した領域)
    • iOS SDK での UINavigationBar に対応
  • ナビゲーションバーにはボタンが配置できる
    • これはiOS SDK での UIBarButtonItem に対応
  • ナビゲーションバーには"戻る"ボタンも配置できる。
  • 一覧をリスト状に表示できる
    • これはiOS SDK での UITableView (style は UITableViewStyleGrouped ライク)に対応。

テスト用に以下のような html ファイルを作成します。

test.html

<html >
  <head>
    <title>ToDo list</title>
    <meta name="apple-mobile-web-app-capable" content="yes" />
    <meta name="apple-mobile-web-app-status-bar-style" content="black" />
    <meta name="viewport" content="user-scalable=no, width=device-width" />
    <link rel="stylesheet" href="css/iphone.css" type="text/css" media="screen" />
    <link rel="stylesheet" href="css/todo.css" type="text/css" media="screen" />
  </head>
  <body >
    <div id="list" >
      <div class="navBar">
        <h1 ><a href="#">Here is title</a></h1>
        <a class="barButton right" href="#">Next</a>
        <a class="barButton back" href="#">Back</a>
      </div>

      <ul  >
        <li >
          <a href="#">List item 1</a>
        </li>
        <li >
          <a href="#">List item 2</a>
        </li>
        <li >
          <a href="#">List item 3</a>
        </li>
      </ul>
    </div>
  
  </body>
</html>

css ファイルにはなにも書いていません。
これを iPhone で見ると次のようになります。


paddingとmarginをゼロに

paddingとmarginはどうせ各部品で細かく調整設定します。なのではじめからゼロにしておきます。
ついでに全体のフォントは設定しておきます。

iphone.css内の最上部

 * {
  margin: 0;
  padding: 0;
}

body {
  font-family: Helavetica;
  font-size: 14px;
}

iPhone で見ると次のようになります。


NavigationBar のように見せる部品スタイル設定

次にナビゲーションバーのスタイルを設定します。test.html 内の navBar クラスを指定した div タグへのスタイル指定です。

iphone.css

.navBar {
  background-color: #ccc;
  background-image: -webkit-gradient(linear, left top, left bottom, from(#ccc), to(#999));
  border-color: #666
  border-style: solid;
  border-width: 0 0 1px 0;
}

.navBar h1, h1 a{
  color: #222;
  font-size: 20px;
  font-weight: bold
  margin: 0 auto;
  padding: 10px 0;
  text-align: center;
  text-shadow: 0px 1px 0px #fff;
}

ポイントは、以下といったところです。

  • .navBar で background-image にてグラデーション指定
  • .navBar h1 にて
    • font-weight でボールド
    • text-align でセンター寄せ
    • text-shadow で白色影

iPhone で見ると次のようになります。

BarButtonItem のように見せる部品スタイル設定

続いて、ナビゲーションバー上の BarButtonItem 状の部品のスタイル指定を行います。test.html 内の barButton クラスを指定した a タグへのスタイル指定です。

barButton では、ふたつのクラスを指定することでボタンを表現します。

  • a class="barButton right" : ナビゲーションバー上の右端ボタン
  • a class="barButton left" : ナビゲーションバー上の左端ボタン
  • a class="barButton back" : ナビゲーションバー上の左端戻るボタン

iphone.css

.navBar .barButton {
  position: absolute;
  top: 7px;
  max-width: 64px;
  overflow: hidden;
  text-align: center;
  line-height: 28px;
  text-overflow: ellipsis;
  text-decoration: none;
  border-width: 0 5px;
  padding: 0 3px;

  color: #fff;
  text-shadow: rgba(0,0,0,0.6) 0px -1px 0px;
  -webkit-border-image: url(../img/button.png) 0 5 0 5;
}

.navBar .barButton.back {
  border-width: 0 8px 0 14px;

  -webkit-border-image: url(../img/back_button.png) 0 8 0 14;
}

主な注意点は以下。

  • position は absolute
  • max-width と text-overflow でテキストが入り切らなかったときの対策
  • webkit-border-image で別途用意したボタンの画像と戻るボタンの画像を指定
    • 戻るボタンの場合、左右の幅指定を別にしていることに注意

続いて、ボタンを左右に指定する補助クラスを定義しておきます。

iphone.css

.right {
  right: 6px;
}

.left {
  left: 6px;
}

.back {
  left: 6px;
}

これで iPhone で見ると次のようになります。

TableView のように見せる部品スタイル設定

続いて、UITableView のように見せる部品スタイルを設定していきます。
UITableView には、UITableViewStylePlain と UITableViewStyleGrouped のふたつのスタイルがありますが、今回設定するのは UITableViewStyleGrouped の方にします。

まずは、ul タグを以下のように設定します。ul のアイテムの項目マークをなしにし、margin を指定しておきます。

iphone.css

ul {
  list-style: none;
  margin: 10px;
  padding: 0;
}

次にリスト項目の設定です。

iphone.css

ul li a {
  background-color: #ffffff;
  border: 1px solid #999999;
  color: #222222;
  display: block;
  font-size: 17px;
  font-weight: bold;
  margin-bottom: -1px;
  padding: 12px 10px;
  text-decoration: none;
}

ここで注意すべき内容は、以下です。

  • display: block; でブロック表示指定
  • text-decoration: none; でリンク下線は消しておく

また、UITableViewStyleGrouped の場合、リスト項目の1番目の項目の左上と右上の角、および、リスト項目の最後の項目の左下と右下の角、が丸くなっています。これを以下のように指定します。

iphone.css

ul li:first-child a {
  -webkit-border-top-left-radius: 8px;
  -webkit-border-top-right-radius: 8px;
}

ul li:last-child a{
  -webkit-border-bottom-left-radius: 8px;
  -webkit-border-bottom-right-radius: 8px;
}

最後に一応 a:active と a:hover に空指定をしておきます。
iphone.css

ul li a:active, ul li a:hover {
}

これで iPhone で見ると次のようになります。

以上で、今回作成したい画面のおおよそのスタイルは設定を終えます。色や画像の変更は todo.css ファイルでオーバーライドすることにします。

参考

今回の記事の内容は、次の本の内容を参考にカスタマイズしています。

iPhoneアプリケーション開発ガイド ―HTML+CSS+JavaScript による開発手法

iPhoneアプリケーション開発ガイド ―HTML+CSS+JavaScript による開発手法

GAEpでiPhone向けWebアプリを作る (1)全体的な見た目を作る

前回の続きです。

ネイティブアプリのように見せる全体的な設定

次の内容を説明します。

  • 表示領域の固定
  • ブラウザのアドレスバーとツールバーを消す設定
  • ステータスバーの色を変更する設定

html ファイルの head タグでは、5つの指定をします。

表示領域の固定

ページ幅を固定するために以下の指定をします。

html ファイルの head タグ内

<meta name="viewport" content="user-scalable=no, width=device-width" />

これにより、iPhone でのページ表示時に、ピンチイン/ピンチアウトでコンテンツの拡大/縮小されるのを防止します。

ブラウザのアドレスバーとツールバーを消す設定

Webアプリは通常ブラウザ内に表示されます。従って、ブラウザのアドレスバーとツールバーが表示され、その内側の領域にコンテンツが表示されます。
しかし下記の設定を行うことで、アイコンから起動したときに限ってですが、アドレスバーとツールバーを非表示にすることができます。

html ファイルの head タグ内

<meta name="apple-mobile-web-app-capable" content="yes" />

iPhoneのブラウザから表示すると次の図のように表示されます。

しかし、上記 meta タグの設定された状態で、このページをホーム画面にブックマークします。

そしてiPhone のホーム画面に追加されたアイコンからこのページを開くと以下のようにアドレスバーとツールバーが消えます。

これで、アイコンからWebアプリを立ち上げるとネイティブアプリのような見た目となります。

ステータスバーの色の設定

Webアプリ表示時にiPhoneのステータスバーの見た目を変更することができます。ただし、これもアイコンから立ち上げたときの効果です。
以下のように設定します。

html ファイルの head タグ内

<meta name="apple-mobile-web-app-status-bar-style" content="black" />

すると下記のようにステータスバーが黒色になります。

スタイルシートの指定

その他に、このWebアプリではふたつのスタイルシートを使用することにしています。
iphone.css ではiPhone共通のスタイルを、todo.css では、このWebアプリ特有のスタイルを指定するつもりで分けています。

html ファイルの head タグ内

<link rel="stylesheet" href="css/iphone.css" type="text/css" media="screen" />
<link rel="stylesheet" href="css/todo.css" type="text/css" media="screen" />

参考

今回の記事の内容は、下記の Apple 資料に記載されています。

また、次の本にも記述されています。

iPhoneアプリケーション開発ガイド ―HTML+CSS+JavaScript による開発手法

iPhoneアプリケーション開発ガイド ―HTML+CSS+JavaScript による開発手法

iPhone 向け Web サイトを作ろうと思っている方にはオススメです。

GAEpでiPhone向けWebアプリを作る (0)はじめに

はじめに

Google App Engine Python を使ったiPhone向けWebアプリを作ってみました。今後暇を見つけて、何回かにわけてこのブログにその内容を掲載したいと考えています。

iPhone向けアプリはたくさんありますが、たいていはiTunes App Storeに登録されているObjective-Cで書かれたネイティブアプリです。
一方、iPhone向けアプリにはもうひとつ、Webアプリというカテゴリがあります。こちらはApp Storeのようなポータルがないせいか(どこかにある?)あまり話題になることはありません。
iPhone向けWebアプリがどんなものかという個人的な興味で、ひとつ簡単なアプリを作ってみましたので、その内容をこのブログでログに残します。

それから Google App Engine (GAE) です。前にGAEpを使ってWebアプリをひとつ作ってみましたが、そのパワーを実感するためにもうちょっと実用的なアプリも作ってみようと思い、今回作業しました。その辺のノウハウの自分用備忘録もかねています。

iPhone向けWebアプリとは
  • メリット
    • iTunes App Storeに登録する必要がない
    • 従って、Appleの審査もないし、年会費も必要ない
    • Objective-Cxcodeも必要ない。従ってMacを購入する必要もない
    • HTML/CSS/javascriptで作れる
    • ネイティブアプリに近い見た目を提供できる
    • 特に、iPhoneのブラウザから「ホーム画面に追加」でiPhoneホーム画面にブックマークして立ち上げると、かなりネイティブアプリに近い使用感
  • デメリット
    • iTunes App Storeのようなポータルがない(メジャーではない)ので、見つけてくれるかどうか難しい
    • 課金の仕組みは、広告など自分で考える必要がある
何を作ったか

できるだけ簡単で、かつ実用性も高そうなアプリということで、ToDo管理アプリを作ってみることにしました。こんな感じです。

見た目がネイティブアプリにそこそこ近いのはわかるかと思います。
以下より実際に触ってみることができます。ただし、google IDによるログインが必要です。

http://penguinwatcher.appspot.com/todo/main

iPhone/iPod touchまたはパソコン版Safariでご確認ください。特にiPhone/iPod touchをご利用の場合は、上記URLにアクセスした後、「ホーム画面に追加」でiPhone/iPod touchのホームにアイコンを追加し、続いてそのアイコンから立ち上げてみてください。

仕様は以下です。できるだけ簡単な仕様にしました。

  • 認証を用いてToDo(やること)を個人別に管理できる
  • ToDoを登録できる。登録には以下を設定できる。
    • ToDoのタイトル(必須)
    • ToDoの詳細(任意)
    • ToDoの締め切り日付(必須)
  • ToDoの一覧を閲覧できる
    • 締め切りが近い20件
    • 締め切り後一定の日にちが経過したToDoアイテムは、サーバ側で自動削除
  • 登録済みのToDoの内容を閲覧および編集できる
  • 登録済みのToDoの内容を削除できる
前提条件