Model inheritance and filtering
I talked about using GAE from the command line. I am going to reuse part of that to show how to make inheritance filtering. First, let’s separate the init code into gaeinit.py:
from appengine_django import LoadSdk LoadSdk() from google.appengine.tools import dev_appserver_main as dae da = dae.DEFAULT_ARGS da[dae.ARG_DATASTORE_PATH] = '/path/to/datastore/file' from appengine_django import InstallAppengineHelperForDjango InstallAppengineHelperForDjango()
Replace the datastore file path as appropriate – any path would do (e.g. ‘/tmp/model-inheritance.datastore’).
Let’s make file called model-inheritance.py in the same folder, initialize using gaeinit and put the models:
import gaeinit from google.appengine.ext import db # Main expando class Word(db.Expando): ttype = None @classmethod def ffil(self, res): return res @classmethod def tfil(self, res): if self.ttype == None: return res return res.filter('t =', self.ttype) @classmethod def all(self): res = super(Word, Word).all() res = self.tfil(res) res = self.ffil(res) return res def __str__(self): return "[%s, %s]" % (self.t, self.i) # Sub-classes class Adjective(Word): ttype = 'adjective' class Noun(Word): ttype = 'noun' class Verb(Word): ttype = 'verb' class SecondVerb(Word): ttype = 'verb' @classmethod def ffil(self, res): return res.filter('i =', 2)
The base model class is called Word. It is a normal Expando, with the filtering additions and a standard str just for pretty printing (see Python docs for this one). Filtering additions are simple:
- all replaces the default Expando all classmethod to allow additional filtering by tfil and ffil,
- tfil is a type filtering method – it filters by ‘t =’, where t is the attribute on the Expando,
- ffil is a function filtering method – by default, doesn’t do anything, so it’s basically an IoC placeholder.
The subclasses just define three word types – adjective, noun and verb. SecondVerb is a contrived example that shows how to use ffil to additionally filter by another property, in this case the dummy i variable.
Here’s how we can test and use it:
# Populate with test data db.delete(Word.all()) for t in ['adjective', 'noun', 'verb']: for i in range(0, 4): d = Word(t=t, i=i) d.put() # List for k in [Word, Adjective, Noun, Verb, SecondVerb]: print "---- %s" % k.__name__ for r in k.all(): print "%s" % r
The above prints the following:
---- Word [adjective, 0] [adjective, 1] [adjective, 2] [adjective, 3] [noun, 0] [noun, 1] [noun, 2] [noun, 3] [verb, 0] [verb, 1] [verb, 2] [verb, 3] ---- Adjective [adjective, 0] [adjective, 1] [adjective, 2] [adjective, 3] ---- Noun [noun, 0] [noun, 1] [noun, 2] [noun, 3] ---- Verb [verb, 0] [verb, 1] [verb, 2] [verb, 3] ---- SecondVerb [verb, 2]
Obviously, this is query-only – you need to use the base class to input all the values, as using e.g. Noun.put() would put it under Noun, not under Word. I find it useful for model categorization, I find this not to be a huge drawback in these cases.