== No More JOINs == SELECT * FROM actor JOIN actor_preferences ON actor_preferences.actor = actor.id WHERE actor.name = 'zed' becomes query = Actor.gql('WHERE name = :1', 'zed') actor_refs = Actor.get_by_key_name([row.id for row in query]) Note: get([100 keys]) is also quite a bit faster than (get(key) for key in [100 keys]) == No More Slowness == nice article on indexes in app engine: http://code.google.com/appengine/articles/index_building.html == DictProperty == from http://code.google.com/p/jaikuengine/source/browse/trunk/common/properties.py from google.appengine.ext import db from google.appengine.api.datastore_types import Blob class DictProperty(db.Property): def validate(self, value): value = super(DictProperty, self).validate(value) if not isinstance(value, dict): raise Exception("NOT A DICT %s" % value) return value def default_value(self): return {} def datastore_type(self): return Blob def get_value_for_datastore(self, model_instance): value = super(DictProperty, self).get_value_for_datastore(model_instance) return Blob(pickle.dumps(value, protocol=-1)) def make_value_from_datastore(self, model_instance): value = super(DictProperty, self).make_value_from_datastore(model_instance) return pickle.loads(str(value)) == Efficient Paging Using __key__ == http://groups.google.com/group/google-appengine/browse_thread/thread/ee5afbde20e13cde == Entity Groups == If you're keen, learn how to use them here: http://code.google.com/appengine/docs/python/datastore/keysandentitygroups.html == Zip it up == from http://code.google.com/p/jaikuengine/source/browse/trunk/build.py import zipfile ZIP_SKIP_RE = re.compile('\.svn|\.pyc|\.[pm]o') IGNORED_CONTRIB = ('admin', 'gis', 'comments', 'localflavor', 'databrowse') def zip_vendor_lib(lib): f = zipfile.ZipFile('%s.zip' % lib, 'w') for dirpath, dirnames, filenames in os.walk('vendor/%s' % lib): if dirpath == os.path.join('vendor', lib, 'contrib'): _strip_contrib(dirnames) for filename in filenames: name = os.path.join(dirpath, filename) if ZIP_SKIP_RE.search(name): logging.debug('Skipped (skip_re): %s', name) continue if not os.path.isfile(name): logging.debug('Skipped (isfile): %s', name) continue logging.debug('Adding %s...', name) f.write(name, name[len('vendor/'):], zipfile.ZIP_DEFLATED) f.close() == Conditional Properties (for now) == if settings.APP_ENGINE: from appengine_django.models import db, BaseModel Model = BaseModel else: from django.db import models Model = models.Model class Blog(Model): if settings.APP_ENGINE: title = db.StringProperty(required=True) author = db.ReferenceProperty(User) else: title = models.CharField(_("title"), max_length=100) author = models.ForeignKey(User, related_name=_("author"))