Saturday, January 10, 2015

App Engine and Django Notes, Round 2

Project: Get Django & Google App Engine to play nice.

Date: 10-Jan-2015

Status: A good start

I've successfully deployed python App Engine code before, and I've used Flask and Django before. However, not in a repeatable way. This time I'm going to start from scratch, without any meta-frameworks to help (until and unless they're needed).

Requirements:

  • Repeatable
  • An ORM that works (I don't want to embed SQL statements in my code now anymore than I did before I started using django in 2007).
  • A current (supported) version of django. App Engine supports 1.5, but that version is insecure.
  • Debuggable (in PyDev)
  • Free (to start with, anyway)

If you're following along, you'll need python 2.7, virtualenv, pip, and the Google App Engine SDK.

Configuration, configuration

This question on stackoverflow seems very relevant. With that as inspiration I'll give this a shot.

Project Skeleton

mkdir -p ~/root/projects/<app-id>/application
cd ~/root/projects/<app-id>/application
touch app.yaml
mkdir lib
pip install -t lib "django<1.8"

Make app.yaml use the django that I installed:

application: <app-id>
version: 1
runtime: python27
api_version: 1
threadsafe: true

libraries: 
- webapp2
  version: 2.5.2

appengine_config.py

Also, we'll need to create an appengine_config.py file, to let App Engine know that we have libraries in the lib folder.

import sys
sys.path.append('lib')

This file is probably important for other things, but that's good enough for now.

Virtual environment

Create a virtual environment. This is a little touchy, since Google App Engine does it's own environment configuration. However, in order to use django, we need a virtual environment with the right version of django in it. Also, we don't want to have to install packages in more than one directory.

cd ~/root/development/env
python -m virtualenv <app-id>
. <app-id>/bin/activate
cd <app-id>lib/python2.7/site-packages

Now, use a .pth file to tell python that we want to add the application's lib directory to our site-packages:

echo $HOME/root/projects/<app-id>/application/lib > aelib.pth

And to confirm that it works:

(<app-id>)machine:application$ python
>>> import django
>>> django.__file__
'/Users/<you>/root/projects/<app-id>/application/lib/django/__init__.pyc'

Good enough. Now anything that is pip installed into the app engine's lib directory will show up in the virtual environment.

Start up a django project

Django was installed with -t, which means that pip didn't create symlinks to the django command line tools into our virtualenv's bin directory (and the directory didn't exist when django was installed). All this means that we have to use the path to the installed django package's bin directory. Fortunately, you only have to create a project once.

(<app-id>)machine:application$ ./lib/django/bin/django-admin.py start-project <app-id>

Much better. Now, our folder structure looks like this:

~/root/projects/<app-id>/application/
    app.yaml
    appengine_config.py
    lib/
    <app-id>/
        manage.py
        <app-id>/
            __init__.py
            settings.py
            urls.py
            wsgi.py

We ned a small modification in order to get things going, which is to move the django project up a level. Move manage.py, and <app-id>/<app-id> up one, and now it will look like this:

~/root/projects/<app-id>/application/
    app.yaml
    appengine_config.py
    lib/
    manage.py
    <app-id>/
        __init__.py
        settings.py
        urls.py
        wsgi.py

This would be unecessary if we had started a project called application, but that would have been difficult given that we needed application/lib directory first to hold django.

Structure

The directory structure is important, since it influences how you work with the project day to day. A good structure makes it so you don't have to guess about who's getting what resource from where. This one is pretty close to a standard Django layout, and looks like it will work for app engine.

Handler for app.yaml

Now, add a handler to app.yaml and we'll see if we can make something work.

handlers:
- url: .*
  script: <app-id>.wsgi.application

One additional step, comment out the DATABASES entry in settings.py. We'll set up a data store a bit later.

Running

Now, run dev_appserver.py app.yaml.

Good, this gives me the App Engine console on http://localhost:8000, and django's "It Worked!" page on http://localhost:8080.

And the final test, upload this to App Engine and see if it works there too...

(<app-id>)machine:application appcfg.py update .

Poof, now <app-id>.appspot.com is showing me the "It Works!" page. This is a good start.

We should be all set for now.

Results

All in all, I'd say this is a pretty good start. At the very least, it's way more successful than my previous attempt, in which I followed someone else's instructions and failed miserably.

We have:

  • A Django 1.7 application
  • It's running in App Engine
  • We're not paying for anything (yet)

However, we don't have:

  • PyDev configuration. PyDev has support for App Engine, so there's hope there.
  • An ORM. There's a limit to how much you can do without data storage.

Friday, January 2, 2015

App Engine Notes

Project: Get Django 1.5 & Google App Engine to play nice.

Date: 2-Jan-2015

Goal: Create a reasonable development environment with the most current version of Django to run on Google App Engine.

Status: Failed

Setup

  1. Install Google Cloud SDK
  2. Create app engine project on the google developer console

Django

App engine seems to only support django 1.5 right now, which is unfortunate.
What is more fortunate, is that they provide a starting template:

$ git clone https://github.com/GoogleCloudPlatform/appengine-django-skeleton.git

Put all that into a ~/root/projects/<project-id>. Remove the .git directory, it will just get in the way.

Create a virtual environment

cd ~/root/development/env
python2.7 -m virtualenv <project-id>
. <project-id>/bin/activate

Set up Pydev

Open Eclipse, start a new pydev App Engine project from existing sources, pointed at ~/root/projects/<project-id>. In eclipse’s preferences, add a new python interpreter at ~/root/development/env/<project-id>/bin/python, and set it as the default for the project.

Configure the app.yaml

GAE uses the app.yaml file for project configuration. The skeleton provides one, edit it to match your & whatnot.

Set up python

Getting everything into the right places in the python path seems to be a bit harder than necessary.

Follow the instructions in the appengine-django-skeleton readme (run build.sh). First, make sure you deactivate your virtual environment ($ deactivate). build.sh will install various dependencies in the current directory.

I’m not sure what modifications have been made to django, but manage.py runserver is supposed to use the dev_appserver.py to run. This doesn’t seem to work out of the box.

Path Hacks

The GoogleAppEngineLauncher program installed symlinks to appengine binaries into /usr/local, but didn’t seem to do anything with the PYTHONPATH.

I’m not sure that it would it necessarily know how to change the PYTHONPATH, since it doesn’t know what interpreter I’m using. On the other hand, it’s also rude to modify my system python, but the documentation also seems to think that installing is all you need to do. Maybe it has something to do with me not putting GoogleAppEngineLauncher.app into /Applications, but I never put anything in there since permissions are becoming weird in that folder (it seems to belong to Apple and their App store now, not to me).

So, to help it out, manually create symlinks in the site-packages directory so python can find what it needs. (I would prefer .egg-link files, but these are just modules, not eggs so that won’t work).

cd ~/root/development/env/<project-id>/lib/python2.7/site-packages
ln -s /Users/seth/root/development/sdk/GoogleAppEngineLauncher.app/Contents/Resources/GoogleAppEngine-default.bundle/Contents/Resources/google_appengine/google
ln -s /Users/seth/root/development/sdk/GoogleAppEngineLauncher.app/Contents/Resources/GoogleAppEngine-default.bundle/Contents/Resources/google_appengine/[^_]*.py .

Now that I’m looking at the files in that directory, it seems that there’s a file called _python_runtime.py that seems to fix up the path. But it also adds a ton of other stuff (everything in GoogleAppEngine-default.bundle/.../lib) to the path too. It might make sense to revisit that, maybe import it in sitecustomize.py or something.


Getting it running

The django app engine skeleton seems to be out of sync with the app engine SDK :(

Supposedly, running python manage.py runserver should work, but in my case, it says:

Traceback (most recent call last):
    ...
Exception: Could not get appid. Is your app.yaml file missing? Error was: cannot import name dev_appserver

The standard way of running a GAE app locally does seem to work though:

dev_appserver.py app.yaml

After that, hit http://localhost:8080, and you’ll see the welcome page.

Results

Hmm, it also seems that this gives you django 1.4, not 1.5 as I thought it would. Now that I’ve realized this, I see that the last commit on this project is about a year ago, November 15, 2013.

Also, the documentation and current SDKs don’t match anymore. This makes me think that I’m going to continue to run into problems with GAE and djangoappengine. Later today or tomorrow I’ll try a different approach, this doesn’t work.

On the plus side, I did figure out a nice way to publish Markdown documents on blogger (via stackedit.io).