This SO question has an answer (by @jgeewax) that is almost right (wrong exit condition, as I commented there). Here is a fixed one...:
class MyModel(db.Expando):
@classmethod
def count_all(cls):
"""
Count *all* of the rows (without maxing out at 1000)
"""
count = 0
query = cls.all().order('__key__')
while True:
current_count = query.count()
if current_count == 0: return count
count += current_count
if current_count == 1000:
last_key = query.fetch(1, 999)[0].key()
query = query.filter('__key__ > ', last_key)
return count
The performance problem, of course, is that this will use one actual query to the datastore for every 1000 items you have -- denormalizing things by keeping an actual count, as @Chris suggests, is going to use far fewer queries. (Be sure to use a sharded counter or other forms of efficient counters as App Engine Fan explains!).
Denormalization is a fact of life with non-relational DBs, and, done properly, can make a huge difference to your performance. As for the worries you express about DRY, just use class methods or other forms of functions to performs all puts and removes of your entities (i.e., [[except within the class methods in question]], never call methods such as .put()
directly on the entities, call the appropriate class methods instead!), and those functions will be the obvious place to keep the denormalized counters up to date!