== Painless Python for Proficient Programmers
- the interactive shell displays non-None expression results
- the print statement does elementary I/O to standard output
use comma to separate the values you want to emit
print uses space separators and newline at the end
- In real programs, you'll use very different tools:
the logging package for logging purposes
add strings to a HTML response object
GUI widgets and their methods
== Some trivial examples
print 'Hello, world!' # classic !-)
x = 23 # name x now means 23
print x # emits 23
x = 'foo' # now x means 'foo' instead
print x # emits foo
del x # name x reverts to undefined
print x # raises an error ("exception")
y = None # name y gets bound ot None
print y # emits None
== Similarities with Java
- Both typically compiled -- bytecode (python got a make engine built in )
But: Python compiles implicitly ( like a built-in make ! )
- "Everygthing" inherits from object
But: in Python, also numbers, functions, classes, ...
"Everything is a first-class object", no " boxing/unboxing"
- uniform object-reference semantics
assignment, argument-passing, return
- garbage collection ( but: CPython also uses RC...)
- Key "marginalia": vast standard library ( + thind party packages & tools ) , introspection, serialization, threads
Examples:
In c when you say a = b , then lot of bits copying happens
Whereas in java and python, b refers exactly the same object as a refered to
java/python both got garbage collector
== Similarities with C++
- Multi-paradigm (not just "forced" OOP )
OOP, procedural, generic, a little FP
- Multiple inheritance
- Operator overloading
but: not for simple assignment ( not an operator )
- "Signature-based" polymorphism
As if "everything was a template" ( but: nicer syntax )
- abundant choices for most all "side issues"
- GUI, Web & other networking , DB , IPc, distributed processing...
Examples
In C++ we can overload assignment operators whereas in python we cannot overload assignment operators
python exactly have the same java symentics of assignment operators
Assignment is a object reference in python. Assignment is not an operator in python
In python everything we write is a template implicitly
== Deep connection with C
"Spirit of C" @87% ( More than java or C++ despite surface syntax...), according to ISO C standard "Rationale".
1. trust the programer
2. don't stop the programmer from doing what need to be done
3. keep the lanugage small and simple
4. provide only one way to perform an operation
5. (be fast, even if not guaranteed to be portable)
( this is the one point out of five that's not at 100% in python]
== The Zen of Python
import this
Beautiful is better than ugly.
Explicit is better than implicit
Simple is better than complex
Complex is better than complicated
Flat is betten than nested
Sparse is better than dense
Readability counts
...
In the face of ambiguity, refuse the temptation to guess.
There should be one, preferably only one, abvious way to do it.
== Python vs Java/C++/C: Syntax
- spare syntax, no ornamentation
- "no wasted pixels" ( Tufle )
- no {} around blocks, just indent
- no {} around conditions in if and while
- generally little punctuation
Every Pixel is important
Don't put a pixel there, unless it communicates a pixel there...
(), {} - wasting a pixel
== Python vs Java/C++/C: semantics
-types: strong but dynamic
- names have no types ( objects have types )
- " duck typing" ( if it walks like a duck , &c )
- No "declarations", just statements
- "everything" is a first-class object
- classes, functions, methods, modules, packages...
- The focus is no high and very high levels
though lower levels are decently supported too
Everything is a object
module, package these are objects
== Duck Typing
Takes some work to teach the ducks to type, but the returns in productivity are quite worthwhile:-)
==Flow Control
- if:
- O+ elif:
- optionally: else:
- while:
- block can contain break, continue
- optionally: else:
- executes if no break terminated the loop
- for in :
- break, continue, else: like in while
- with as :
- in 2.5, requires: from __future__ import with_statement
== Built-in simple types ( all immutable )
- numbers: int, long, float, complex
- 23 434343434343 ox17 2.3 4.5+6.7j
- operators: + - * ** / // % ~ & | ^ << >>
- built-in functions: abs min max pow round sum
- Strings: plain and Unicode
- 'single' "double" r`aw` u"nicode" \n&\c
- operators: + (cat), * (rep), %(format)
- rich "format language" (akin to C's printf)
- Built-in functions: chr ord unichr
- are immutable sequences: len, [] (index/slice), for
- each item is a "character" ( actually: a length-1 sting )
- tons and tons of methods: capitalize, center, ...
== Discovering methods
dir('anystring')
........
........
........
........
........
You will be getting huge numbers of methods, attributes of that object
== > Getting help about a method
>>> help(''.center)
Help on builtin function center:
center(...)
S.center(width[, fillchar]) -> string
Returns S centered in a string of length withd. Padding is done using the specified fill character ( default is a space )
(END)
>>>
==> ... and then, experiment
>>> 'ciao'.center(10, '+')
'+++ciao+++'
>>> 'ciao'.center(3, '+')
'ciao'
>>> 'ciao'.center(9, '+' )
'+++ciao++'
Note: learn by doing....
== > Built-in container types
- tuple: immutable sequence
- () (23,) (23, 45) tuple('ciao')
- list: mutable sequence ( a "vector")
- [] [23] [23, 45] list('ciao')
- set, frozenzet: simple hashtables
-set () set((23,)) set('ciao')
- dict: map key -> value by hashtable
- {} {2:3} {4:5, 6:7} dict(ci='oo')
- all containers support: len(c), looping (for x in c), membership testing ( if x in c )
- and , generally , many methods ( sets also support operators )
==> Sequences
- stings, tuples and lists are sequences (other squence types are in the standard library )
- repetition ( c*N), catenation ( C1 + C2 )
- indexing: c[i], slicing: c[i:j] and c[i:j:k]:
- 'ciao'[2] =='a', 'ciao'[3:1:-1] =='oa'
- always: start included, end excluded ( per koenig...)
- lists are mutable sequences ( can assign to item or slice )
- assigning to a slice can change the length
- dicts and sets are not sequences ( though they are iterables )
- ordering "arbitrary" ( as befits hash tables), no slicing
== > More about Containers
- mutating vs non-mutating methods
- most mutating methods return None
- e.g. alist.append(...), aset.add(...), adict.update(...)
- a few mutating methods return non-None:
-alist.pop(...), adict.pop(...), adict.setdefault(...)
- set has an immutable variant (frozenset)
- dict has a variant defaultdict ( in module collections )
- also iin collections: deque( sequence with added methods... left, clear, rotate; no slicing, no sort )
- no heap type, but rather heap functions on lists(module heapq)
==> Example: making a heap ( priority-queue type )
import heapq
class heap(object):
def __inti__(self, content=()): // Initializer
self._L = list (content)
heapq.heapify(self._L)
def push(self, item): heapq.heappush(self._L, item)
def pop(self): return heapq.heappop(self._L)
def replace(self, item):
return heapq.heapreplace(self._L, item)
def __len__(self): return len(self._L)
def __iter__(self): return iter(self._L)
def __repr__(self): return 'heap(%r)' % self._L
== > Comparisons, tests, truth ( or, rather, "truthiness")
- equality, identity: == !=, is , is not
- order: < > <= >=
- membership: in, not in
- "chaining": 3 <= x < 9
- falsy: numbers == 0, "", None, empty containers, False ( aka bool(0))
- truthy: anything else, True ( aka bool(1))
- not x == not bool(x) for any x
- and, or "short-circuit" (-> return an operand)
- same for built-ins any, all (-> return a bool )
==> Exceptions
- Errors( and other "anomalies" )"raise exceptions" (instances of Exception or any subtype of Exception )
- Statement raise explicitly raises an exception
- Exceptions propagate " along the call stack", terminating functions along the way, until they're "caught"
- If uncaught, an exception terminates the program
- Statement try/except can catch exceptions (also: try/finally, and elegant with to implement "RAAI" [ in 2.5, needs from __future__ import with_statement at start of module )]
==> Some RAAI examples
from __future__ import with_statement
with open('afile.txt') as f:
for aline in f: print aline[3],
with some_lock do: a_critical_section()
import contextlib, mysqldb
with contextlib.closing(mysqldb.connect()) as db:
with contextlib.closing(db.cursor()) as c:
c.execute("SELECT * FROM t WHERE t.zap< 23")
for record in c.fetchall(): ...
==> iterators and for loops
for i in c:
===>
_t = iter(c)
while True:
try: i = _t.next()
except StopIteration: break
also:
( for i in c ) ("genexp")
[ for in in c ("list comprehension")
==> Some examples of genexp use (w/" accumulators")
def countall(it): return sum ( 1 for x in it )
print countall(x for x in range(99) if x%5==x%7) #15
if any(x>5 for x in xs): ...
if all(x>y>z for x, y, z in zip(xs, ys, zs)): ...
# or, better(since zip makes a list...):
from itertools import izip
if all(x>y>z for x, y, z in izip(xs, ys, zs)): ...
print max(i for i, x in in enumerate(xs) if x>0)
And... We're halfway through!
=== Rapid Development with Python Django and Google App Engine
Talk Overview
- This is not a plug for Python, Django or Google App Engine
- Except implicitly :)
- Best practices for using Django with App Engine
- project setup
- The "Google App Engine Django Helper" module
- Using App Engine's db.Model instead of Django's Model Class
- Writing unit tests
python is a Great Language
Django is a Great Web FrameWork
Google App Engine - Is a great Application Development Platform
What i am trying to tell here is , to use some of the best practices of using Django with Appengine
Today i am going to bore you to Depth Briefly...
i hope there will be plenty of time for Q&A
=== Google App Engine Recap
- Does one thing well: run web apps
- Simple Configuration
- Request handlers
- Accelerators and shortcuts
=== App Engine Does One Thing Well
- App Engine handlers HTTP requests, nothing else
- Think RPC: request in , processing, response out
- Works well for the web and AJAX; also for other services
- Resources are scaled automatically
- Requests may go to the same process, serially
- Requests may go to different processes, in parallel or serially
- Highly scalable datastore based on Bigtable (Google internal database )
- Not SQL: no joins
Ajax webapplication
=== Configuring an App Engine Application
- An application is a directory with everything underneath it
symbolic links are followed
- Single file app.yami in app root directory
Defines application metadata
Maps URL patterns ( regular expressions) to request handlers
Separates static files from program files
- Dev server (SDK) emulates deployment environment
They have special API's
==== Request Handlers
- URL patterns are mapped to request handlers by app.yami
yami = is a markup language similar to python
- Handler is invoked like a CGI script
- Environment variables give request parameters
E.g. PATH_INFO, QUERY_STRING, HTTP_REFERER
- Write response to stdout
Status (optional), headers, blank line, body
==== Request Handler Acclerators and Shortcuts
- CGI doesn't mean slow!
- Define a main() function
Module will remain loaded, main() called for each requests
Can use module globals for caching
Must use "if __name__##'__main__':main()" boilerplate
- CGI doesn't mean clumsy !
- WSGI support layered on top of CGI: util.run_wsgi_app(app)
Web services Gateway Interface, little much resemblance of CGI(Common Gateway Interface )
Interface to the web server
==== Django Project Setup for App Engine
- Django is a bit finicky about project structure
- code in one or more 'app' (application) subdirectories
- cannot have the same name as the project
- Must have a single settings.py file
- found via DJANGO_SETTINGS_MODULE environment variable
- App Engine Vs Django
- No SQL: must use App Engine's own database
- Top-level directoy appended at end of sys.path
- Django 0.96.1 preloaded, which is pretty old
- Slight differences between dev_appserver and real deployment
=== Boilerplate Files, Standard Project Lay-out
- Boilerplate files
- These hardly vary between projects
- app.yami: direct all non-static requests to main.py
- main.py: initialize Django and send it all request
- settings.py: change only a few settings from defaults
- Project lay-out
- static/*: static files: serverd directly by App Engine
- myapp/*.py: app-specific python code
urls.py, views.py, models.py, tests.py and more
- templates/*.html: templates ( or myapp/templates/*.html)
=== Minimal app.yaml
application: myapp # .appspot.com
version: 1
runtime: python
api_version: 1
handlers:
- url: /static
static_dir: static
- url: .*
script: main.py
=== The Most Minimal main.py
import os
from google.appengine.ext.webapp import util
os.environ['DJANGO_SETTINGS_MODULE'] = 'settings'
from django.core.handlers import wsgi
def main():
app = msgi.WSGIHandler()
util.run_wsgi_app(app)
if __name__ == '__main__':
main()
=== BARE Minimal Settings.py
import os
DEBUG = os.environment['SERVER_SOFTWARE'].startswith('Dev')
INSTALLED_APPS( 'myapp', )
MIDDLEWARE_CLASSES = ()
ROOT_URLCONT = 'myapp.urls'
TEMPLATE_CONTEXT_PROCESSORTS = ()
TEMPLATE_DIRS = ( os.path.join(os.path.dirname(__file__), 'template'),)
TEMPLATE_LOADERS = ( 'django.template.loaders.filesystem.load.template.source',)
T2 = 'UTC'
AppEngine application always runs in UTC
=== Google App Engine Django Helper
- Separate Open source project
By two Googlers, Matt Brown and Andy smith
http://code.google.com/p/google-app-engine-django
- Takes care of monkey-patching Django
Loads newer version of Django if available in app root dir
- Enables using standard Django project management tool
For unit tests and fixture loading ( development only )
- Comes with boilerplate app.yaml, main.py, settings.py
=== Getting Started
- Download and unzip appengine_helper_for_django_rN.zip
- Rename directory to 'mysite' or whatever you like
- This becomes your project root; contents;
COPYING, KNOWN_ISSUES, Makefile, README
- __init__.py (empty)
- app.yaml (edit application: only )
- appengine_django/... (helper code lives here )
- main.py (generic bootstrap )
- manage.py (Django management script )
- settings.py ( edit settings here )
- urls.py ( edit URL mappings here )
=== Changing the Standard Set-up
- Edit app.yaml to set your application id ( can do this later )
- Create subdirectory myapp
- Edit settings.py, adding 'myapp' to INSTALLED_APPS
- In myapp, create:
- __init__.py (empty)
- views.py (add your view code here )
- models.py (add your model definitions here )
- tests.py ( add your unit tests here )
To use Django HEAD, copy the django packages here
/usr/local/google_appengine must be your App Engine SDK
=== Your First View
- Edit urls.py:
from django.conf.urls.defaults import *
urlpatterns = patterns('myapp.views',(r'^$', 'index'),
# ...more here later...
)
- Edit myaap/views.py:
from django.httpo import HttpResponse
def index(request):
return HttpResponse('Hello World')
- Run it: ./manage.py runserver
- Point your browser to: http://localhost:8080
=== Using Models
- Edit models.py:
from google.appengine.ext import db
from appengine_django.models import BaseModel
class Shout(BaseModel): # subclass of db.Model
title = db.StringProperty(required=True)
test = db.TextProperty()
mtime = db.DateTimeProperty(auto_now_add=True)
user = db.UserProperty()
- Up next:
- write views to use models
- Use forms derived from your models with templates
=== Using Forms
- I tend to put forms in views.py; do as you please
from django.shortcuts import render_to_response
from google.appengine.ext.db import djangoforms
import models
class ShoutForms(djangoforms.ModelForm):
class Meta:
model = models.Shout
exclude = ['mtime'm, 'user']
query = models.Shout.gql("ORDER BY mtime DESC")
def index(request):
return render_to_response('index.html', { 'shouts': query.run(),'form': ShoutForm()})
=== Using Templates
- Put this in templates/index.html
{% for shout in shouts %}
{{ shout.user }} ; {{ shout.title }}
{{ shout.text }}
{{ shout.mtime | timesince }}
{ % endfor %}
{{ form }}
=== The post() Method
- Add to urls.py:
(r'^post/$', 'post');
- Add to views.py:
from google.appengine.api import users
from django.http import HttpResponseRedirect
def post(request):
form = ShoutForm(request.POST)
if not form.is_valid():
return render_to_response('index.html', {'form':form})
shout = form.save(commit=False)
shout.user = users.get_current_user()
shout.put()
return HttpResponseRedirect('/')
=== Writing Tests
- Python has two test frameworks:
- doctest.py
- Tests are interactive sessions captured in doc stings
- Really easy to get started
- Some caveats (output must be reproducible)
- unitest.py
- Tests are methods in classes derived from unittest TestCase
- Modeled after JUnit
=== Running Tests
- Use whichever test framework you like
./manage.py test myapp
- Runs all tests found in myapp
- Specifically:
- looks for doctest and unittest tests
- looks in models.py and tests.py
=== Example Doc Tests
- Put this in myapp/tests.py
r"""
from django.test.client import Client
from models import *
c = Client()
r = c.get('/')
r.status_code
200
r.content
'\n\n
- the interactive shell displays non-None expression results
- the print statement does elementary I/O to standard output
use comma to separate the values you want to emit
print uses space separators and newline at the end
- In real programs, you'll use very different tools:
the logging package for logging purposes
add strings to a HTML response object
GUI widgets and their methods
== Some trivial examples
print 'Hello, world!' # classic !-)
x = 23 # name x now means 23
print x # emits 23
x = 'foo' # now x means 'foo' instead
print x # emits foo
del x # name x reverts to undefined
print x # raises an error ("exception")
y = None # name y gets bound ot None
print y # emits None
== Similarities with Java
- Both typically compiled -- bytecode (python got a make engine built in )
But: Python compiles implicitly ( like a built-in make ! )
- "Everygthing" inherits from object
But: in Python, also numbers, functions, classes, ...
"Everything is a first-class object", no " boxing/unboxing"
- uniform object-reference semantics
assignment, argument-passing, return
- garbage collection ( but: CPython also uses RC...)
- Key "marginalia": vast standard library ( + thind party packages & tools ) , introspection, serialization, threads
Examples:
In c when you say a = b , then lot of bits copying happens
Whereas in java and python, b refers exactly the same object as a refered to
java/python both got garbage collector
== Similarities with C++
- Multi-paradigm (not just "forced" OOP )
OOP, procedural, generic, a little FP
- Multiple inheritance
- Operator overloading
but: not for simple assignment ( not an operator )
- "Signature-based" polymorphism
As if "everything was a template" ( but: nicer syntax )
- abundant choices for most all "side issues"
- GUI, Web & other networking , DB , IPc, distributed processing...
Examples
In C++ we can overload assignment operators whereas in python we cannot overload assignment operators
python exactly have the same java symentics of assignment operators
Assignment is a object reference in python. Assignment is not an operator in python
In python everything we write is a template implicitly
== Deep connection with C
"Spirit of C" @87% ( More than java or C++ despite surface syntax...), according to ISO C standard "Rationale".
1. trust the programer
2. don't stop the programmer from doing what need to be done
3. keep the lanugage small and simple
4. provide only one way to perform an operation
5. (be fast, even if not guaranteed to be portable)
( this is the one point out of five that's not at 100% in python]
== The Zen of Python
import this
Beautiful is better than ugly.
Explicit is better than implicit
Simple is better than complex
Complex is better than complicated
Flat is betten than nested
Sparse is better than dense
Readability counts
...
In the face of ambiguity, refuse the temptation to guess.
There should be one, preferably only one, abvious way to do it.
== Python vs Java/C++/C: Syntax
- spare syntax, no ornamentation
- "no wasted pixels" ( Tufle )
- no {} around blocks, just indent
- no {} around conditions in if and while
- generally little punctuation
Every Pixel is important
Don't put a pixel there, unless it communicates a pixel there...
(), {} - wasting a pixel
== Python vs Java/C++/C: semantics
-types: strong but dynamic
- names have no types ( objects have types )
- " duck typing" ( if it walks like a duck , &c )
- No "declarations", just statements
- "everything" is a first-class object
- classes, functions, methods, modules, packages...
- The focus is no high and very high levels
though lower levels are decently supported too
Everything is a object
module, package these are objects
== Duck Typing
Takes some work to teach the ducks to type, but the returns in productivity are quite worthwhile:-)
==Flow Control
- if
- O+ elif
- optionally: else:
- while
- block can contain break, continue
- optionally: else:
- executes if no break terminated the loop
- for
- break, continue, else: like in while
- with
- in 2.5, requires: from __future__ import with_statement
== Built-in simple types ( all immutable )
- numbers: int, long, float, complex
- 23 434343434343 ox17 2.3 4.5+6.7j
- operators: + - * ** / // % ~ & | ^ << >>
- built-in functions: abs min max pow round sum
- Strings: plain and Unicode
- 'single' "double" r`aw` u"nicode" \n&\c
- operators: + (cat), * (rep), %(format)
- rich "format language" (akin to C's printf)
- Built-in functions: chr ord unichr
- are immutable sequences: len, [] (index/slice), for
- each item is a "character" ( actually: a length-1 sting )
- tons and tons of methods: capitalize, center, ...
== Discovering methods
dir('anystring')
........
........
........
........
........
You will be getting huge numbers of methods, attributes of that object
>>> help(''.center)
Help on builtin function center:
center(...)
S.center(width[, fillchar]) -> string
Returns S centered in a string of length withd. Padding is done using the specified fill character ( default is a space )
(END)
>>>
==> ... and then, experiment
>>> 'ciao'.center(10, '+')
'+++ciao+++'
>>> 'ciao'.center(3, '+')
'ciao'
>>> 'ciao'.center(9, '+' )
'+++ciao++'
Note: learn by doing....
== > Built-in container types
- tuple: immutable sequence
- () (23,) (23, 45) tuple('ciao')
- list: mutable sequence ( a "vector")
- [] [23] [23, 45] list('ciao')
- set, frozenzet: simple hashtables
-set () set((23,)) set('ciao')
- dict: map key -> value by hashtable
- {} {2:3} {4:5, 6:7} dict(ci='oo')
- all containers support: len(c), looping (for x in c), membership testing ( if x in c )
- and , generally , many methods ( sets also support operators )
==> Sequences
- stings, tuples and lists are sequences (other squence types are in the standard library )
- repetition ( c*N), catenation ( C1 + C2 )
- indexing: c[i], slicing: c[i:j] and c[i:j:k]:
- 'ciao'[2] =='a', 'ciao'[3:1:-1] =='oa'
- always: start included, end excluded ( per koenig...)
- lists are mutable sequences ( can assign to item or slice )
- assigning to a slice can change the length
- dicts and sets are not sequences ( though they are iterables )
- ordering "arbitrary" ( as befits hash tables), no slicing
== > More about Containers
- mutating vs non-mutating methods
- most mutating methods return None
- e.g. alist.append(...), aset.add(...), adict.update(...)
- a few mutating methods return non-None:
-alist.pop(...), adict.pop(...), adict.setdefault(...)
- set has an immutable variant (frozenset)
- dict has a variant defaultdict ( in module collections )
- also iin collections: deque( sequence with added methods... left, clear, rotate; no slicing, no sort )
- no heap type, but rather heap functions on lists(module heapq)
==> Example: making a heap ( priority-queue type )
import heapq
class heap(object):
def __inti__(self, content=()): // Initializer
self._L = list (content)
heapq.heapify(self._L)
def push(self, item): heapq.heappush(self._L, item)
def pop(self): return heapq.heappop(self._L)
def replace(self, item):
return heapq.heapreplace(self._L, item)
def __len__(self): return len(self._L)
def __iter__(self): return iter(self._L)
def __repr__(self): return 'heap(%r)' % self._L
== > Comparisons, tests, truth ( or, rather, "truthiness")
- equality, identity: == !=, is , is not
- order: < > <= >=
- membership: in, not in
- "chaining": 3 <= x < 9
- falsy: numbers == 0, "", None, empty containers, False ( aka bool(0))
- truthy: anything else, True ( aka bool(1))
- not x == not bool(x) for any x
- and, or "short-circuit" (-> return an operand)
- same for built-ins any, all (-> return a bool )
==> Exceptions
- Errors( and other "anomalies" )"raise exceptions" (instances of Exception or any subtype of Exception )
- Statement raise explicitly raises an exception
- Exceptions propagate " along the call stack", terminating functions along the way, until they're "caught"
- If uncaught, an exception terminates the program
- Statement try/except can catch exceptions (also: try/finally, and elegant with to implement "RAAI" [ in 2.5, needs from __future__ import with_statement at start of module )]
==> Some RAAI examples
from __future__ import with_statement
with open('afile.txt') as f:
for aline in f: print aline[3],
with some_lock do: a_critical_section()
import contextlib, mysqldb
with contextlib.closing(mysqldb.connect()) as db:
with contextlib.closing(db.cursor()) as c:
c.execute("SELECT * FROM t WHERE t.zap< 23")
for record in c.fetchall(): ...
==> iterators and for loops
for i in c:
===>
_t = iter(c)
while True:
try: i = _t.next()
except StopIteration: break
also:
(
[
==> Some examples of genexp use (w/" accumulators")
def countall(it): return sum ( 1 for x in it )
print countall(x for x in range(99) if x%5==x%7) #15
if any(x>5 for x in xs): ...
if all(x>y>z for x, y, z in zip(xs, ys, zs)): ...
# or, better(since zip makes a list...):
from itertools import izip
if all(x>y>z for x, y, z in izip(xs, ys, zs)): ...
print max(i for i, x in in enumerate(xs) if x>0)
And... We're halfway through!
=== Rapid Development with Python Django and Google App Engine
Talk Overview
- This is not a plug for Python, Django or Google App Engine
- Except implicitly :)
- Best practices for using Django with App Engine
- project setup
- The "Google App Engine Django Helper" module
- Using App Engine's db.Model instead of Django's Model Class
- Writing unit tests
python is a Great Language
Django is a Great Web FrameWork
Google App Engine - Is a great Application Development Platform
What i am trying to tell here is , to use some of the best practices of using Django with Appengine
Today i am going to bore you to Depth Briefly...
i hope there will be plenty of time for Q&A
=== Google App Engine Recap
- Does one thing well: run web apps
- Simple Configuration
- Request handlers
- Accelerators and shortcuts
=== App Engine Does One Thing Well
- App Engine handlers HTTP requests, nothing else
- Think RPC: request in , processing, response out
- Works well for the web and AJAX; also for other services
- Resources are scaled automatically
- Requests may go to the same process, serially
- Requests may go to different processes, in parallel or serially
- Highly scalable datastore based on Bigtable (Google internal database )
- Not SQL: no joins
Ajax webapplication
=== Configuring an App Engine Application
- An application is a directory with everything underneath it
symbolic links are followed
- Single file app.yami in app root directory
Defines application metadata
Maps URL patterns ( regular expressions) to request handlers
Separates static files from program files
- Dev server (SDK) emulates deployment environment
They have special API's
==== Request Handlers
- URL patterns are mapped to request handlers by app.yami
yami = is a markup language similar to python
- Handler is invoked like a CGI script
- Environment variables give request parameters
E.g. PATH_INFO, QUERY_STRING, HTTP_REFERER
- Write response to stdout
Status (optional), headers, blank line, body
==== Request Handler Acclerators and Shortcuts
- CGI doesn't mean slow!
- Define a main() function
Module will remain loaded, main() called for each requests
Can use module globals for caching
Must use "if __name__##'__main__':main()" boilerplate
- CGI doesn't mean clumsy !
- WSGI support layered on top of CGI: util.run_wsgi_app(app)
Web services Gateway Interface, little much resemblance of CGI(Common Gateway Interface )
Interface to the web server
==== Django Project Setup for App Engine
- Django is a bit finicky about project structure
- code in one or more 'app' (application) subdirectories
- cannot have the same name as the project
- Must have a single settings.py file
- found via DJANGO_SETTINGS_MODULE environment variable
- App Engine Vs Django
- No SQL: must use App Engine's own database
- Top-level directoy appended at end of sys.path
- Django 0.96.1 preloaded, which is pretty old
- Slight differences between dev_appserver and real deployment
=== Boilerplate Files, Standard Project Lay-out
- Boilerplate files
- These hardly vary between projects
- app.yami: direct all non-static requests to main.py
- main.py: initialize Django and send it all request
- settings.py: change only a few settings from defaults
- Project lay-out
- static/*: static files: serverd directly by App Engine
- myapp/*.py: app-specific python code
urls.py, views.py, models.py, tests.py and more
- templates/*.html: templates ( or myapp/templates/*.html)
=== Minimal app.yaml
application: myapp # .appspot.com
version: 1
runtime: python
api_version: 1
handlers:
- url: /static
static_dir: static
- url: .*
script: main.py
=== The Most Minimal main.py
import os
from google.appengine.ext.webapp import util
os.environ['DJANGO_SETTINGS_MODULE'] = 'settings'
from django.core.handlers import wsgi
def main():
app = msgi.WSGIHandler()
util.run_wsgi_app(app)
if __name__ == '__main__':
main()
=== BARE Minimal Settings.py
import os
DEBUG = os.environment['SERVER_SOFTWARE'].startswith('Dev')
INSTALLED_APPS( 'myapp', )
MIDDLEWARE_CLASSES = ()
ROOT_URLCONT = 'myapp.urls'
TEMPLATE_CONTEXT_PROCESSORTS = ()
TEMPLATE_DIRS = ( os.path.join(os.path.dirname(__file__), 'template'),)
TEMPLATE_LOADERS = ( 'django.template.loaders.filesystem.load.template.source',)
T2 = 'UTC'
AppEngine application always runs in UTC
=== Google App Engine Django Helper
- Separate Open source project
By two Googlers, Matt Brown and Andy smith
http://code.google.com/p/google-app-engine-django
- Takes care of monkey-patching Django
Loads newer version of Django if available in app root dir
- Enables using standard Django project management tool
For unit tests and fixture loading ( development only )
- Comes with boilerplate app.yaml, main.py, settings.py
=== Getting Started
- Download and unzip appengine_helper_for_django_rN.zip
- Rename directory to 'mysite' or whatever you like
- This becomes your project root; contents;
COPYING, KNOWN_ISSUES, Makefile, README
- __init__.py (empty)
- app.yaml (edit application: only )
- appengine_django/... (helper code lives here )
- main.py (generic bootstrap )
- manage.py (Django management script )
- settings.py ( edit settings here )
- urls.py ( edit URL mappings here )
=== Changing the Standard Set-up
- Edit app.yaml to set your application id ( can do this later )
- Create subdirectory myapp
- Edit settings.py, adding 'myapp' to INSTALLED_APPS
- In myapp, create:
- __init__.py (empty)
- views.py (add your view code here )
- models.py (add your model definitions here )
- tests.py ( add your unit tests here )
To use Django HEAD, copy the django packages here
/usr/local/google_appengine must be your App Engine SDK
=== Your First View
- Edit urls.py:
from django.conf.urls.defaults import *
urlpatterns = patterns('myapp.views',(r'^$', 'index'),
# ...more here later...
)
- Edit myaap/views.py:
from django.httpo import HttpResponse
def index(request):
return HttpResponse('Hello World')
- Run it: ./manage.py runserver
- Point your browser to: http://localhost:8080
=== Using Models
- Edit models.py:
from google.appengine.ext import db
from appengine_django.models import BaseModel
class Shout(BaseModel): # subclass of db.Model
title = db.StringProperty(required=True)
test = db.TextProperty()
mtime = db.DateTimeProperty(auto_now_add=True)
user = db.UserProperty()
- Up next:
- write views to use models
- Use forms derived from your models with templates
=== Using Forms
- I tend to put forms in views.py; do as you please
from django.shortcuts import render_to_response
from google.appengine.ext.db import djangoforms
import models
class ShoutForms(djangoforms.ModelForm):
class Meta:
model = models.Shout
exclude = ['mtime'm, 'user']
query = models.Shout.gql("ORDER BY mtime DESC")
def index(request):
return render_to_response('index.html', { 'shouts': query.run(),'form': ShoutForm()})
=== Using Templates
- Put this in templates/index.html
{% for shout in shouts %}
{{ shout.user }} ; {{ shout.title }}
{{ shout.text }}
{{ shout.mtime | timesince }}
{ % endfor %}
{{ form }}
=== The post() Method
- Add to urls.py:
(r'^post/$', 'post');
- Add to views.py:
from google.appengine.api import users
from django.http import HttpResponseRedirect
def post(request):
form = ShoutForm(request.POST)
if not form.is_valid():
return render_to_response('index.html', {'form':form})
shout = form.save(commit=False)
shout.user = users.get_current_user()
shout.put()
return HttpResponseRedirect('/')
=== Writing Tests
- Python has two test frameworks:
- doctest.py
- Tests are interactive sessions captured in doc stings
- Really easy to get started
- Some caveats (output must be reproducible)
- unitest.py
- Tests are methods in classes derived from unittest TestCase
- Modeled after JUnit
=== Running Tests
- Use whichever test framework you like
./manage.py test myapp
- Runs all tests found in myapp
- Specifically:
- looks for doctest and unittest tests
- looks in models.py and tests.py
=== Example Doc Tests
- Put this in myapp/tests.py
r"""
from django.test.client import Client
from models import *
c = Client()
r = c.get('/')
r.status_code
200
r.content
'\n\n
No comments:
Post a Comment