views:

47

answers:

2

Hi,

What would be the best way to get the latest inserted object using AppEngine ? I know in Django this can be done using

MyObject.objects.latest()

in AppEngine I'd like to be able to do this

class MyObject(db.Model):
  time = db.DateTimeProperty(auto_now_add=True)

# Return latest entry from MyObject.
MyObject.all().latest()

Any idea ?

+5  A: 

Your best bet will be to implement a latest() classmethod directly on MyObject and call it like

latest = MyObject.latest()

Anything else would require monkeypatching the built-in Query class.

Update

I thought I'd see how ugly it would be to implement this functionality. Here's a mixin class you can use if you really want to be able to call MyObject.all().latest():

class LatestMixin(object):
    """A mixin for db.Model objects that will add a `latest` method to the
    `Query` object returned by cls.all(). Requires that the ORDER_FIELD
    contain the name of the field by which to order the query to determine the
    latest object."""

    # What field do we order by?
    ORDER_FIELD = None

    @classmethod
    def all(cls):
        # Get the real query
        q = super(LatestMixin, cls).all()
        # Define our custom latest method
        def latest():
            if cls.ORDER_FIELD is None:
                raise ValueError('ORDER_FIELD must be defined')
            return q.order('-' + cls.ORDER_FIELD).get()
        # Attach it to the query
        q.latest = latest
        return q

# How to use it
class Foo(LatestMixin, db.Model):
    ORDER_FIELD = 'timestamp'
    timestamp = db.DateTimeProperty(auto_now_add=True)

latest = Foo.all().latest()
Will McCutchen
Hey Will, I thought about it, but I can't call this function without initiating an object. Is there a way to call it without object init. ?
Martin
Sure you can, if you define it as a class method (using the `@classmethod` decorator).
Will McCutchen
Excellent ! You just gave me a lesson :)
Martin
I'd actually suggest doing that instead of using the goofy proof of concept mixin I just added to my answer.
Will McCutchen
+3  A: 

MyObject.all() returns an instance of the Query class

Order the results by time:

MyObject.all().order('-time')

So, assuming there is at least one entry, you can get the most recent MyObject directly by:

MyObject.all().order('-time')[0]

or

MyObject.all().order('-time').fetch(limit=1)[0]
msbmsb
Or MyObject.all().order('-time').get()
Drew Sears
MyObject.all().order('-time')[0] should be avoided, as it will fetch a batch of 20 entities even though you only want one.
Wooble
@Wooble: Right.
msbmsb