首先我们以一个简单的hello world应用介绍一下一个Python应用在SAE上的创建和部署过程。 编辑应用代码?SAE采用SVN来作为代码部署工具,使用SVN客户端检出应用helloworld的代码。 jaime@westeros:~$ svn co https://svn.sinaapp.com/helloworld 每个应用可以创建多个版本,这些版本可以在线上同时运行,每个版本以数字标示,其代码对应于SVN目录下以其版本号命名的目录。 进入helloworld目录,创建一个目录1作为默认版本,切换到目录1。 jaime@westeros:~$ cd helloworld jaime@westeros:~/helloworld$ mkdir 1 jaime@westeros:~/helloworld$ cd 1 创建应用配置文件config.yaml,内容如下: name: helloworld version: 1 创建index.wsgi,内容如下: import sae def app(environ, start_response): status = '200 OK' response_headers = [('Content-type', 'text/plain')] start_response(status, response_headers) return ['Hello, world!'] application = sae.create_wsgi_app(app) SAE上的Python应用的入口为 index.wsgi:application ,也就是 index.wsgi 这个文件中名为 application 的callable object。在helloworld应用中,该application为一个wsgi callable object。 部署应用?提交刚刚编辑的代码,就可以完成应用在SAE上的部署。 jaime@westeros:~/helloworld$ svn add 1/ jaime@westeros:~/helloworld$ svn ci -m "initialize project" 在浏览器中输入 http://helloworld.sinaapp.com ,就可以访问刚提交的应用了。 注解 SVN的仓库地址为:https://svn.sinaapp.com/<your-application-name>, 用户名和密码为SAE的安全邮箱和安全密码。 使用web开发框架?Django?目前SAE上预置了多个版本的Django,默认的版本为1.2.7,在本示例中我们使用1.4版本。 创建一个新的Python应用,检出SVN代码到本地目录并切换到应用目录。 创建一个Django project:mysite。 jaime@westeros:~/pythondemo$ django-admin.py startproject mysite jaime@westeros:~/pythondemo$ ls mysite manage.py mysite/ 重命名该project的根目录名为1,作为该应用的默认版本代码目录。 jaime@westeros:~/pythondemo$ mv mysite 1 在默认版本目录下创建应用配置文件 config.yaml ,在其中添加如下内容: libraries: - name: "django" version: "1.4" 创建文件index.wsgi,内容如下 import sae from mysite import wsgi application = sae.create_wsgi_app(wsgi.application) 最终目录结构如下 jaime@westeros:~/pythondemo$ ls 1 config.yaml index.wsgi manage.py mysite/ jaime@westeros:~/pythondemo/1$ ls 1/mysite __init__.py settings.py urls.py views.py 部署代码,访问 http://<your-application-name>.sinaapp.com ,就可看到Django的欢迎页面了。 完整示例 ( django tutorial 中的poll、choice程序) 处理用户上传文件?在setttings.py中添加以下配置。 # 修改上传时文件在内存中可以存放的最大size为10m FILE_UPLOAD_MAX_MEMORY_SIZE = 10485760 # sae的本地文件系统是只读的,修改django的file storage backend为Storage DEFAULT_FILE_STORAGE = 'sae.ext.django.storage.backend.Storage' # 使用media这个bucket STORAGE_BUCKET_NAME = 'media' # ref: https://docs./en/dev/topics/files/ 发送邮件?在settings.py中添加以下配置,即可使用sae的mail服务来处理django的邮件发送了。 ADMINS = ( ('administrator', 'administrator@gmail.com'), ) # ref: https://docs./en/dev/ref/settings/#email EMAIL_BACKEND = 'sae.ext.django.mail.backend.EmailBackend' EMAIL_HOST = 'smtp.example.com' EMAIL_PORT = 587 EMAIL_HOST_USER = 'sender@gmail.com' EMAIL_HOST_PASSWORD = 'password' EMAIL_USE_TLS = True SERVER_EMAIL = DEFAULT_FROM_EMAIL = EMAIL_HOST_USER 数据库的主从读写?参见Django官方文档 Multiple databases 如何syncdb到线上数据库?在本地开发环境中,如下配置数据库,即可执行 python manage.py syncdb 直接syncdb到线上数据库。 # 线上数据库的配置 MYSQL_HOST = 'w.rdc.sae.sina.com.cn' MYSQL_PORT = '3307' MYSQL_USER = 'ACCESSKEY' MYSQL_PASS = 'SECRETKEY' MYSQL_DB = 'app_APP_NAME' from sae._restful_mysql import monkey monkey.patch() DATABASES = { 'default': { 'ENGINE': 'django.db.backends.mysql', 'NAME': MYSQL_DB, 'USER': MYSQL_USER, 'PASSWORD': MYSQL_PASS, 'HOST': MYSQL_HOST, 'PORT': MYSQL_PORT, } } 警告 本feature还在开发中,目前还很buggy。 如何serve admin app的静态文件?方法一: 修改 settings.py 中的 STATIC_ROOT 为应用目录下 static 子目录的绝对路径。 运行 python manage.py collectstatic 将静态文件收集到应用的 static 子目录下。 修改 config.yaml ,添加对static文件夹下的静态文件的handlers。 handlers: - url: /static static_dir: path/to/mysite/static 方法二: 在开发调试(settings.py中DEBUG=True)过程中,可以将 staticfiles_urlpatterns 加到你的URLConf,让Django来处理admin app的静态文件: urls.py -------- from django.contrib import admin admin.autodiscover() urlpatterns = patterns('', ... # Uncomment the next line to enable the admin: url(r'^admin/', include(admin.site.urls)), ) from django.contrib.staticfiles.urls import staticfiles_urlpatterns urlpatterns += staticfiles_urlpatterns() 由于SAE默认static为静态文件目录,需要修改config.yaml,添加任意一条规则覆盖默认行为。 config.yaml ----------- ... handlers: - url: /foo static_dir: foo ref: https://docs./en/1.4/ref/contrib/staticfiles/ https://docs./en/1.4/howto/deployment/wsgi/modwsgi/#serving-the-admin-files Flask?index.wsgi import sae from myapp import app application = sae.create_wsgi_app(app) myapp.py import MySQLdb from flask import Flask, g, request app = Flask(__name__) app.debug = True from sae.const import (MYSQL_HOST, MYSQL_HOST_S, MYSQL_PORT, MYSQL_USER, MYSQL_PASS, MYSQL_DB ) @app.before_request def before_request(): g.db = MySQLdb.connect(MYSQL_HOST, MYSQL_USER, MYSQL_PASS, MYSQL_DB, port=int(MYSQL_PORT)) @app.teardown_request def teardown_request(exception): if hasattr(g, 'db'): g.db.close() @app.route('/') def hello(): return "Hello, world! - Flask" @app.route('/demo', methods=['GET', 'POST']) def greeting(): html = '' if request.method == 'POST': c = g.db.cursor() c.execute("insert into demo(text) values(%s)", (request.form['text'])) html += """ <form action="" method="post"> <div><textarea cols="40" name="text"></textarea></div> <div><input type="submit" /></div> </form> """ c = g.db.cursor() c.execute('select * from demo') msgs = list(c.fetchall()) msgs.reverse() for row in msgs: html += '<p>' + row[-1] + '</p>' return html Bottle?index.wsgi from bottle import Bottle, run import sae app = Bottle() @app.route('/') def hello(): return "Hello, world! - Bottle" application = sae.create_wsgi_app(app) web.py?index.wsgi import os import sae import web urls = ( '/', 'Hello' ) app_root = os.path.dirname(__file__) templates_root = os.path.join(app_root, 'templates') render = web.template.render(templates_root) class Hello: def GET(self): return render.hello() app = web.application(urls, globals()).wsgifunc() application = sae.create_wsgi_app(app) Tornado?WSGI模式 index.wsgi import tornado.wsgi import sae class MainHandler(tornado.web.RequestHandler): def get(self): self.write("Hello, world! - Tornado") app = tornado.wsgi.WSGIApplication([ (r"/", MainHandler), ]) application = sae.create_wsgi_app(app) 使用Torando Worker config.yaml name: pylabs version: 1 worker: tornado index.wsgi import tornado.web from tornado.httpclient import AsyncHTTPClient class MainHandler(tornado.web.RequestHandler): @tornado.web.asynchronous def get(self): http = tornado.httpclient.AsyncHTTPClient() http.fetch("http://wiki.", callback=self._callback) self.write("Hello to the Tornado world! ") self.flush() def _callback(self, response): self.write(response.body) self.finish() settings = { "debug": True, } # application should be an instance of `tornado.web.Application`, # and don't wrap it with `sae.create_wsgi_app` application = tornado.web.Application([ (r"/", MainHandler), ], **settings) 注解
小技巧 以上所有的示例代码的完整版本都可以在我们的github repo中获得。 https://github.com/sinacloud/sae-python-dev-guide/tree/master/examples/ |
|