Python:在数据存储中建模一对一关系

4 投票
2 回答
753 浏览
提问于 2025-04-16 23:11

假设我们有两个模型:User(用户)和Token(令牌)。一个用户只能拥有一个Token,而一个Token也只能属于一个用户。

如果你来自关系型数据库,下面的做法看起来就像是正确的方式

class User(db.Model):
    name = db.StringProperty()

class Token(db.Model):
    name = db.StringProperty()
    owner = db.ReferenceProperty(User)

这样做当然会创建一个User.token_set,它是一个db.Query。但我其实更想用User.token来直接访问令牌,而不是用User.token_set.get(),那么我们可以这样做……

class User(db.Model):
    name = db.StringProperty()
    token = db.ReferenceProperty(Token)

class Token(db.Model):
    name = db.StringProperty()
    owner = db.ReferenceProperty(User)

现在我可以双向访问它们:User.tokenUser.owner。而且可以忽略那些自动创建的.token_setuser_set

这样做有什么问题吗?从逻辑上来说,或者性能上来说?

也许我根本就不应该有两个模型。实际上,它们都只包含四到五个属性。它们是不是应该合并成一个?在什么情况下,一对一的关系应该被合并成一个模型呢?

谢谢任何建议!

2 个回答

1

在用户类中添加令牌引用有好处也有坏处。好处是你在获取令牌时会更快,坏处是插入数据时需要更多的操作。

user = User(name='john').put()
token = Token(name='somehash', user=user).put()
user.token = token
user.put()

而且在删除令牌时,你还得处理空引用的问题。

如果你想要更简洁的语法,可以考虑使用类装饰器:

class User(db.Model):
    name = db.StringProperty()

    @property
    def token(self):
        if not hasattr(self, '_token_cached'):
            setattr(self, '_token_cached', self.token_set.get())
       return self._token_cached
4

这是一个应用引擎的数据存储。它不是传统的SQL数据库。你应该根据系统的使用情况来组织数据。比如说,你可能有一些地方是用户和令牌一起使用的,由于没有多对一的问题,这些数据可以放在同一个模型实例里。这样做会让你在性能上更好(比如通过令牌快速找到用户),而且在处理一致性方面也更简单(更新令牌和用户可以在一个事务中完成)。

我不确定自我链接在这里是否合适,所以如果不合适的话,可能会有人编辑这部分内容。不过我写了一个网站 http://acooke.org/cute/LessonsLea2.html,试图把我在数据存储方面的经验整理在一起。你可能会觉得它有用。

[编辑:可以这样理解:你不是在建模数据,而是在写一个旨在扩展的系统。这一点比建模数据更重要。这就是应用引擎的运作方式。我并不是说这是个好主意,但这就是应用引擎的做法。如果你的主要目标是建模数据,比如你想让多个应用以不同方式使用你的数据,那你就不应该使用应用引擎。]

撰写回答