0

我有一个相当重的User文档类,我想将它分成两部分:文档中的用户个人资料(姓名和头像)和UserProfile文档中的其余部分User,如下所示(使用 MongoEngine):

from mongoengine import *

class User(Document):
    login = StringField()
    password = StringField()
    posts = ListField(ReferenceField("Post", dbref = False))
    #... a bunch of other fields

class UserProfile(Document):
    name = StringField()
    avatar = URLField()

我希望 UserProfile 和 User 具有相同的 ObjectId,这样我只需要一个 ObjectId 来引用 User 和 UserProfile。毕竟,这实际上是一对一的关系,而且由于用户可以撰写许多帖子,我不想将她的个人资料嵌入帖子本身。创建用户文档时,我会立即创建相应的配置文件,如下所示:

john = User.objects.create(login = "john", password = "super!password")
john_profile = UserProfile.objects.create(id = john.id, name = "John Smith",
                avatar = "http://www.example.com/img/photo.jpg")

到目前为止,一切都很好。现在我有一个Post文档,其中包含一个author引用该User文档的字段:

class Post(Document):
    author = ReferenceField("User", dbref = False)
    text = StringField()

我想添加一个author_profile引用,基于相同的 ObjectId。我试过这个:

class Post(Document):
    author = ReferenceField("User", dbref = False)
    author_profile = ReferenceField("User", db_field = "author", dbref = False)
    text = StringField()

但我得到以下异常:

mongoengine.base.InvalidDocumentError: Multiple db_fields defined for: author

所以看来我必须“手动”这样做。可能是这样的:

class Post(Document):
    author = ReferenceField("User", dbref = False)
    text = StringField()
    @property
    def author_profile(self):
        if hasattr(self, "_author_profile"):
            return self._author_profile
        self._author_profile = UserProfile.objects.get(id = self._data["author"].id)
        return self._author_profile

我想这还不错,但是没有更好的解决方案吗?

谢谢。

注意:我阅读了关于一对一关系的mongodb 文档,以及mongoengine ReferenceField 文档,但它对这个特定问题没有帮助。

4

2 回答 2

0

您必须存储相同的 id 两次才能执行此操作:

class Post(Document):
    author = ReferenceField("User", dbref = False)
    author_profile = ReferenceField("UserProfile", dbref = False)
    text = StringField()

我不确定这是否会给您的解决方案带来任何好处 - 取消引用的查询数量可能有所改善,但我必须对此进行测试!

于 2013-04-10T12:14:28.423 回答
0

我最终写了这个:

def user_profile(reference_name):
    attrib_name = "_" + reference_name + "_profile"
    def user_profile_getter(self):
        if not hasattr(self, attrib_name):
            reference = self._data.get(reference_name, None)
            if not reference:
                return None
            setattr(self, attrib_name, UserProfile.objects.get(reference.id))
        return getattr(self, attrib_name)
    return property(user_profile_getter)

我的Post班级现在看起来像这样:

class Post(Document):
    author = ReferenceField("User", dbref = False)
    author_profile = user_profile("author")
    text = StringField()

每当我添加ReferenceField指向User类的 a 时,我也会添加这样的user_profile(只读)引用。请注意,如果您只访问author_profile,它不会加载author,反之亦然。

于 2013-04-10T13:44:15.697 回答