谷歌应用引擎 Python 数据存储
我想做的是一个数据结构,这个结构里包含用户的名字、ID和加入日期。然后我还想要一个“子结构”,这个子结构里包含用户的“文本”和修改日期。每个用户会有多个这样的文本实例。
class User(db.Model):
ID = db.IntegerProperty()
name = db.StringProperty()
datejoined = db.DateTimeProperty(auto_now_add=True)
class Content(db.Model):
text = db.StringProperty()
datemod= db.DateTimeProperty(auto_now_add = True)
这个代码设置得对吗?
3 个回答
1
你可能会看到另一种解决方案,那就是使用引用属性(referenceproperty)。
class User(db.Model):
name = db.StringProperty()
datejoined = db.DateTimeProperty(auto_now_add=True)
class Content(db.Model):
user = db.ReferenceProperty(User,collection_name='matched_content')
text = db.StringProperty()
datemod= db.DateTimeProperty(auto_now_add = True)
content = db.get(content_key)
user_name = content.user.name
#looking up all of the content for a particular user
user_content = content.user.matched_content
#create new content for a user
new_content = Content(reference=content.user)
4
你会遇到一个问题,就是让 User.ID
唯一化并不是一件简单的事。问题在于,如果有两个写入操作同时发生在不同的数据库分片上,它们都会几乎在同一时间检查是否有符合唯一性要求的记录,但都没有找到,然后它们就会各自创建相同的记录(在唯一性方面),这样就会导致数据库状态不合法。为了解决这个问题,appengine 提供了一种确保某些数据存储实体总是放在同一台物理机器上的方法。
要做到这一点,你需要利用实体的键来告诉 Google 如何组织这些实体。假设你想让用户名是唯一的。你需要把 User
改成这样:
class User(db.Model):
datejoined = db.DateTimeProperty(auto_now_add=True)
没错,就这么简单。这里没有用户名,因为它会被用在键里,所以不需要单独出现。如果你愿意,你可以这样做……
class User(db.Model):
datejoined = db.DateTimeProperty(auto_now_add=True)
@property
def name(self):
return self.key().name()
要创建一个 User
的实例,你现在需要做一些不同的事情,你需要在初始化方法中指定一个 key_name
。
someuser = User(key_name='john_doe')
...
someuser.save()
其实,你想确保用户之间不会互相覆盖,所以你需要把用户创建的过程放在一个事务中。首先定义一个函数来进行必要的检查:
def create_user(username):
checkeduser = User.get_by_key_name(username)
if checkeduser is not None:
raise db.Rollback, 'User already exists!'
newuser = User(key_name=username)
# more code
newuser.put()
然后,以这种方式调用它:
db.run_in_transaction(create_user, 'john_doe')
要查找一个用户,你只需这样做:
someuser = User.get_by_key_name('john_doe')
接下来,你需要某种方法将内容与其用户关联起来,反之亦然。一个解决方案是通过将用户声明为内容的父级,把内容放入与用户相同的实体组中。为此,你不需要改变内容本身,只需稍微不同地创建它(就像你对 User 所做的那样):
somecontent = Content(parent=User.get_by_key_name('john_doe'))
因此,给定一个内容项,你可以通过检查它的键来查找用户:
someuser = User.get(somecontent.key().parent())
反过来,查找特定用户的所有内容就稍微复杂一点。
allcontent = Content.gql('where ancestor is :user', user=someuser).fetch(10)