MongoDB mongoid 的 accepts_nested_attributes_for 怎么用

yakjuly · 2012年01月16日 · 最后由 yakjuly 回复于 2012年02月04日 · 5163 次阅读

最近刚开始在实际项目中用 mongodb,遇到一些问题,请教一下大家。

class Policy
  include Mongoid::Document

  has_many :buckets
  accepts_nested_attributes_for :buckets

  field :min_device, :type => Integer, :default => 0
  field :max_device, :type => Integer, :default => 10
  field :name
end

class Bucket
  include Mongoid::Document

  belongs_to :policy

  field :start_point, :type => Integer
  field :end_point, :type => Integer  
  field :min, :type => Integer
  field :max, :type => Integer
end
params = {"policy"=>{"name"=>"aaaaaa", "buckets_attributes"=>{"new_1326681393748"=>{"end_point"=>"4", "max"=>"4", "min"=>"1", "_destroy"=>"false", "start_point"=>"1"}, "0"=>{"end_point"=>"11", "max"=>"10", "min"=>"7", "_destroy"=>"false", "start_point"=>"9"}}, "min_device"=>"1", "max_device"=>"4"}}

步骤

@policy = Policy.new(params[:policy])
 => #<Policy _id: , name: "aaaaaa", min_device: 1, _type: nil, max_device: 4>
@policy.buckets
 => [#<Bucket _id: , policy_id: nil, end_point: 4, max: 4, _type: nil, min: 1, start_point: 1>, #<Bucket _id: , policy_id: nil, end_point: 11, max: 10, _type: nil, min: 7, start_point: 9>]
@policy.save
@policy
 => #<Policy _id: 10, name: "aaaaaa", min_device: 1, _type: nil, max_device: 4>
@policy.buckets
 => [#<Bucket _id: , policy_id: nil, end_point: 4, max: 4, _type: nil, min: 1, start_point: 1>, #<Bucket _id: , policy_id: nil, end_point: 11, max: 10, _type: nil, min: 7, start_point: 9>] 

问题

  1. policy 关联的 buckets 为什么都没有关联保存?

搜了 stackoverflow 后 把 Policy 的 has_many :buckets 改成了 has_many :buckets, :autosave => true 后。重试以上步骤

@policy.buckets
 => [#<Bucket _id: 17, policy_id: nil, end_point: 4, max: 4, _type: nil, min: 1, start_point: 1>, #<Bucket _id: 18, policy_id: nil, end_point: 11, max: 10, _type: nil, min: 7, start_point: 9>] 

bucket 保存了 id,但是 policy_id 仍然为空,他们的关联关系没有保存下来。为什么,有什么办法解决?

另外还有个小问题,embeds_many embeds_in 和 has_many belongs_to 区别在哪里?

关于“小问题”http://mongoid.org/docs/relations.html embeds_many 是 Embedded relations has_many 是 Referenced relations

Embedded relations are associations between one or many objects where the child object is embedded within the parent in the same document in the database. They can be extremely efficient when kept to a managable size in MongoDB since the total number of queries required to retrieve an entire object graph can be reduced to one.

Referenced relations are associations between documents that reside in separate collections in MongoDB. The link between the documents is handled similar to a relational database where a "foreign key" needs to be stored on one or both sides of the relation, but note that accessing a referenced relation required a separate query to the database since joins cannot be performed.

关于 anaf,你的参数和文档里提到的不一致 http://api.rubyonrails.org/classes/ActiveRecord/NestedAttributes/ClassMethods.html

params = { :member => {
  :name => 'joe', :posts_attributes => [
    { :title => 'Kari, the awesome Ruby documentation browser!' },
    { :title => 'The egalitarian assumption of the modern citizen' },
    { :title => '', :_destroy => '1' } # this will be ignored
  ]
}}

member = Member.create(params['member'])
member.posts.length # => 2
member.posts.first.title # => 'Kari, the awesome Ruby documentation browser!'
member.posts.second.title # => 'The egalitarian assumption of the modern citizen'

mongoid 里也是这种格式http://mongoid.org/docs/relations/nested_attributes.html

简单说,就是你没用数组

@cqpx embeds_many 不知道有什么场景下比较适合,你能简单介绍一下吗?

我的参数是用 nested_form 插件生成的,里面会带一个 key,我可以试下官方格式的参数。

@cqpx 我把参数改成官方的格式还是不行。

a
 => {"policy"=>{"name"=>"aaaaaa", "buckets_attributes"=>[{"end_point"=>"4", "max"=>"4", "min"=>"1", "_destroy"=>"false", "start_point"=>"1"}, {"end_point"=>"11", "max"=>"10", "min"=>"7", "_destroy"=>"false", "start_point"=>"9"}], "min_device"=>"1", "max_device"=>"4"}} 
p = Policy.new a["policy"]
p.save
p.buckets
 => [#<Bucket _id: 21, policy_id: nil, end_point: 4, max: 4, _type: nil, min: 1, start_point: 1, _destroy: "false">, #<Bucket _id: 22, policy_id: nil, end_point: 11, max: 10, _type: nil, min: 7, start_point: 9, _destroy: "false">] 

在你需要 has_one/has_many 这样的从属关系,但是又不想再单独为被包含的对象建一个表的时候用。

比如 user has_one profile,profile 包括 name,address,phone 等等

你不想单独建一个 profile 表,但是全部作为 User 的属性又显得很乱,就可以把 profile 嵌入到 user 里面,实际上还在 user 表里,但是有了 has_one/has_many 这样的从属关系,逻辑上更清晰

https://github.com/chloerei/code_campo/blob/master/app/models/user.rb https://github.com/chloerei/code_campo/blob/master/app/models/profile.rb

淘宝的 User,UserCredit 也是这样的关系, http://open.taobao.com/doc/api_cat_detail.htm?cat_id=1&category_id=102 如果淘宝没用 mongodb 这样的文档型数据库,那么它就必须建一个 UserCredit 表, 用了 mongodb,就可以把 UserCredit 嵌入到 User 里

#4 楼 @yakjuly 为什么你的 policy 的_id 是数字不是 BSON?会不会跟这个有关? 我这里用 anaf 是可以的https://github.com/cqpx/yakjuly

@cqpx 我是用了 ruby-china 的插件

Mongoid 辅助插件

gem 'mongo-rails-instrumentation','0.2.4'

Mongoid 使用自增整形 ID

gem 'mongoid_auto_increment_id', "0.3.1"

初步接触 mongodb 还没有看他们插件的实现。 你这么一提醒 很可能是插件的实现有问题。

可以试试用 codecampo 实现自增 id 的方法看行不行,这个方法就不会覆盖 mongoid 默认的 BSON 格式,而是另外加一个 field 来数数 https://github.com/chloerei/code_campo/blob/master/app/models/user.rb https://github.com/chloerei/code_campo/blob/master/app/models/mongoid/number_id.rb

@cqpx

可以试试,看了下 number_id 解决了自增 id 的问题,但还是没有解决自动设置外键关联的问题。 如果可以增强一下 number_id 自动根据关联对象 给 外键 field 赋值,就比较好用了。

谢谢你,这么热心回答问题。

需要 登录 后方可回复, 如果你还没有账号请 注册新账号