2013-08-16 2 views
2

Я пытаюсь отслеживать текущие, предыдущие и выбранные (изменения в конце периода) пользовательские состояния пользователя.Mongoid встраивает один класс в несколько полей

В конце месячного периода, если chosen_subscription != current_subscription, изменяется current_subscription.

class User 
    include Mongoid::Document 

    embeds_one :previous_subscription, class_name: "Subscription" 
    embeds_one :current_subscription, class_name: "Subscription" 
    embeds_one :chosen_subscription, class_name: "Subscription" 
end 

class Subscription 
    include Mongoid::Document 

    embedded_in :user 

    field :plan, type: String 
    field :credits, type: Integer 
    field :price_per_credit, type: BigDecimal 

    field :start, type: Date 
    field :end, type: Date 
end 

Mongoid хочет, чтобы указать это больше и таким образом, что не имеет смысла для меня:

Mongoid::Errors::AmbiguousRelationship: 

Problem: 
    Ambiguous relations :previous_subscription, :current_subscription, :chosen_subscription defined on User. 
Summary: 
    When Mongoid attempts to set an inverse document of a relation in memory, it needs to know which relation it belongs to. When setting :user, Mongoid looked on the class Subscription for a matching relation, but multiples were found that could potentially match: :previous_subscription, :current_subscription, :chosen_subscription. 
Resolution: 
    On the :user relation on Subscription you must add an :inverse_of option to specify the exact relationship on User that is the opposite of :user. 

Это происходит, когда я перезаписать существующий current_subscription. Полагаю, в данный момент Mongoid хочет отменить регистрацию подписки пользователя старой подписки.

Конечно, каждый подписной объект принадлежит только одному пользователю и user == user.previous_subscription.user == user.current_subscription.user == user.chosen_subscription.user

Но это не имеет смысла для меня, чтобы сказать, что Subscriptionuser является обратным по любому из трех.

Как его правильно построить?

+0

Я не совсем уверен, как бы вы ее построите но вы можете попробовать просить группу моногидов google. Проблема mongoid говорит вам о том, что если вы делаете 'subscription.user = some_user', он не знает, к какой подписке вы говорите, с точки зрения пользователя. Вы также можете попробовать установить 'inverse_of: nil', если вы не собираетесь использовать' subscription.user = 'в своем коде. – rubish

+0

Я никогда не устанавливал подписку. Я думаю, что он был установлен неявно, когда я устанавливаю пользователя. * _ Подписка. inverse_of: ноль хорошо работал для меня. Если вы создадите для этого ответ, я буду отмечать его правильно. – Jan

+0

Я добавил комментарий в ответ с немного дополнительной информацией и некоторыми ошибками с предложением. Я не считаю, что это идеальное решение, но работает для меня в большинстве случаев. – rubish

ответ

5

Как говорится: вы должны добавить опцию: inverse_of. Попробуйте следующее:

class User 
    include Mongoid::Document 

    embeds_one :previous_subscription, class_name: "Subscription", inverse_of: :previous_subscription 
    embeds_one :current_subscription, class_name: "Subscription", inverse_of: :current_subscription 
    embeds_one :chosen_subscription, class_name: "Subscription", inverse_of: :chosen_subscription 
end 

class Subscription 
    include Mongoid::Document 

    embedded_in :user, inverse_of: :previous_subscription 
    embedded_in :user, inverse_of: :current_subscription 
    embedded_in :user, inverse_of: :chosen_subscription 

    field :plan, type: String 
    field :credits, type: Integer 
    field :price_per_credit, type: BigDecimal 

    field :start, type: Date 
    field :end, type: Date 
end 
+1

Я бы подумал, что embedding_in не может использоваться несколько раз, особенно для одного и того же родителя. Но это работает. Однако для embads_one я использовал inverse_of:: user. – Jan

0

Каждое отношение mongoid имеет обратное отношение, которое используется для правильной настройки обеих сторон. В большинстве случаев его можно правильно идентифицировать на основе соглашений об именах, используемых классов и т. Д., Но иногда вам необходимо их явно определить.

Проблемы Mongoid жалуется здесь является то, что отношения, определенные в User моделей неоднозначны, поскольку Mongoid не может определить, какие из них сопоставляется subscription.user отношений в Subscription. Более того, когда используется subscription.user = some_user, mongoid не может идентифицировать, если вам нужно установить previous_subscription, current_subscription или chosen_subscription у пользователя на объект подписки. Даже если вы явно не вызываете этот метод, вы можете столкнуться с этими проблемами, так как mongoid пытается установить обратное, когда вы устанавливаете отношение.

Я не уверен, что это правильный способ построения системы, но вы можете определить inverse_of: nil в своих отношениях, чтобы сказать mongoid, чтобы не устанавливать обратные отношения при настройке отношения. Этот подход следует использовать, только если вам не нужно использовать subscription.user= в вашем коде. subscription.user также могут иметь проблемы, но можно использовать недокументированные методы doc._parent или doc._root, если они вам в этом нужны. Этот подход, хотя и решает проблему, но имеет свои недостатки. Я попытался показать их здесь с помощью следующего кода:

class Tester 
    include Mongoid::Document 

    field :name, type: String 

    embeds_one :first_module, class_name: 'TestModule', inverse_of: nil 
    embeds_one :last_module, class_name: 'TestModule', inverse_of: nil 

end 

class TestModule 
    include Mongoid::Document 

    field :name, type: String 

    # not needed as such, but required to tell mongoid that this is a embedded document 
    embedded_in :tester, inverse_of: nil 
end 

t1 = Tester.new(name: 't1') 
m1 = t1.build_first_module(name: 't1m1') 
m2 = t1.build_last_module(name: 't1m2') 

t1.first_module == m1 #=> true 
t1.last_module == m2 #=> true 

m1.tester    #=> nil 
m2.tester    #=> nil 

t1.save 
t1 = Tester.last 
m1 = t1.first_module 
m2 = t1.last_module 

t1.first_module == m1 #=> true 
t1.last_module == m2 #=> true 

m1.tester    #=> nil 
m2.tester    #=> nil 

Там было несколько дискуссий о чем-то вроде этого на Mongoid отслеживания проблем тоже: #1677 & #3086

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