noio
noio

Reputation: 5812

Extending appengine's db.Property with caching

I'm looking to implement a property class for appengine, very similar to the existing db.ReferenceProperty. I am implementing my own version because I want some other default return values. My question is, how do I make the property remember its returned value, so that the datastore query is only performed the first time the property is fetched? What I had is below, and it does not work. I read that the Property classes do not belong to the instances, but to the model definition, so I guess that the return value is not cached for each instance, but overwritten on the model every time. Where should I store this _resolved variable?

class PageProperty(db.Property):
  data_type = Page

  def get_value_for_datastore(self, model_instance):
    page = super(PageProperty, self).get_value_for_datastore(model_instance)        
    self._resolved = page
    return page.key().name()

  def make_value_from_datastore(self, value):
    if not hasattr(self, '_resolved'):
        self._resolved = Page.get_by_name(value)
    return self._resolved

Edit

Alex' answer is certainly usable. But it seems that the built-in db.ReferenceProperty does store the _RESOLVED variable on the model instance. As evidenced by:

[...]
    setattr(model_instance, self.__resolved_attr_name(), value)
[...]

def __resolved_attr_name(self):
    return '_RESOLVED' + self._attr_name()

The get_value_for_datastore method is passed the model instance, but make_value_from_datastore is not, so how do they find the _RESOLVED property from that method?

Edit 2

From the code I gather that google is using the __get__() and __set__() methods, both of which do get the model instance as an argument. Are those usable in custom classes? What is the difference with get_value_for_datastore and its counterpart?

Upvotes: 0

Views: 359

Answers (2)

Nick Johnson
Nick Johnson

Reputation: 101149

If you only want to change some small part of the behaviour of ReferenceProperty, you may want to simply extend it, overriding its default_value method. You may find the source for ReferenceProperty to be instructive.

Upvotes: 1

Alex Martelli
Alex Martelli

Reputation: 881843

A PageProperty instance exists per-model, not per-entity (where an entity is an instance of the model class). So I think you need a dictionary that maps pagename -> Page entity, instead of a single attribute per PageProperty instance. E.g., maybe something like...:

class PageProperty(db.Property):
  data_type = Page

  def __init__(self, *a, **k):
    super(PageProperty, self).__init__(*a, **k)
    self._mycache = {}       

  def get_value_for_datastore(self, model_instance):
    page = super(PageProperty, self).get_value_for_datastore(model_instance)        
    name = page.key().name()
    self._mycache[name] = page
    return name

  def make_value_from_datastore(self, value):
    if value not in self._mycache:
        self._mycache[value] = Page.get_by_name(value)
    return self._mycache[value]

Upvotes: 2

Related Questions