开发者

How to represent one-to-one relationship in App Engine

Say you have a concept of "user" records that you'd like to store in the data store.

class User (db.Model):
  first_name = db.StringProperty()
  last_name = db.StringProperty()
  created = db.DateTimeProperty(auto_now_add=True)
  twitter_oauth_token = db.StringProperty()
  twitter_oauth_secret = db.StringProperty()

There are some fields you'd like to use almost ever time you use a user object, like first_name and last_name.

However, there are some fields you only have one use case for, for example, twitter_oauth_token and twitter_oauth_secret, and it's somewhat inefficient to bother serializing and deserializing these when they're not needed 95% of the time.

So if you split your model up:

class User (db.Model):
  first_name 开发者_如何学编程= db.StringProperty()
  last_name = db.StringProperty()
  created = db.DateTimeProperty(auto_now_add=True)

class UserTwitterOauth(db.Model):
  oauth_token = db.StringProperty(required=True)
  oauth_secret = db.StringProperty(required=True)
  created = db.DateTimeProperty(auto_now_add=True)

You can put a ReferenceProperty to the User in the UserTwitterOauth, but this would actually be one-to-many as there's nothing stopping there to being multiple UserTwitterOauth objects per User. You want there to be at most one UserTwitterOauth related to any User. How can you relate these models on a one-to-one basis?


In this specific case, your best option is probably to make the UserTwitterOauth entity a child of the User entity with a well-known key name, like so:

my_user = User(first_name="John", last_name="Smith")
my_user.put()
extra_info = UserTwitterOauth(parent=my_user, key_name="UserTwitterOauth")
extra_info.put()

You can add a straightforward method or property to the User class to make it easy to retrieve the additional information, and a class method to UserTwitterOauth to serve as a factory method, preserving the convention.

Incidentally, note that User is a dangerous name for an entity - the Users API has a class called User too, and unless you're very careful with your imports, you may end up referring to one when you intend to refer to the other.


A reference property to the user from the twitter access token is by far the easiest to maintain, in my view. It is true that the user could be referenced by many access tokens.

You will however find yourself doing things by convention a lot of times when working GAE.

EDIT preventing several access tokens referencing same user:

You can access referencing access tokens as a query via the User.usertwitteroauth_set property. If you want a more descriptive name, specify the parameter collection_namewhen setting up the ReferenceProperty. Say for example you want to remove any referencing access tokens before you add a new one, you could gather that logic as such:

class User(db.Model):
    def set_access_token(self, access_token):
        db.delete(self.twitter_access_tokens) # Think this should work, otherwise iterate over the query.
        new_access_token.user = self
        new_access_token.put()


class UserTwitterOauth(db.Model):
    user = db.ReferenceProperty(User, collection_name = 'twitter_access_tokens')
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