如何使用MongoEngine ReferenceField为2个集合使用相同的ObjectId?

问题描述:

我有一个相当重要的文档类User,我想将它分成两部分:UserProfile文档中的用户配置文件(名称和头像),其余部分位于User文档中,如下所示(使用MongoEngine):如何使用MongoEngine ReferenceField为2个集合使用相同的ObjectId?

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 documentation以及mongoengine ReferenceField documentation,但它并没有帮助我解决这个具体问题。

+0

为什么你不使用登录(我认为它是唯一的)为_id? – 2013-04-05 12:12:09

+0

是的,我可以,你是对的,但我不认为它解决了我的问题:我将如何实现'post.author'和'post.author_profile'?我需要两个ReferenceFields,一个指向User,另一个指向UserProfile,它们都具有相同的ID(无论是objectid还是login)。你看到我的观点了吗? – MiniQuark 2013-04-05 19:25:03

+0

我从来没有与mongoengine合作过,所以不幸的是我无法帮助你。另外,我不喜欢使用DBRef,因为我更喜欢手动控制关系。我不知道你是否对mongodb是陌生的,但需要记住的一点是,关系数据库不是设计数据。 – 2013-04-09 11:41:47

你不得不存储相同的ID两次做到这一点:

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

我不知道,如果是带给您的解决方案的任何利益 - 有可能在查询中取消引用数的改进,但我必须测试一下!

我结束了写这个:

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类,我也添加这样一个user_profile (只读)参考。请注意,如果您只访问author_profile,则不会加载author,反之亦然。