Google Developer Day2008

June 11, 2008 at 10:06 AM | View Comments

今日はGoolge developer day2008に行きました。

http://farm4.static.flickr.com/3015/2566861476_40ae70f212_m.jpg

午後からはGoogle app engineのHackathonに参加。

Image APIが登場してから、しばらくたちますが、サンプルのものはDjangoを使ったものではないので Djangoだけをつかってやってみたかったのですが、変なところでハマりまくって時間内に動くものができませんでした。 だめだなぁ。

途中からはOS-X10.5にPIL (Python Image Library)をインストールするのに四苦八苦。OS-XはIPythonとかPILとか入った状態で出荷してほしいな。

家に帰ってとりあえず作ったのが以下

非常に単純なのでアレですが、一応公開してみます。

http://farm4.static.flickr.com/3061/2568643513_4e330552cf.jpg?v=0

/app.yaml

application: ueblog33
version: 1
runtime: python
api_version: 1

handlers:

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

/index.yaml

自動生成

/django

開発版のdjangoをそのまま

/main.py

import os
import sys

# Google App Engine imports.
from google.appengine.ext.webapp import util
# Remove the standard version of Django.
for k in [k for k in sys.modules if k.startswith('django')]:
  del sys.modules[k]
# Force sys.path to have our own directory first, in case we want to import
# from it.
sys.path.insert(0, os.path.abspath(os.path.dirname(__file__)))

# Must set this env var *before* importing any part of Django
os.environ['DJANGO_SETTINGS_MODULE'] = 'photoapp.settings'

# Import the part of Django we need.
import django.core.handlers.wsgi
import django.core.signals
import django.db
import django.dispatch.dispatcher

# Unregister the rollback event handler
django.dispatch.dispatcher.disconnect(
  django.db._rollback_on_exception,
  django.core.signals.got_request_exception)

# Log errors
#django.dispatch.dispatcher.connect(
#    log_exception,
#    django.core.signals.got_request_exception)

def main():
  # Create a Django application for WSGI.
  application = django.core.handlers.wsgi.WSGIHandler()

  # Run the WSGI CGI handler with that application
  util.run_wsgi_app(application)

if __name__ == '__main__':
  main()

誰か公開していたものをそのまま流用

/photoapp

(python ./django/bin/django-admin.py startproject photoappで作成)

/photoapp/__ini__.py

自動生成

/photoapp/manage.py

自動生成

/photoapp/settings.py

DEBUG = True
TEMPLATE_DEBUG = DEBUG

ADMINS = (
    # ('Your Name', 'your_email@domain.com'),
)

MANAGERS = ADMINS
TIME_ZONE = 'Tokyo/Asia'
LANGUAGE_CODE = 'ja'
SITE_ID = 1
USE_I18N = True
MEDIA_ROOT = ''
MEDIA_URL = ''
ADMIN_MEDIA_PREFIX = '/media/'
SECRET_KEY = '' #自分のを使う
TEMPLATE_LOADERS = (
    'django.template.loaders.filesystem.load_template_source',
    'django.template.loaders.app_directories.load_template_source',
#     'django.template.loaders.eggs.load_template_source',
)

MIDDLEWARE_CLASSES = (
    'django.middleware.common.CommonMiddleware',
    'django.contrib.sessions.middleware.SessionMiddleware',
    #'django.contrib.auth.middleware.AuthenticationMiddleware',
    'django.middleware.doc.XViewMiddleware',
)
ROOT_URLCONF = 'photoapp.urls'
import os
ROOT_PATH = os.path.dirname(os.path.abspath(__file__))
TEMPLATE_DIRS = (
    os.path.join(ROOT_PATH, 'templates'),
)
INSTALLED_APPS = (
    #'django.contrib.auth',
    'django.contrib.contenttypes',
    #'django.contrib.sessions',
    'django.contrib.sites',
    'photoapp.photo'
)
TEMPLATE_CONTEXT_PROCESSORS = (
   #'django.core.context_processors.auth',
   'django.core.context_processors.debug',
   'django.core.context_processors.i18n',
   #'django.core.context_processors.media',
   'django.core.context_processors.request',
)

(自動生成を修正)

/phtoapp/urls.py

