15

I am leveraging ndb's to_dict method to convert an object's properties into a python dict. From everything I can tell, this method does not include the object's key or parent within the dict as per the documentation:

https://developers.google.com/appengine/docs/python/ndb/modelclass#Model_to_dict

However for my situation I need the key to be in the dict. My preference would be to leverage the builtin method and subclass it or something similar rather than create my own to_dict method.

What is the best way to accomplish this or am I missing something obvious? Thanks in advance.

FYI: I am not leveraging django for this project but instead straight python deployed up to gae.

Alan Ruth
  • 193
  • 1
  • 7

2 Answers2

37

You're not missing anything ;-)

Just add the key to the dictionary after you call to_dict, and yes override the method.

If you have multiple models that don't share the same base class with your custom to_dict, I would implement it as a mixin.

to define to_dict as a method of a Mixin class. you would

class ModelUtils(object):
    def to_dict(self):
        result = super(ModelUtils,self).to_dict()
        result['key'] = self.key.id() #get the key as a string
        return result

Then to use it.

class MyModel(ModelUtils,ndb.Model):
    # some properties etc...
alex
  • 479,566
  • 201
  • 878
  • 984
Tim Hoffman
  • 12,976
  • 1
  • 17
  • 29
  • Thanks Tim! Worked perfectly. I am new to python and don't know how to override the method as you mention. I can post that as another question unless you are willing to edit your answer and provide some guidance. I appreciate the quick response and have enjoyed learning from you via all your other answers here on stack. – Alan Ruth May 31 '13 at 06:30
  • Danke sir. helped me as well – Jossef Harush Kadouri Mar 25 '14 at 10:50
3

Another easy way to achieve that (without having to override to_dict) is to add a ComputedProperty that returns the id, like so:

class MyModel(ndb.Model):

  # this property always returns the value of self.key.id()
  uid = ndb.ComputedProperty(lambda self: self.key.id(), indexed=False)

  # other properties ...

A ComputedProperty will be added to the result of to_dict just like any other property.

There are just two constraints:

  1. Apparently the name of the property can not be key (since that would conflict with the actual key) and id doesn't work either.
  2. This won't work if you don't specify a key or id when you create the object.

Also, the computed value will be written to the data store when you call put(), so it consumes some of your storage space.

An advantage is that this supports the include and exclude arguments of to_dict() out of the box.

Marten
  • 3,802
  • 1
  • 17
  • 26
  • This doesn't seem to work when initially creating an entity since the key hasn't yet been defined. I'm getting this error: `uid = ndb.ComputedProperty(lambda self: self.key.id()) AttributeError: 'NoneType' object has no attribute 'id'` – Stetzon Jun 30 '16 at 22:20
  • Correct, that's what I meant by constraint #2 in my answer. You must assign a key when the object is created to make this work. At least in my uses cases that's not an issue though. – Marten Jun 30 '16 at 22:30
  • 1
    Yep, I caught it almost right after I posted my comment. Must have missed your note the first time through. Thanks for the clarification. I was able to get it to work by [allocating and assigning](https://cloud.google.com/appengine/docs/python/ndb/creating-entity-keys#allocating_ids) a key to the entity before the initial _put_ – Stetzon Jun 30 '16 at 22:56