开发者

:has_one relationship problem when using MongoDB

I have a pretty simple setup. To sum it up here's what I'm doing:

class Movie
  include MongoMapper::Document
  has_one :setting
end

class Setting
  include MongoMapper::EmbeddedDocument
  belongs_to :movie
end

What I want to do is to update the setting of a movie in the same form as the movie other information. Therefor I do that :

- form_for ['movies', @movie] do |f|
  # ...
  -f.fields_for @movie.setting do |ms|
    # ...

This doesn't work as I get this error :

stack level too deep

[text bellow is repeated a hundred time or so]

/Users/marc/.gem/ruby/1.8/gems/mongo_mapper-0.6.10/lib/mongo_mapper/associations/one_proxy.rb:46:in开发者_StackOverflow `find_target'
/Users/marc/.gem/ruby/1.8/gems/mongo_mapper-0.6.10/lib/mongo_mapper/associations/proxy.rb:98:in `load_target'
/Users/marc/.gem/ruby/1.8/gems/mongo_mapper-0.6.10/lib/mongo_mapper/associations/proxy.rb:88:in `method_missing'
/Users/marc/.gem/ruby/1.8/gems/mongo_mapper-0.6.10/lib/mongo_mapper/associations/one_proxy.rb:56:in `target_class'
/Users/marc/.gem/ruby/1.8/gems/mongo_mapper-0.6.10/lib/mongo_mapper/associations/one_proxy.rb:46:in `find_target'
/Users/marc/.gem/ruby/1.8/gems/mongo_mapper-0.6.10/lib/mongo_mapper/associations/proxy.rb:98:in `load_target'
/Users/marc/.gem/ruby/1.8/gems/mongo_mapper-0.6.10/lib/mongo_mapper/associations/one_proxy.rb:17:in `replace'
/Users/marc/.gem/ruby/1.8/gems/mongo_mapper-0.6.10/lib/mongo_mapper/associations.rb:39:in `setting='
/Users/marc/.gem/ruby/1.8/gems/mongo_mapper-0.6.10/lib/mongo_mapper/embedded_document.rb:185:in `send'
/Users/marc/.gem/ruby/1.8/gems/mongo_mapper-0.6.10/lib/mongo_mapper/embedded_document.rb:185:in `initialize'
/Users/marc/.gem/ruby/1.8/gems/mongo_mapper-0.6.10/lib/mongo_mapper/embedded_document.rb:177:in `each'
/Users/marc/.gem/ruby/1.8/gems/mongo_mapper-0.6.10/lib/mongo_mapper/embedded_document.rb:177:in `initialize'
/Users/marc/.gem/ruby/1.8/gems/mongo_mapper-0.6.10/lib/mongo_mapper/dirty.rb:42:in `initialize'
/Users/marc/.gem/ruby/1.8/gems/mongo_mapper-0.6.10/lib/mongo_mapper/embedded_document.rb:91:in `new'
/Users/marc/.gem/ruby/1.8/gems/mongo_mapper-0.6.10/lib/mongo_mapper/embedded_document.rb:91:in `initialize_doc'
/Users/marc/.gem/ruby/1.8/gems/mongo_mapper-0.6.10/lib/mongo_mapper/document.rb:316:in `find_one'
/Users/marc/.gem/ruby/1.8/gems/mongo_mapper-0.6.10/lib/mongo_mapper/document.rb:321:in `find_one!'
/Users/marc/.gem/ruby/1.8/gems/mongo_mapper-0.6.10/lib/mongo_mapper/document.rb:88:in `find!'
/Users/marc/.gem/ruby/1.8/gems/mongo_mapper-0.6.10/lib/mongo_mapper/document.rb:96:in `find'
/Users/marc/Code/mycompany/dontreadthat/sources/app/controllers/application_controller.rb:53:in `set_page_title'

Here's the catch : When I replace the has_one relationship by a key in the Movie model:

key :setting, Setting

... it works fine. No stack error.

I could just drop the relation and go with the key but :

  • It's not pretty

  • If I try to update movie.setting using .update_attributes it drops all the other attributes. Let's say I update movie.setting.key1, it will reset movie.setting.key2... which is normal

I can't find anything helpful out there, so any help or pointers would be greatly appreciated.


I'm pretty sure that has_one relationships aren't supported as embedded documents. So, for example, this does work:

class Setting
  include MongoMapper::Document
  key :movie_id, ObjectId
  belongs_to :movie
end

class Movie
  include MongoMapper::Document
  one :setting, :class => Setting
end

If you don't want the first-class Settings document, which you probably don't need, you might consider storing these settings using a key of type Hash or another key of some custom type you've defined for Mongo. See the WindowSize class in the MongoMapper test suite for an example.


I ended up setting the Setting as a key and overloading the = method :

in movie

  def setting=(new_setting)
    super ( (self.setting.nil?)? new_setting : (self.setting.keys.merge new_setting) )
  end

and in setting

  def keys
    keys_hash = {}
    self.attributes.each do |attribute|
      keys_hash.merge!( {attribute[0].to_s => attribute[1].to_s}) unless attribute[0].to_s == "_id"
    end
    return keys_hash
  end

It's not optimal, but it'll work fine until there is a better way of doing has_one relationships.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