Creating a key reference between db.Model and NDB model.Expando

362 views
Skip to first unread message

Niklas Rosencrantz

unread,
Jan 31, 2012, 2:31:12 PM1/31/12
to appengine-...@googlegroups.com
Hello
My mini project this time is creating a reference as a key between a db.Model and an NDB model.Expando (the webapp2 user model.)
I wonder which type to use, the ndb or the old ie a model.KeyProperty() or a db.ReferenceProperty(User)?

I'm logging which user placed which Order and Order is a db.Model that I wish to connect with the relevant User entity.
So I'm looking at something like

class Order(db.Model):
  '''a webshop transaction'''
  items = db.ListProperty(db.Key) #the items being purchased
  amounts = db.ListProperty(int) #the corresponding amount of items
  total =  db.StringProperty() #what to pay
  user = model.KeyProperty() #or db.ReferenceProperty(User)?

Could you please tell me which type that I should use or anything else you may notice about this case?
Thanks
Nick Rosencrantz

Guido van Rossum

unread,
Jan 31, 2012, 2:38:31 PM1/31/12
to appengine-...@googlegroups.com
You can't use NDB properties in db model classes (or vice versa) so
you'll have to use a db.ReferenceProperty.

--
--Guido van Rossum (python.org/~guido)

Niklas Rosencrantz

unread,
Jan 31, 2012, 3:10:45 PM1/31/12
to appengine-...@googlegroups.com
I failed creating the reference:

buyer = db.ReferenceProperty(auth_models.User)
File "/media/Lexar/bnano/google/appengine/ext/db/__init__.py", line
3506, in __init__
raise KindError('reference_class must be Model or _SELF_REFERENCE')
KindError: reference_class must be Model or _SELF_REFERENCE

Could I somehow use a db.Key instead to create the reference? Or
create a reference the other way with a list for every user what
products the user bought.

Or just resort to the using the id instead of the key:

buyer_id = db.IntegerProperty()

?

Using the last way I can know the user saving it this way

order.buyer_id = int(user.key.id())

Thanks for the help

Jakob Holmelund

unread,
Jan 31, 2012, 4:26:23 PM1/31/12
to appengine-...@googlegroups.com
You could just use the user_id from the webapp2 User model as reference.. That would only require an IntegerProperty

Guido van Rossum

unread,
Jan 31, 2012, 4:27:04 PM1/31/12
to appengine-...@googlegroups.com
You can use ReferenceProperty(None), which is equivalent to
ReferenceProperty(Model).

You would have to set it using userinstance.key.to_old_key(), and to
get it you would have to get the key from the reference property using
get_value_for_datastore. This returns a db.Key, you can convert that
to an ndb Key using ndb.model.Key.from_old_key(oldkey).

But all this seems really awkward. Maybe you can store the key as a
string in your old db model?

Niklas Rosencrantz

unread,
Jan 31, 2012, 7:09:44 PM1/31/12
to appengine-...@googlegroups.com
Thanks for the advice. A string or an integer for the reference works.
I think I will do what Jakob says and use the id instead of the key
since I already have this working for my webshop:

class Order(db.Model):
'''a webshop transaction'''
items = db.ListProperty(db.Key)

amounts = db.ListProperty(int)
buyer_id = db.IntegerProperty()
total = db.StringProperty()

I think I also could rewrite the above model to an NDB model and then
I could use the keyproperty, but it's fine as it is now.
Cheers, Niklas

Kenny Trytek

unread,
Apr 5, 2013, 12:11:34 PM4/5/13
to appengine-...@googlegroups.com
Here's what I ended up doing to reference an ndb.Model from a db property:

class NDBKeyProperty(db.TextProperty):
    """
    Provides a way to reference an ndb Model from a db Model.
    """
    def validate(self, value):
        return value

    def get_value_for_datastore(self, model_instance):
        result = super(NDBKeyProperty, self).get_value_for_datastore(
            model_instance)

        if not result:
            return None

        attributes = {'args': result.flat(),
                      'kwargs': {'app': result.app(),
                                 'namespace': result.namespace()}}

        return simplejson.dumps(attributes)

    def make_value_from_datastore(self, value):
        if not value:
            return None

        result = simplejson.loads(value)
        return ndb.Key(*result['args'], **result['kwargs'])

To use it:
class MyModel(db.Model):
    ndb_ref = NDBKeyProperty()
Reply all
Reply to author
Forward
0 new messages