gae meets django
TRANSCRIPT
使用Django创建Google App Engine应用
• Google App Engine介绍
• Django介绍
• 代码示例
Google App Engine介绍
什么是Google App Engine• Google的Web Hosting服务
•将你的Web应用部署到Google的基础设施之上
•使你的应用能够自动Scaling和load balancing
•提供数据存储服务
•集成了Google User认证和Gmail等服务
运行环境和限制• Python 2.5.2
•内置Django 0.96.1,并支持所有支持CGI的框架(以及任何使用CGI适配器的WSGI框架)
•运行在Sandbox中,不能访问文件系统,不能建立socket,不支持cron job,不能创建子进程
•限定时间内必须返回response
•应用必须是纯Python,不支持C的扩展
存储-Datastore•基于Google BigTable,分布式存储服务
•面向对象,非关系型数据库,不支持ORM
•相同Model可以有不同的属性
•支持查询,排序,事务
•不支持join, sum, avg等,不支持存储过程
•每次查询最多返回1000条记录
•全文检索?
App Engine Service
• Google帐号认证
• Gmail(发送email)
• URL Fetch
• Memcached
• Image (PIL)
价格(免费部分)Fixed Quota Per Day Usage Quotas
大约500万PV/月
价格(收费部分)
• $0.10 - $0.12 per CPU core-hour
• $0.15 - $0.18 per GB-month of storage
• $0.11 - $0.13 per GB outgoing bandwidth
• $0.09 - $0.11 per GB incoming bandwidth
申请Google App Engine• http://appengine.google.com/
•通过Gmail帐号
•通过短信认证
•域名: http://yourapp.appspot.com
•可以通过Google Apps绑定自己的域名
用途
•学习Web开发
•尝试各种idea, 减少startup的前期投入成本
•解决scalability问题
• App Gallery http://appgallery.appspot.com/
未来计划•对更多语言的支持
•收费计划
•大数据量的上传和下载
•离线处理
•数据导入/导出
Django简介
Django简介
•一个基于Python的full-stack Web框架
• ORM, URL mapping, admin interface, template, middleware, i18n, cache...
•很快很强大
为什么用Django(而不是webapp)
• Google App Engine Helper for Django
•功能更强大
•可移植性
项目示例-Blog系统
项目名称 - OnlyPython
创建开发环境
• 下载Google App Engine SDK
http://code.google.com/appengine/downloads.html
• 从SVN下载最新的Django源代码
http://code.djangoproject.com/svn/django/trunk/
• 下载Google App Engine Helper for Django
http://code.google.com/p/google-app-engine-django/
项目目录结构--- appengine-django (app engine helper for django 源文件目录)
--- django (django源文件目录)
--- onlypy (项目代码目录)
--- static (静态文件目录,存放js, css, 图片等)
--- app.yaml (app engine配置文件)
--- index.yaml (app engine索引配置文件)
--- main.py (app engine的启动脚本)
--- manage.py (Django的管理脚本)
--- settings.py (项目配置文件)
--- urls.py (URL mapping)
app.yamlapplication: onlypython
version: 1
runtime: python
api_version: 1
handlers:
-‐ url: /static
static_dir: static
-‐ url: /.*
script: main.py
main.pyimport osimport sysimport logging
from appengine_django import InstallAppengineHelperForDjangoInstallAppengineHelperForDjango()
# Google App Engine imports.from google.appengine.ext.webapp import util
# Import the part of Django that we use here.import django.core.handlers.wsgi
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()
settings.pyTIME_ZONE = 'UTC'
MIDDLEWARE_CLASSES = (
'django.middleware.common.CommonMiddleware',
'appengine_django.auth.middleware.AuthenticationMiddleware',
)
ROOT_URLCONF = 'urls'
ROOT_PATH = os.path.dirname(__file__)
TEMPLATE_DIRS = (
os.path.join(ROOT_PATH, 'onlypy/templates')
)
INSTALLED_APPS = (
'appengine_django',
'django.contrib.auth',
'onlypy.blog',
)
TODO: 添加Blog
models.pyfrom google.appengine.ext import db
class Category(db.Model): name = db.StringProperty() def __str__(self): return self.name
class Post(db.Model): author = db.UserProperty() title = db.StringProperty(required=True, verbose_name=u'标题') tag = db.StringProperty(verbose_name=u'标签') content = db.TextProperty(required=True, verbose_name=u'内容')
create_time = db.DateTimeProperty(auto_now_add=True) update_time = db.DateTimeProperty(auto_now=True) category = db.ReferenceProperty(Category, required=True, verbose_name=u'类别')
is_published = db.BooleanProperty(verbose_name=u'已发布')
def get_absolute_url(self) : return '/post/%s/'%self.key().id()
forms.pyfrom google.appengine.ext.db import djangoforms as formsfrom models import Post
class PostForm(forms.ModelForm): class Meta: model = Post exclude = ['author']
views.pydef add_post(request):
if request.method == 'GET': form = PostForm() if request.method == 'POST': form = PostForm(request.POST) if form.is_valid(): post = form.save() post.author = users.get_current_user() post.put() return HttpResponseRedirect('/post/add/')
return render_to_response('blog/add_post.html', {'form': form}, context_instance=RequestContext(request))
add_post.html{% extends "base.html" %}
{% block content %}<h1>添加新文章</h1>
<form name="mainForm" method="post" action=""> {{form.as_p}} <input type="submit" value="保存"/>
</form>{% endblock %}
urls.pyfrom django.conf.urls.defaults import *
urlpatterns = patterns('onlypy.blog.views', (r'^post/add/$', 'add_post'),)
Run• cd /yourpath/onlypython/
• python ./manage.py runserver 127.0.0.1:8000
TODO: 显示Blog列表
views.pydef list_post(request):
posts = Post.all().order('-‐create_time') if (not is_admin()): posts = posts.filter("is_published", True) return object_list(request, queryset=posts, allow_empty=True, template_name='blog/list_post.html', extra_context={'is_admin': is_admin()}, paginate_by=20)
index.yamlindexes:
-‐ kind: Post properties: -‐ name: is_published -‐ name: create_time direction: desc
list_post.html{% extends "base.html" %}{% load markup %}{% load paginator %}
{% block content %}{% for post in object_list %} <table border="0" cellspacing="0" cellpadding="0" width="100%"> <tr> <td valign="top"> <h1 style="margin-‐bottom: 2px;"> <a href="/post/{{post.key.id}}"> [{{ post.category.name }}] {{ post.title }}{% if post.is_published %}{%else%}(未发布){%endif%}
</a> </h1> <p style="padding:0px; margin: 0px;" class="small_font"> 类别: <a href="/category/{{ post.category.key.id }}/">
{{ post.category.name }}</a> <span style="padding-‐left: 10px;">{{ post.author.nickname }}写于{{ post.create_time|date:"Y-‐M-‐d H:i" }}</span>
</p> </td> </tr></table><div style="padding-‐left: 30px;"> {{ post.content|markdown }}</div>{% endfor %}{% paginator %}{% endblock %}
urls.pyfrom django.conf.urls.defaults import *
urlpatterns = patterns('onlypy.blog.views', (r'^post/add/$', 'add_post'), (r'^$', 'list_post'),)
项目信息• 项目代码 http://code.google.com/p/onlypy/
• 项目演示 http://www.onlypython.com
Q&A