from django.conf.urls.defaults import *

urlpatterns = patterns('',
    (r'^photo/', include('photoapp.photo.urls')),
)

/photoapp/photo

( cd photoapps

python manage.py startapp photo

)

で生成

/photoapp/photo/__init__.py

(自動生成)

/photoapp/photo/urls.py

from django.conf.urls.defaults import *

urlpatterns = patterns('',
    (r'^$', 'photoapp.photo.views.index'),
    (r'^upload/$', 'photoapp.photo.views.upload'),
    (r'^image/(?P<img_id>\w+)/$', 'photoapp.photo.views.image' ),
)

/photoapp/photo/models.py

from google.appengine.ext import db

class Photo(db.Model):
    photo = db.BlobProperty()
    title = db.StringProperty(required=True)

/photoapp/photo/views.py

from django.http import HttpResponse
from django.shortcuts import render_to_response
from django import newforms as forms
from photoapp.photo.models import Photo

from google.appengine.ext import db
from google.appengine.api import images

class UploadForm(forms.Form):
    title = forms.CharField(max_length=100, min_length=1)
    photo  = forms.FileField(required=True)

def index(request):
    queryset = Photo.all()
    return render_to_response(
        'upload.html', dict(object_list=queryset)
    )

def upload(request):
    if request.method == "POST":
        form = UploadForm(request.POST, request.FILES) #FileField使った場合はrequest.FILESを忘れずに
        if form.is_valid():
            photo = Photo(
                  title=request.POST["title"],
                     )
            photo_object = form.cleaned_data["photo"]
            content = images.resize(photo_object.content,  200, 200) #image apiを使ってリサイズ
            photo.photo = db.Blob(content) #binaryデータはdb.Blobを使う
            photo.put()
            form = UploadForm()
    else:
        form = UploadForm()
    queryset = Photo.all()
    return render_to_response(
        'upload.html', dict(
                    form=form,
                    object_list=queryset)
    )

def image(request, img_id): #Photoインスタンスのkeyを元に画像を表示
    photo = Photo.get(img_id)
    if photo:
        return HttpResponse(photo.photo, content_type="image/jpg")
    else:
        return HttpResponse("NO IMAGE") #本当はNO IMAGEのイメージを出す

/photoapp/photo/templates/upload.html

<html>
<body>
{% if form %}
  <form action="." enctype="multipart/form-data" method="POST"><!-- enctype 忘れないように -->
  {{ form }}
<input type="submit">
</form>
{% endif %}
{% for object in object_list %}
  <div>{{ object.title }}</div>
  <div><img src='/photo/image/{{ object.key }}/'></img></div>

{% endfor %}
</body>
</html>

一応newformsを使ってます。

本来なら画像のファイル形式をみたり、いろいろな画像サイズを作ったりする必要があります。

前半プレゼンだったのですが、思いっきり英語でした。英語できないだめだな〜〜。

WSGIはダブルセスジアイと言ってました ウィスキーって言ってたおれ涙目

あまりにつまらないところでハマったので、知り合い以外と絡むこともなく、

せっかくGoogleの人がいっぱいいたのに何も質問できず、、、、、。

事前準備は重要ですね。

貴重な一日を無駄にしてしまった。。。。

しかし、日本に来てまだ1年半なのに、「この人英語しゃべれるのかな?」と疑問に思うほど日本語が上手な

Ian Lewisさん

http://www.ianlewis.org/index.php/jp/ (日本語)

http://www.ianlewis.org/ (英語)

とも知りあえたし、楽しかったです。

帰りはIanさんと、大谷さんやDjango-jaの人たちと食事して帰りました。

hackasonでもらったTシャツ

http://farm4.static.flickr.com/3172/2569483674_09d79661d6.jpg?v=0
categories: python, googleappengine, django
Read and Post Comments

Google app engine用汎用ビュー date_based

April 15, 2008 at 10:49 PM | View Comments

まだ作りかけですが、Google app engine用の汎用ビューdate_basedを作ってみました。

コードはCodeReposのコミット権をもらったので、そちらにあげておきました。

http://coderepos.org/share/browser/lang/python/gae_django/views/generic/date_based.py

template_nameを必ず渡す必要があるのと、object_detailができてません。

