Thanks for your advice Nick.

My actual app is a bit more complex than the example I gave. I'll explain
more in my response to your comments below.

Will this be an all-or-nothing proposition? Eg, if the put of 'foo' changed
> the foo_name, they'll all need updating, otherwise none of them will? In
> that case, you'd be better off checking if it's changed, and updating them
> all (without the inequality filter) if it did change.
>

Yes, indeed it's all-or-nothing.

The above will change every single Bar and Baz entity that doesn't match
> foo's entity name, though, including those that have nothing to do with the
> foo you modified.
>

Yep, that's true. In my real app though, I don't want to query on foo's
name, but instead on foo's "metadata signature", which is a hash of a
combination of foo's key and properties (or rather, the properties that
reverse-referenced entities duplicate, such as foo's name).

 Depending on how many Bar and Baz entities there are per Foo, you could
> just drop the inequality and filter them out once they're fetched.
>

I have ReferenceProperty properties all over the place in my model design,
so there are many Foo-type Models, some with up to a dozen different
reverse-referenced Bar/Baz-type Models, with each of those Foo->Bar/Baz
relations potentially having thousands of entities.

Given that you're already hard-coding in the list of collections, you could
> avoid the need for a 'get_model_class_for_collection' method by replacing
> the loop with:
>
> "for model_class in (Bar, Baz):"
>

In my real app I'm not hard-coding these (due to the maintenance required as
the model design evolves), I'm trying to get them dynamically, see this
other thread I created:

http://groups.google.com/group/google-appengine/browse_thread/thread/1a6e911f3eff79ed

However, I may indeed need to hard-code these if I can't find any
satisfactory way of getting all the "collection_name"s dynamically.

Now more about my real app...

I have a mixin class for Models to inherit from, which looks like this:

class ModelCommons(object):

  def get_metadata_signature(self):
    return hashlib.sha1('Kind:%s|ID:%s|KeyName:%s|Prop:%s' % (
      self.kind(), self.key().id(), self.key().name(),
repr(self.get_current_metadata()))).hexdigest()

  def get_current_metadata(self):
    meta = []
    for prop_name in self.__class__.meta_property_names:
      meta.append((prop_name, getattr(self.__class__,
prop_name).get_value_for_datastore(self)))
    return meta

  def update_collections_metadata(self):
    current_metadata_signature = self.get_metadata_signature()
    for collection_name in self.get_collection_names() # In a perfect world
      model_class =
self.get_model_class_from_collection_name(collection_name) # The magic
method I was looking for
      reference_prop_name =
self.get_reverse_reference_property_name_from_collection_name(collection_name)
# Another magic method that I need
      metadatasig_prop_name = '%s_metadata_signature' % reference_prop_name
      while True:
        # BEGIN TRANSACTION (not shown)
        entity = model_class.all().filter('%s !='
% metadatasig_prop_name, current_metadata_signature).get()
        if not entity:
          break
        setattr(entity, reference_prop_name, self)

 entity.update_reference_property_metadata_for_property(reference_prop_name)
        entity.put()
        # END TRANSACTION

  def update_reference_property_metadata_for_property(self, prop_name):
    # Implementation not shown, but assume this method also
    # updates the {{prop_name}}_metadata_signature property.

  def post_put(self):
    if self.get_metadata_signature() != self._initial_metadata_signature
      from google.appengine.ext import deferred
      deferred.defer(self.update_collections_metadata) # Haven't tried to
see if this works, but that's the idea

# For the ever-so-boring Author -> Article example:

class Author(db.Model, ModelCommons):
  name = db.StringProperty()
  avatar_url = db.StringProperty()
  permissions = db.StringListProperty()

  meta_property_names = ('name', 'avatar_url')

  # Yuck way to set the initial state. Is there a better way?
  def __init__(self, *args, **kwds):
    super(self.__class__, self).__init__(*args, **kwds)
    self._initial_metadata_signature = self.get_metadata_signature()

class Article(db.Model, ModelCommons):
  title = db.StringProperty()
  content = db.BlobProperty()
  author = db.ReferenceProperty(Author, collection_name = 'articles')
  author_metadata_signature = db.StringProperty()
  author_name = db.StringProperty()
  author_avatar_url = db.StringProperty()
  pre_put():
    self.update_reference_property_metadata_for_property('author')

I'll also have clean-up tasks just in case the deferred task in post_put()
isn't successfully added.

If you've followed all that code, when this happens:

author = Author(key_name = 'BillyJoel123', name='Billy Joel',
  avatar_url='http://whatever.com/billy.png', permissions =
['editor','admin'])
author.put()

article = Article(author=author, title = 'I love to sing', content='Yes I
do')
article.put()

# Some time later...
author = db.get(db.Key.from_path('Author', 'BillyJoel123'))
author.avatar_url = 'http://whatever.com/anotherpic.png'
author.put()

Then article's "author_*" properties will (eventually) be updated without
any extra intervention.

So that's what I'm trying to do. It's possible right now if I choose to
hard-code certain information, but as I mentioned earlier, maintaining the
model design when it's big (and as it evolves) can cause headaches.

I think Google App Engine should make available public methods to be able to
get information about the collections for each entity and/or model class
that has collections. Such collection information includes:


   - The collection_names for each Model class (similar to the properties()
   method of the db.Model class)
   - The reverse-referenced Model class for each collection
   - The property name of the ReferenceProperty of the reverse-referenced
   Model class


This information is already available "in the background", so I can't see
why it shouldn't be made publicly available. How can I make a request to
have this included in future SDK versions?

PS. If you've managed to read all the way down to here, thanks for taking
the time to read this. :)

-- 
You received this message because you are subscribed to the Google Groups 
"Google App Engine" group.
To post to this group, send email to google-appeng...@googlegroups.com.
To unsubscribe from this group, send email to 
google-appengine+unsubscr...@googlegroups.com.
For more options, visit this group at 
http://groups.google.com/group/google-appengine?hl=en.

Reply via email to