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_name
when 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')
精彩评论