Introducing
Your new presentation assistant.
Refine, enhance, and tailor your content, source relevant images, and edit visuals quicker than ever before.
Trending searches
松尾 貴史
@tmatsuo
サイオステクノロジー株式会社
株式会社キャンディット
Google App Engine API Expert
Tokyo GTUG Organizer
Kay's daddy
Kay でも gtx
class Tag(db.Model):
name = db.StringProperty()
count= db.IntegerProperty()
class Post(db.Model):
tags = db.ListProperty(db.Key)
body = db.TextProperty(required=True)
created = db.DateTimeProperty(auto_now_add=True)
Post.tags stores keys of Tags
tags = []
for tag_name in tag_input.split():
tags.append(Tag.get_or_insert(key_name=tag_name))
post = Post(tags=[t.key() t in tags], body=body)
post.put()
t = Tag.get_by_key_name(tag_input)
if t is None:
raise Http404
posts = Post.all().filter("tags =", t.key()).fetch(200)
Redundant db operation
class Tag(db.Model):
name = db.StringProperty()
count= db.IntegerProperty()
class Post(db.Model):
tags = db.StringListProperty()
body = db.TextProperty(required=True)
created = db.DateTimeProperty(auto_now_add=True)
Post.tags stores Tags themself as string
tags = tag_input.split()
for tag_name in tags:
Tag.get_or_insert(key_name=tag_name)
post = Post(tags=tags, body=body)
post.put()
posts = Post.all().filter("tag =", tag_input).fetch(200)
Simple and fast
検索する場合は冗長に持とう
class Foo(db.Model):
prop1 = db.StringProperty()
prop2 = db.StringProperty()
prop3 = db.StringProperty()
prop4 = db.StringProperty()
prop5 = db.StringProperty()
prop6 = db.StringProperty()
class Bar(db.Model):
prop1 = db.StringProperty(indexed=False)
prop2 = db.StringProperty(indexed=False)
prop3 = db.StringProperty(indexed=False)
prop4 = db.StringProperty(indexed=False)
prop5 = db.StringProperty(indexed=False)
prop6 = db.StringProperty(indexed=False)
class Baz(db.Model):
prop1 = db.StringProperty(name="p1")
prop2 = db.StringProperty(name="p2")
prop3 = db.StringProperty(name="p3")
prop4 = db.StringProperty(name="p4")
prop5 = db.StringProperty(name="p5")
prop6 = db.StringProperty(name="p6")
@classmethod
def kind(cls):
return "z"
検索やソートしないならインデックスを作らない
q = MyModel.all().filter("contents =", "word1").\
filter("contents =", "word2").\
filter("contents =", "word3").\
order("-created")
entries = q.fetch(10)
q = MyModel.all().filter("contents =", "word1").\
filter("contents =", "word2").\
filter("contents =", "word3").\
filter("created_month =", this_month)
# TODO: This might be not enough
entries = q.fetch(1000)
# Sorting on memory
entries.sort(cmp=lambda x, y: cmp(x.created, y.created))
ソートもインデックス使うので気を付けましょう
# -*- coding: utf-8 -*-
from kay.utils import render_to_response
import hoge_utils
import fuga_utils
import moge_utils
def index(request):
return render_to_response('myapp/index.html', {'message': 'Hello'})
def hoge(request):
entries = hoge_utils.get_entries()
return render_to_response('myapp/hoge.html', {entries: entries})
def fuga(request):
entries = fuga_utils.get_entries()
return render_to_response('myapp/fuga.html', {entries: entries})
def moge(request):
entries = moge_utils.get_entries()
return render_to_response('myapp/moge.html', {entries: entries})
# -*- coding: utf-8 -*-
from kay.utils import render_to_response
def index(request):
return render_to_response('myapp/index.html', {'message': 'Hello'})
def hoge(request):
import hoge_utils
entries = hoge_utils.get_entries()
return render_to_response('myapp/hoge.html', {entries: entries})
def fuga(request):
import fuga_utils
entries = fuga_utils.get_entries()
return render_to_response('myapp/fuga.html', {entries: entries})
def moge(request):
import moge_utils
entries = moge_utils.get_entries()
return render_to_response('myapp/moge.html', {entries: entries})
一部でしか使わないモジュールは遅延ロードする
ユーザー新規登録→確認メール→クリックで完了
def create_temp_session(use_name):
import datetime
from google.appengine.api.labs import taskqueue
def txn():
salt = crypto.gen_salt()
activation_key = crypto.sha1(salt+user_name).hexdigest()
expiration_date = datetime.datetime.now() + \
datetime.timedelta(seconds=3600) # 一時間
taskqueue.add(url='/expire_registration',
registration_key=str(activation_key)),
eta=expiration_date, transactional=True)
session = TmpSession(key_name=activation_key)
db.put(session)
return session
session = db.run_in_transaction(txn)
return session
Paging に使えそう
# -*- coding: utf-8 -*-
# myapp.views
from myapp.models import MyModel
from kay.utils import render_to_response
PAGESIZE=10
def index(request, cursor=None):
q = MyModel.all()
if cursor:
q = q.with_cursor(cursor)
ms = q.fetch(PAGESIZE)
cursor = q.cursor()
if MyModel.all(cursor=cursor).get() is None:
# no more entities
cursor = None
return render_to_response('myapp/index.html',
{'ms': ms, 'next': cursor})
fetch + get しなきゃいけない?
# -*- coding: utf-8 -*-
# myapp.views
import logging
import base64
import os
from myapp.models import MyModel
from kay.utils import render_to_response
PAGESIZE=10
def index(request, cursor=None):
q = MyModel.all()
if cursor:
q = q.with_cursor(cursor)
ms = q.fetch(PAGESIZE+1)
if len(ms) == PAGESIZE+1:
raw_cursor = q._last_raw_query.GetCompiledCursor()
for p in raw_cursor.position_:
p.set_start_inclusive("true")
nextcursor = base64.urlsafe_b64encode(raw_cursor.Encode())
else:
nextcursor = None
return render_to_response('myapp/index.html',
{'ms': ms[:PAGESIZE],
'next': nextcursor})
Nick says "Be cautious"
ページングにはもう一捻り必要ぽい
RPCのタイムライン
簡単に使える
handlers:
...
...
- url: /stats.*
script: $PYTHON_LIB/google/appengine/ext/appstats/ui.py
- url: /.*
script: kay/main.py
普段のハンドラの上に書きましょう。
DjangoとかKayだとこれだけ
MIDDLEWARE_CLASSES = (
'google.appengine.ext.appstats.recording.AppStatsDjangoMiddleware',
)
webappでも大丈夫
1. Find the code that runs your application object. It most likely uses either 'util.run_wsgi_app()' (where 'util' is a submodule of the 'google.appengine.ext.webapp' package), or 'wsgiref.handlers.CGIHandler().run()'. If the former, you're ready for step 2.
Otherwise, you must insert:
from google.appengine.ext.webapp import util
at the top of the file (with the other imports) and replace the call to 'wsgiref.handlers.CGIHandler().run(app)' with a call to:
util.run_wsgi_app(app)
NOTE: If you have multiple entry points (top-level or "main" scripts), you must do this for each script.
2. Create a file named appengine_config.py in your app's root directory and add the following code to it:
def webapp_add_wsgi_middleware(app):
from appstats import recording
app = recording.appstats_wsgi_middleware(app)
return app
Thank you
Transactional Task Queue
Cursor
Paging -お薦めしない
Paging
自動で消える一時セッション
Custom Admin Page
Appstats
Adminコンソールに機能追加できる
app.yaml
admin_console:
pages:
- url: /stats
name: "Stats"
クリックすると
app.yaml
settings.py
正規化のし過ぎ
不要なインデックス
Model - after
Tagging - after
Model - before
Tagging - before
Logic - Put
Model
Logic - Search
Model - おまけ
Greedy module loading
Exploding Index
Query - after
Query - before
Views - after
Views - before
http://code.google.com/p/kay-framework/
# -*- coding: utf-8 -*-
"""
Copyright (c) 2009 by Takashi Matsuo <tmatsuo@candit.jp>
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the following
disclaimer in the documentation and/or other materials provided
with the distribution.
* The names of the contributors may not be used to endorse or
promote products derived from this software without specific
prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
"""
# TODO: slim3 wo pakuru