如何使用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,但它并没有帮助我解决这个具体问题。
答
你不得不存储相同的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
,反之亦然。
为什么你不使用登录(我认为它是唯一的)为_id? – 2013-04-05 12:12:09
是的,我可以,你是对的,但我不认为它解决了我的问题:我将如何实现'post.author'和'post.author_profile'?我需要两个ReferenceFields,一个指向User,另一个指向UserProfile,它们都具有相同的ID(无论是objectid还是login)。你看到我的观点了吗? – MiniQuark 2013-04-05 19:25:03
我从来没有与mongoengine合作过,所以不幸的是我无法帮助你。另外,我不喜欢使用DBRef,因为我更喜欢手动控制关系。我不知道你是否对mongodb是陌生的,但需要记住的一点是,关系数据库不是设计数据。 – 2013-04-09 11:41:47