GoogleAppEngine/Python(以下、GAE)でDjangoを動かしたときのメモを残します。
事前調査
GAEに含まれているバージョンについて
GAEにどのバージョンが含まれているかを調べてみたところ、Django1.5をalphaサポートしているようでした。
Libraries in Python 2.7 - Python — Google Cloud Platform
ただ、現在の最新版は1.9であり、LTSも1.8なので、そのままでは使えなさそうでした。
GAEへDjangoを持ち込むことについて
以下の公式ドキュメントより、Django1.8をGAEへ持ち込めそうでした。
Libraries in Python 2.7 - Python — Google Cloud Platform
ただ、GAEのデータストアはNDB storage APIとかで扱うなど、そのままでは使いづらそうでした。
そのため、その部分をうまくやるライブラリがないかを探したところ、Django nonrel
とDjangae
がありました。以下によるとDjangaeの方がメンテナンスされていそうでした。
Qiitaの記事では、Django1.8とdjangae-scaffoldで動作確認をとっていたため、今回は
- djangae-scaffold無し
- Django1.8 + Djangae
での動作確認をとることにしました。
環境
- Windows10
- Google App Engine SDK for Python 1.9.30-2015-11-18
- デフォルトでインストール
- Python 2.7.11
- デフォルトでインストール、インストール先は
c:\python27
- デフォルトでインストール、インストール先は
- virtualenv環境でpip install
- Django 1.8.7
- プロジェクト名:
myproject
- アプリ名:
mysite
- Class-based Viewsを使用するアプリ
- プロジェクト名:
- Djangae 0.9.2
- Django 1.8.7
なお、ディレクトリ構成は以下の通りです。
d:\Sandbox\djangae-thinkami-sample ├── env\ (virtualenv環境) │ └── ... ├── libs\ (GAEに持ち込むサードパーティライブラリのインストール先) │ └── ... ├── myproject\ (Djangoプロジェクトのディレクトリ) │ ├── settings.py │ └── ... ├── mysite\ (Djangoアプリのディレクトリ) │ ├── fixtures\ │ ├── templates\ │ ├── migrations\ │ └── ... ├── app.yaml ├── appengine_config.py ├── manage.py ├── requirements.txt └── ...
流れ
pip install
# Djangoプロジェクト用のディレクトリを作成 (env) d:\Sandbox>mkdir djangae-thinkami-sample (env) d:\Sandbox>cd djangae-thinkami-sample # virtualenv環境を作り、activate d:\Sandbox\djangae-thinkami-sample>virtualenv -p c:\python27\python.exe env d:\Sandbox\djangae-thinkami-sample>env\Scripts\activate # pip install: GAEへ持っていくため、`-t`オプションを使ってDjango/Djangaeをインストール (env) d:\Sandbox\djangae-thinkami-sample>pip install -t libs django==1.8.7 (env) d:\Sandbox\djangae-thinkami-sample>pip install djangae -t libs # 確認 (env) d:\Sandbox\djangae-thinkami-sample>pip list djangae (0.9.2) Django (1.8.7) pip (7.1.2) setuptools (18.2) wheel (0.24.0)
Djangoプロジェクト・アプリのひな形を作成
以下を参考に、Djangoプロジェクトを現在のディレクトリへ展開します。
python - Force django-admin startproject if project folder already exists - Stack Overflow
# 現在のディレクトリにDjangoプロジェクトを作成 # 末尾の`.`を忘れずに付ける (env) d:\Sandbox\djangae-thinkami-sample>python .\libs\django\bin\django-admin.py startproject myproject . # Djangoアプリを作成 (env) d:\Sandbox\djangae-thinkami-sample>python manage.py startapp mysite
Djangae向けの設定を実施
以下のDjangaeの公式ドキュメントをもとに、各種設定を行います。
Installation & Deployment - Djangae Documentation
なお、Python2.7系のため、コメントに日本語を使う場合は# -*- coding: utf-8 -*-
をファイルの先頭に追加します。
d:\Sandbox\djangae-thinkami-sample\app.yaml
GAEで必要な設定を行うapp.yaml
ファイルを作成します。
application: djangaethinkamisample version: 20151228 runtime: python27 api_version: 1 threadsafe: true handlers: - url: /_ah/(mapreduce|queue|warmup).* script: myproject.wsgi.application login: admin - url: /.* script: myproject.wsgi.application
なお、app.yaml
の詳細は以下の公式ドキュメントにあります。
Configuring with app.yaml - Python — Google Cloud Platform
d:\Sandbox\djangae-thinkami-sample\myproject\settings.py
先頭にdjangae.settings_base
のimportを追加します。
from djangae.settings_base import * # Build paths inside the project like this: os.path.join(BASE_DIR, ...) import os
INSTALL_APPS
に追加します。
なお、djangaeが先頭でない場合、You must place 'djangae' before any 'django' apps in INSTALLED_APPS
というエラーになります。
INSTALLED_APPS = ( 'djangae', # add thinkAmi 'django.contrib.admin', ... 'django.contrib.staticfiles', 'mysite', # add thinkAmi )
公式ドキュメントでも推奨されているため、MIDDLEWARE_CLASSES
にdjangae.contrib.security.middleware.AppEngineSecurityMiddleware
を追加します。
MIDDLEWARE_CLASSES = ( 'djangae.contrib.security.middleware.AppEngineSecurityMiddleware', # add thinkAmi 'django.contrib.sessions.middleware.SessionMiddleware', ... )
d:\Sandbox\djangae-thinkami-sample\manage.py
このあたりに以下の一行を変更します。
# from django.core.management import execute_from_command_line from djangae.core.management import execute_from_command_line
d:\Sandbox\djangae-thinkami-sample\myproject\wsgi.py
このあたりを変更します。
# Modify thinkAmi: djangae settings # application = get_wsgi_application() from djangae.wsgi import DjangaeApplication application = DjangaeApplication(get_wsgi_application())
ローカルにてIt Works!
の確認
この時点で起動すると、DjangoのIt Works!
が表示されるはずです。
そこで、runserver後にhttp://localhost:8000
へとアクセスしたところ、ブラウザにエラーが表示されました。
また、コマンドプロンプトには
(env) d:\Sandbox\djangae-thinkami-sample>python manage.py runserver ... ImportError: No module named django.core.wsgi
という、Djangoを認識してなさそうなエラーがありました。
そのため、GAEの公式ドキュメントなどを参考に、以下の内容でappengine_config.py
を作成します。
- Libraries in Python 2.7 - Python — Google Cloud Platform
- How to include third party Python libraries in Google App Engine? - Stack Overflow
- Python Module Configuration - Python — Google Cloud Platform
from google.appengine.ext import vendor vendor.add('libs')
なお、上記でも動作しない場合、環境変数PYTHONPATH
も設定します。
- pip -t: A simple and transparent alternative to virtualenv
- Install a Python package into a different directory using pip? - Stack Overflow
(env) d:\Sandbox\djangae-thinkami-sample>set PYTHONPATH=.\libs
再度、runserver後にhttp://localhost:8000
へとアクセスすると、別のエラーが表示されました。
(env) d:\Sandbox\djangae-thinkami-sample>python manage.py runserver ... ImportError: No module named msvcrt
そのため、stackoverflowを参考にappengine_config.py
へ追記します。
python - Django 1.7 on App Engine "ImportError: No module named msvcrt" - Stack Overflow
import os on_appengine = os.environ.get('SERVER_SOFTWARE','').startswith('Development') if on_appengine and os.name == 'nt': os.name = None
もう一度runserver後にhttp://localhost:8000
へとアクセスすると
It worked! Congratulations on your first Django-powered page.
が表示されました。
GAE上にてIt Works!
の確認
GAE上でも問題なく動作するかを確認します。
まずはGoogle Cloud Platform Consoleへアクセスし、GAEのアプリを作成します。
続いて、appcfg.py
を使って、ソースコードをGAEへデプロイします。
- Installation & Deployment - Djangae Documentation
- Uploading, Downloading, and Managing a Python App - Python — Google Cloud Platform
(env) d:\Sandbox\djangae-thinkami-sample>python "C:\Program Files (x86)\Google\google_appengine\appcfg.py" update ./ ... 10:38 PM Completed update of app: djangaethinkamisample, version: 20151228
なお、初回の場合、途中で認証を求められることがありますが、許可
することで
- ブラウザに
The authentication flow has completed.
が表示 - コマンドプロンプトでは、中断していたデプロイ処理が再開・続行
となります。
デプロイ完了後、http://<your_app_name>.appspot.com
へとアクセスすると、
Error: Server Error The server encountered an error and could not complete your request. ...
というエラーが表示されました。
Applications Overviewにてログを見ると、
ImproperlyConfigured: Error loading either pysqlite2 or sqlite3 modules (tried in that order): No module named _sqlite3
というエラーがありました。データベース設定を修正していないため、GAEでSQLiteを使おうとしてエラーになったようです。
そのため、以下のDjangaeの公式ドキュメントを参考にして、DATABASES
の設定を修正します。
The Database Backend - Djangae Documentation
# DATABASES = { # 'default': { # 'ENGINE': 'django.db.backends.sqlite3', # 'NAME': os.path.join(BASE_DIR, 'db.sqlite3'), # } # } from djangae.utils import on_production DATABASES = { 'default': { 'ENGINE': 'djangae.db.backends.appengine' } } if on_production(): pass else: DATABASES['sql'] = { 'ENGINE': 'django.db.backends.sqlite3', 'NAME': 'development.sqlite3' }
修正後、再度アップロードしてブラウザでアクセスすると、
It worked! Congratulations on your first Django-powered page.
が表示され、GAEでDjangoの動作確認がとれました。
ここまでのDjangoアプリの全体は以下となります。
thinkAmi-sandbox/Django_Djangae_sample at 0fd064f0c1ac9ead769e70b87ee031f1a3606ed6
Djangoアプリの機能を追加
実装内容
次に、Djangoアプリで以下が実現できるような機能を追加します。
http://<your_app_name>.appspot.com/mysite/register
へアクセス- フォームへ入力し、
Save
ボタンを押す - フォーム内容をModel(= GAEのデータストア)に保存
http://<your_app_name>.appspot.com/mysite/<保存した際のID>
へリダイレクトし、Modelの内容を更新可能な状態で表示
そのため、以下を実装します(実装の詳細は省略)。
- myproject\urls.py
urlpatterns
にmysite
アプリ用のエントリを追加
- mysite\urls.py
urlpatterns
に必要なエントリを追加
- mysite\models.py
- ForeignKeyを試したいため、ForeignKeyを持つ
Impression
と、その参照先のCultivar
を用意
- ForeignKeyを試したいため、ForeignKeyを持つ
- mysite\forms.py
Impression
モデルを使ったModelFormのDetailForm
を用意
- mysite\widgets.py
- フォームで
<input type=date>
を使うため、ModelFormで使うWidgetsを用意
- フォームで
- mysite\views.py
- Class-based Viewとして、登録用の
ImpressionRegisterView
と更新用のImpressionUpdateView
を用意
- Class-based Viewとして、登録用の
- mysite\templates.html
- viewで使われるテンプレートを用意
- fixtures\cultivar.yaml
- ForeignKeyの参照先向けのfixtureを用意
- app.yaml
また、フォームの内容は以下の通りです。
ローカルでの確認
動作確認を行うため、マイグレーションなどを行います。
# マイグレーションファイルの作成 (env) d:\Sandbox\djangae-thinkami-sample>python manage.py makemigrations # マイグレーション (env) d:\Sandbox\djangae-thinkami-sample>python manage.py migrate # Cultivarモデルにfixtureで初期データを投入 (env) d:\Sandbox\djangae-thinkami-sample>python manage.py loaddata cultivar ... Installed 3 object(s) from 1 fixture(s)
その後にrunserverを行い、http://localhost:8000/mysite/register
にアクセスして表示・保存ができることを確認します。
GAEでの確認
GAEにアップロード後、Djangaeのドキュメントに従いマイグレーションを行います。
Local/remote management commands - Djangae Documentation
# アップロード (env) d:\Sandbox\djangae-thinkami-sample>python "C:\Program Files (x86)\Google\google_appengine\appcfg.py" update ./ # マイグレーション (env) d:\Sandbox\djangae-thinkami-sample>python manage.py --sandbox=remote migrate ... urllib2.HTTPError: HTTP Error 404: Not Found Unexpected HTTP status 404
エラーメッセージを調べてみたところ、Remote API for Pythonまわりの問題でした。
- Remote API for Python - Python — Google Cloud Platform
- python - Google App Engine remote_api returns 404 using remote_api_shell.py - Stack Overflow
そこで、app.yaml
にRemote API for Pythonを使う設定を追加します。
builtins: - remote_api: on
再度マイグレーションを行うと、
(env) d:\Sandbox\djangae-thinkami-sample>python manage.py --sandbox=remote migrate Go to the following link in your browser: https://accounts.google.com/o/oauth2/auth?... Enter verification code:
のように、コマンドプロンプトにURLが表示されます。
ブラウザを起動してそのURLをアドレスに入力すると、認証が求められます。
許可
するとブラウザに
このコードをコピーし、アプリケーションに切り替えて貼り付けてください: <your code>
が表示されます。
<your code>
をコマンドプロンプトに貼り付けて実行すると、マイグレーションが行われました。
... Authentication successful. System check identified some issues: ... Running migrations: ... Applying mysite.0001_initial... OK Applying sessions.0001_initial... OK
続いてloaddataを行います。
(env) d:\Sandbox\djangae-thinkami-sample>python manage.py --sandbox=remote loaddata cultivar ... Installed 3 object(s) from 1 fixture(s)
http://<your_app_name>.appspot.com/mysite/register
へアクセスして保存し、動作を確認します。
また、GAEのデータストアにも登録されていました。
以上で、Google App EngineでDjango + Djangaeが動作することを確認できました。
ソースコード
GitHubに上げました。
thinkAmi-sandbox/Django_Djangae_sample
その他参考
GAEのサンプルについて
Google Cloud Platform - GitHubにてDjangoを検索すると、サンプルがいくつかありました。
GoogleCloudPlatform/appengine-django-skeletonはSDKではなく、pip -t
のものを使っていました。
インスタンス時間について
以下が参考になりました。
株式会社ジェニシス 技術開発事業部ブログ: 【GAE】インスタンス時間1