For better or worst, google app engine allows us to use whatever project tree structure we want, as long as we connect all urls with appropriate handlers in app.yaml it will all work just fine.
My first gae app had models.py for all my models (Django school, can't help it :) ) and a separate {view_name}.py for each url. I know, it's ugly, but following gae tutorial lead me to this structure and it all worked ok. First sign of having the wrong kind of project structure was when I've written a custom module to enhance ndb and couldn't make dev_appserver recognize it.
After long hours of digging through Internet and a fair amount of frustration, I finally turned to my mentor for an answer and he pointed me to a certain StackOverflow question regarding same problem.
And here is my final project structure:
project/ src/ app1/ lib/ __init__.py mycustommodule.py static/ img/ css/ js/ templates/ tests/ __init__.py mytests.py __init__.py app.yaml cron.yaml lib_path.py index.yaml models.py urls.py views.py
Most of what you see is self-explanetory, I think.
index.yaml is produced by gae when you upload your application to the cloud, do not touch it.
lib_path.py is a simple file adding lib/ to sys.path in such a way that dev_appserver recognizes it:
import os import sys sys.path.append(os.path.join(os.path.dirname(__file__), 'lib'))
Before this, I had linked my custom module in site-packages, which allowed me to import my modul in the consol, test and use it just fine, but when I ran dev_appserver it crashed on import error. I went as far as creating a setup.py and doing this didn't help much, and it also didn't feel right.
Now, to use my custom module, I can import it like this anywhere in my application:
from lib import mycustommodule
and woot! it works. How nice and simple indeed.
Lets take a look at urls.py:
import lib_path import wsgiref.handlers import webapp2 import views url_map = [ ('/', views.MainPage), ] app = webapp2.WSGIApplication(url_map, debug=True)
Here is where I call for a lib_path and it has to be called first. I don't call it anywhere else.
My urls.py:
application: helloworld version: 1 runtime: python27 api_version: 1 threadsafe: yes handlers: - url: /static static_dir: static - url: /.* script: urls.app libraries: - name: webapp2 version: "2.5.1" - name: jinja2 version: "2.6"
It is better to use a real version number than 'latest'. This way if one of the functions you were using is not present in the newest version it won't break your code and keep your applicaiton running.
And let's not forget views.py :
import os import jinja2 import webapp2 from models import * jinja_environment = jinja2.Environment( loader=jinja2.FileSystemLoader(os.path.dirname(__file__))) class MainPage(webapp2.RedirectHandler): def get(self): msg = 'Hello World!' template_values = { 'data': msg, } template = jinja_environment.get_template('/templates/home.html') self.response.out.write(template.render(template_values))
Simple, right? ;)
One other thing, I suggest to write all the queries (with filters) in your models.py under @classmethod for following reasons:
- Simple to use - if anyone else works with you on this project, he doesn't have to know how to query, so one less place for errors.
- Easy to change - if google will change how BigTable does this query or that, you will have to update your code in one place only.
- Easy to maintain.
That's it. Hope it helps you to get started with developing your application and not spending precious time on orginising imports.
No comments:
Post a Comment