また、Google app engineのTIMEZONEはUTCなのですが、そのへんを考慮してないので日付の指定がズレてしまいます。そのうちsettings.pyのTIME_ZONEを見てその辺の修正をかけるようにする予定です。

テンプレートに表示するときはtemplatetagsで時刻を修正する予定です。

http://coderepos.org/share/browser/lang/python/gae_django/templatetags/datefilter.py

pytzが必要です。

categories: django_randomnote, python, googleappengine
Read and Post Comments

Google app engineでDjangoを動かす

April 09, 2008 at 10:36 AM | View Comments

まず、普通にGoogle app engineを使えるようにするには秋元さんのエントリーが詳しいのでそちらを参考に

秋元さんはWindowsを使っているがMacの場合はdmgとpkgでインストールする。

ローカル環境であれこれするdev_appserver.pyと、デプロイするためのappcfg.pyはそれぞれ/user/local/bin/に入る

  • /usr/local/bin/dev_appserver.py
  • /usr/local/bin/appcfg.py

またdemoやlibなどのファイルたちは/usr/local/google_appengineにある。

Google app engineはPythonは2.5が必要なのでOS-X10.5以外の人は別途インストールしましょう。Djangoのインストールだがeveresさんの記事が分かりやすいです。

http://www.everes.net/2007/sep/06/install-django-for-begginers-osx/

アプリを作るディレクトリに移ります(どこでもいい)

% cd ~/tmp

/usr/local/google_appengine/libにあるdjangoを使ってプロジェクトを作る(今回はueblog33というアプリ名にしました)

% python /usr/local/google_appengine/lib/django/django/bin/django-admin.py startproject ueblog33

次のように4つのファイルができているはずです。

% ls ueblog33

__init__.py manage.py settings.py urls.py

アプリ直下にdjango自体が必要なのでコピーしましょう

% cp -r /usr/local/google_appengine/lib/django/django ueblog33/django

google app engineを使うためにはueblog33/main.pyを作る必要がある。

http://code.google.com/appengine/articles/django.html

のmain.pyをコピーしてしまいましょう。しかし、#Log errorの後の行はエラーになるので#でコメントアウトしておきましょう。

# Log errors
#django.dispatch.dispatcher.connect(
#    log_exception,
#    django.core.signals.got_request_exception)

次app.yamlをhttp://code.google.com/appengine/articles/django.html を参考につくる

application: ueblog33
version: 1
runtime: python
api_version: 1

handlers:
- url: /static
  static_dir: static

- url: /.*
  script: main.py

静的ファイルを作る場所を指定してあるようなのでstaticディレクトリを作っておきましょう

% mkdir ueblog33/static

次にアプリを作ります。今回はhogeという名前のアプリにします。

% cd ueblog33

% python manage.py startapp hoge

次にsettings.pyの設定。やはりhttp://code.google.com/appengine/articles/django.htmlの例でいいのですが

  • ROOT_URLCONF = 'urls' にする
  • INSTALLED_APPSに今さっきつくったアプリを入れる

ようにしましょう。

# Django settings for ueblog project.
import os
DEBUG = True
TEMPLATE_DEBUG = DEBUG
ADMINS = (
    # ('Your Name', 'your_email@domain.com'),
)
MANAGERS = ADMINS

LANGUAGE_CODE = 'ja'

SITE_ID = 1

USE_I18N = True

MEDIA_ROOT = ''

MEDIA_URL = ''

ADMIN_MEDIA_PREFIX = '/media/'

SECRET_KEY = '' #ここは自分のやつを使ってください
TEMPLATE_LOADERS = (
    'django.template.loaders.filesystem.load_template_source',
    'django.template.loaders.app_directories.load_template_source',
#     'django.template.loaders.eggs.load_template_source',
)

MIDDLEWARE_CLASSES = (
    'django.middleware.common.CommonMiddleware',
    'django.contrib.sessions.middleware.SessionMiddleware',
    #'django.contrib.auth.middleware.AuthenticationMiddleware',
    'django.middleware.doc.XViewMiddleware',
)

ROOT_URLCONF = 'urls'

