以前开发php时,一直在使用sae的平台和服务,非常的喜欢。现在在整openstack,所以改用python做一些东西,为了不来回切换两个语言,我决定学习学习django,方便做一些自己的东西。关于sae下python的使用,sae官方文档写的非常全面,我这里只是记录自己的一个学习过程

搭建本地开发环境

安装django

easy_install django

下载安装本地开发环境

git clone https://github.com/SAEPython/saepythondevguide.git
cd dev_server
python setup.py install

创建python项目

到sae.sina.com.cn下创建一个python项目

进入管理面板创建版本,版本号为1

使用svn下载代码

svn co https://svn.sinaapp.com/xxxxx/

进入主目录,发现一个1的文件夹,这个就是对应的django的工程目录

django-admin.py start project mysite
mv mysite/* 1    

在1下创建配置文件config.yaml,并写入如下内容

libraries:
- name: "django"
   version: "1.4"

在1下创建index.wsgi,内容如下

import sae
from mysite import wsgi
application = sae.create_wsgi_app(wsgi.application)    

项目创建完毕,在1中执行dev_server.py来启动sae项目,默认localhost:8080访问

我在这里遇到一个问题,我是用Windows虚拟的Linux,所以我在Windows下无法通过ip:8080访问到linux。看了d>ev_server.py的代码发现这里host是写死为localhost的,所以我将代码小改动了一下

#/usr/local/lib/python2.7/dist-packages/sae_python_dev.../EGG-INFO/scripts/dev_server.py
run_simple(option.host, options.port...)
if __name__ == '__main__':
    parser = Option.Parser()
    parser.add_option("--host",dest="host",default="localhost")    

这样就能通过增加–host,将外部访问的ip设定好了

实现一个简单的投票应用

在1目录下,创建应用

python manage.py startapp polls

修改配置文件settings

import os

if 'SERVER_SOFTWARE' in os.environ:
    from sae.const import(
                          MYSQL_HOST,
                          MYSQL_PORT,
                          MYSQL_USER,
                          MYSQL_PASS,
                          MYSQL_DB
                          )
else:
    MYSQL_HOST = "localhost"
    MYSQL_PORT = "3306"
    MYSQL_USER = "root"
    MYSQL_PASS = "xxxxx"
    MYSQL_DB = "app_polls"

DATABASES = {
    'default': {
        'ENGINE':   'django.db.backends.mysql',
        'NAME':     MYSQL_DB,
        'USER':     MYSQL_USER,
        'PASSWORD': MYSQL_PASS,
        'HOST':     MYSQL_HOST,
        'PORT':     MYSQL_PORT,
    }
}
...
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.
    os.path.join(os.path.dirname(__file__), 'templates'),
)

INSTALLED_APPS = (
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.sites',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    # Uncomment the next line to enable the admin:
    'django.contrib.admin',
    # Uncomment the next line to enable admin documentation:
    # 'django.contrib.admindocs',
    'polls'
)

这里的配置项主要是将SAE和本地开发环境区分开,在SAE环境下使用它们提供的变量就可以直接连接数据库了,不过记得要在SAE控制面板进行初始化

配置主urls,即mysite下的urls

from django.conf.urls import patterns, include, url

from django.contrib import admin
admin.autodiscover()

urlpatterns = patterns('',
    url(r'^admin/', include(admin.site.urls)),
    url(r'^polls/', include('polls.urls')),
)

在polls文件夹下修改urls

from django.conf.urls import patterns, url

urlpatterns = patterns('polls.views',
    url(r'^$', 'index'),
    url(r'^(?P<poll_id>\d+)/$', 'detail'),
    url(r'^(?P<poll_id>\d+)/results/$', 'results'),
    url(r'^(?P<poll_id>\d+)/vote/$', 'vote'),
)

在polls文件夹下创建model.py

from django.db import models


class Poll(models.Model):
    question = models.CharField(max_length=200)
    pub_date = models.DateTimeField('date published')


class Choice(models.Model):
    poll = models.ForeignKey(Poll)
    choice = models.CharField(max_length=200)
    votes = models.IntegerField()

然后在mysql中创建一个add_polls数据库,使用

python manage.py syncdb

同步数据库,这个仅限本地,如果要在sae使用的话,需要本地生成后导入到sae上。

在polls文件夹下创建view视图文件

from django.shortcuts import render_to_response, get_object_or_404
from django.template import RequestContext
from django.http import HttpResponseRedirect
from django.core.urlresolvers import reverse
from polls.models import Poll, Choice


#主页显示最新的5条投票列表
def index(request):
    latest_poll_list = Poll.objects.all().order_by('-pub_date')[:5]
    return render_to_response('index.html', {'latest_poll_list': latest_poll_list})


#获得某条信息详细情况
def detail(request, poll_id):
    p = get_object_or_404(Poll, pk=poll_id)
    return render_to_response('detail.html', {'poll': p},
                               context_instance=RequestContext(request))


#投票
def vote(request, poll_id):
    p = get_object_or_404(Poll, pk=poll_id)
    try:
        selected_choice = p.choice_set.get(pk=request.POST['choice'])
    except (KeyError, Choice.DoesNotExist):
        return render_to_response('detail.html', {
            'poll': p,
            'error_message': "You didn't select a choice.",
        }, context_instance=RequestContext(request))
    else:
        selected_choice.votes += 1
        selected_choice.save()
        return HttpResponseRedirect(reverse('polls.views.results', args=(p.id,)))


#显示投票结果
def results(request, poll_id):
    p = get_object_or_404(Poll, pk=poll_id)
    return render_to_response('results.html', {'poll': p})

在polls下创建templates文件夹,并创建以下三个文件

detail.html

<h1>{{ poll.question }}</h1>
{% if error_message %}<p><strong>{{ error_message }}</strong></p>{% endif %}
<form action="/polls/{{ poll.id }}/vote/" method="post">
{% csrf_token %}
{% for choice in poll.choice_set.all %}
    <input type="radio" name="choice" id="choice{{ forloop.counter }}" value="{{ choice.id }}" />
    <label for="choice{{ forloop.counter }}">{{ choice.choice }}</label><br />
{% endfor %}
<input type="submit" value="Vote" />
</form>

index.html

{% if latest_poll_list %}
    <ul>
    {% for poll in latest_poll_list %}
        <li><a href="/polls/{{ poll.id }}/">{{ poll.question }}</a></li>
    {% endfor %}
    </ul>
{% else %}
    <p>No polls are available.</p>
{% endif %}

results.html

<h1>{{ poll.question }}</h1>
<ul>
{% for choice in poll.choice_set.all %}
    <li>{{ choice.choice }} -- {{ choice.votes }} vote{{ choice.votes|pluralize }}</li>
{% endfor %}
</ul>
<a href="/polls/{{ poll.id }}/">Vote again?</a>