2013-04-04 2 views
0

У меня довольно тяжелый User класс документа, и я хотел бы, чтобы сократить его на две части: профиль (имя и аватар) пользователя в UserProfile документе, а остальные в User документе, как это (используя MongoEngine):Как я могу использовать одну и ту же ObjectId для 2 коллекций, используя MongoEngine ReferenceField?

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() 

Я хотел был бы иметь тот же ObjectId как для UserProfile, так и для пользователя, так что мне нужен только один ObjectId для ссылки как на пользователя, так и на 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? –

+0

Да, я могу, вы правы, но я не думаю, что он исправляет мою проблему: как я буду реализовывать 'post.author' и' post.author_profile'? Мне нужны два ReferenceFields, один указывает на пользователя, а другой указывает на UserProfile, оба с тем же идентификатором (будь то objectid или login). Вы видите мою мысль? – MiniQuark

+0

Я никогда не работал с mongoengine, поэтому, к сожалению, я не могу с этим поделать. Кроме того, я не люблю использовать DBRef, потому что я предпочитаю управлять отношениями вручную. Я не знаю, если вы новичок в mongodb, но одна важная вещь, о которой нужно помнить, - это не проектные данные, как в реляционных базах данных. –

ответ

0

Вы должны были бы хранить тот же идентификатор дважды, чтобы сделать это:

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

Я не уверен, если это приносит какие-либо выгоды для вашего решения - там возможно улучшение количества запросов к разыменованию , но мне пришлось бы это проверить!

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 класс, я также добавить такой user_profile (только для чтения). Обратите внимание: если вы только получаете доступ к author_profile, он не загружает author и наоборот.

Смежные вопросы