ROOT_PATH = os.path.dirname(__file__)
TEMPLATE_DIRS = (
    # Put strings here, like "/home/html/django_templates" or "C:/www/django/templates".
    # Always use forward slashes, even on Windows.
    # Don't forget to use absolute paths, not relative paths.
    ROOT_PATH + '/templates',
)

INSTALLED_APPS = (
    #'django.contrib.auth',
    'django.contrib.contenttypes',
    #'django.contrib.sessions',
    'django.contrib.sites',
    'hoge',
)

次にueblog33/urls.pyの編集

今回は

http://ueblog33.appspot.com/hoge/list/

で一行掲示板を作りたいので以下の通り

from django.conf.urls.defaults import *

urlpatterns = patterns('',
     (r'^hoge/list/$', 'hoge.views.bbs_list'),
)

次に/hoge/list/でアクセスされた場合の処理を任せるueblog/hoge/views.pyを作ります。

google app engineではdjango.modelを使えないのでgoogle.appenine.ext.dbを使ってます。また、googleのusersも使ってみました。

#!-*- coding:utf-8 -*-
from django.shortcuts import render_to_response
from django import newforms as forms
from google.appengine.ext import db
from google.appengine.api import users
import datetime

class Entry(db.Model):
    created = db.DateTimeProperty()
    body = db.StringProperty(required=True)

class EntryForm(forms.Form):
    body = forms.CharField(max_length=100, min_length=1)

def bbs_list(request):
    form = EntryForm(request.POST or None)
    if form.is_valid():
        e = Entry(body=unicode(request.POST["body"], 'utf8'),  #unicodeで渡す必要がある
            created=datetime.datetime.now())
        e.put()
        form = EntryForm()
    query = Entry.all()
    query.order('created')
    user = users.get_current_user()
    if user:
        nickname = ("%s (<a href='%s'>sign out</a>)" % (user.nickname(), users.create_logout_url("/hoge/list/")))
    else:
        nickname = "名無しさん <a href='%s'>Sign in or register</a>." %  users.create_login_url("/hoge/list/")
    return render_to_response('hoge/entry_list.html', {'form':form, 'object_list':query, 'nickname':nickname})

最後の行でhoge/entry_list.htmlというテンプレートを使うようにしてるのでそれもつくります。

% mkdir hoge/templates

% mkdir hoge/templates/hoge

まず、ベースになるbase.htmlを ueblog33/hoge/template/base.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>{% block title %}{% endblock %}</title>
    </head>
    <body>
       {% block content %}
       {% endblock %}
    </body>
</html>

つぎに ueblog33/hoge/template/hoge/entry_list.htmlを作ります。

{% extends "base.html" %}
{% block title %}HOGE{% endblock %}
{% block content %}
<ul>
{% for object in object_list %}
<li>{{ object.body|escape }} : {{ object.created|date:"Y-m-d (D) H:i:s" }} : {{ nickname }}</li>
{% endfor %}
</ul>
<p>
  <form method="POST" action="." >
      {{ form }}
   <input type="submit" />
   </form>
</p>
 {% endblock %}

次にローカル環境で動かす

最初のueblog33の親ディレクトリに移動して

% cd ..

% python /usr/local/bin/dev_appserver.py ueblog33

をするとhttp://localhost:8080/hoge/list/

で先ほどつくったアプリが動く。Ctrl + C で止まる(Windowsは止まらないのでタスクマネージャーから無理矢理止めた)

ちなみにローカルのdatastoreをクリアするには

% python dev_appserver.py --clear_datastore ueblog33/

とやる。

うまく動いているようなら

% python /usr/local/bin/appcfg.py update ueblog33

でデプロイできます。

django.modelが使えないのならdjango使う旨味はない気もしましたが、djangoはテンプレートとかよくできてるのでそれはそれでありな気がします。

試してから書きますけどThe Expando Classはかなり衝撃的な気がします。

http://code.google.com/appengine/docs/datastore/expandoclass.html

※ よくよく考えてみたらgoogle app engineに入ってるdjango 0.96じゃなくて最新版のでもいいのではないかと思ったらeveresさんがやっていた。

http://www.everes.net/2008/apr/09/django-on-google-app-engine/

categories: googleappengine, django
Read and Post Comments

track feed ueblog