使用has_many:通过和构建

rm-*_*-rf 9 ruby ruby-on-rails has-many-through

我有三个模型,全部用于has_many:通过关系.它们看起来像这样:

class Company < ActiveRecord::Base

  has_many :company_users, dependent: :destroy
  has_many :users, through: :company_users

  accepts_nested_attributes_for :company_users, :users

end

class CompanyUser < ActiveRecord::Base
  self.table_name = :companies_users #this is because this was originally a habtm relationship
  belongs_to :company
  belongs_to :user
end

class User < ActiveRecord::Base
  # this is a devise model, if that matters

  has_many :company_users, dependent: :destroy
  has_many :companies, through: :company_users

  accepts_nested_attributes_for :company_users, :companies

end
Run Code Online (Sandbox Code Playgroud)

这个加载很好,并且连接可以很好地用于查询.但是,每当我做某事时

@company = Company.last
@user = @company.users.build(params[:user])

@user.save    #=> true
@company.save #=> true
Run Code Online (Sandbox Code Playgroud)

无论是User记录和CompanyUser记录生成,但company_id在现场CompanyUser记录设置NULL

INSERT INTO `companies_users` (`company_id`, `created_at`,`updated_at`, `user_id`) 
VALUES (NULL, '2012-02-19 02:09:04', '2012-02-19 02:09:04', 18)
Run Code Online (Sandbox Code Playgroud)

当你这样做时,它会做同样的事情 @company.users << @user

我敢肯定我在这里做了些蠢事,我只是不知道是什么.

Mau*_*res 14

你不能使用has_many:就像那样,你必须这样做:

@company = Company.last
@user    = User.create( params[:user] ) 
@company.company_users.create( :user_id => @user.id )
Run Code Online (Sandbox Code Playgroud)

然后,您将正确定义关联.

更新

对于下面的评论,由于您已经有accepts_nested_attributes_for,您的参数必须如下所示:

{ :company => 
    { :company_users_attributes => 
        [ 
          { :company_id => 1, :user_id => 1 } ,
          { :company_id => 1, :user_id => 2 },
          { :company_id => 1, :user_id => 3 } 
        ]
    } 
}
Run Code Online (Sandbox Code Playgroud)

您可以自动为用户添加用户.

  • @ company.users.create(params [:user])也可以工作(至少在Rails 4中)。由于某种原因,`@ user = @ company.users.build(params [:user]); @ user.save`并未为我设置关联。 (2认同)

Kar*_*ant 6

如果你有一个has_many :through关联并且你想使用build它来保存一个关联,你可以使用:inverse_ofJoin Model 中的belongs_to 关联上的选项来完成这个

这是来自 rails docs 的修改示例,其中标签具有 has_many :through 与帖子的关联,并且开发人员正在尝试使用以下build方法通过连接模型 (PostTag) 保存标签:

@post = Post.first
@tag = @post.tags.build name: "ruby"
@tag.save

Run Code Online (Sandbox Code Playgroud)

普遍的期望是最后一行应该保存连接表(post_tags)中的“通过”记录。但是,默认情况下这不起作用。这仅在 :inverse_of 设置时才有效

class PostTag < ActiveRecord::Base
  belongs_to :post
  belongs_to :tag, inverse_of: :post_tags # add inverse_of option
end

class Post < ActiveRecord::Base
  has_many :post_tags
  has_many :tags, through: :post_tags 
end

class Tag < ActiveRecord::Base
  has_many :post_tags
  has_many :posts, through: :post_tags  
end

Run Code Online (Sandbox Code Playgroud)

所以对于上面的问题,belongs_to :user像这样在 Join Model (CompanyUser)的关联上设置 :inverse_of 选项:

class CompanyUser < ActiveRecord::Base
  belongs_to :company
  belongs_to :user, inverse_of: :company_users
end

Run Code Online (Sandbox Code Playgroud)

将导致以下代码在连接表(company_users)中正确创建记录

company = Company.first
company.users.build(name: "James")
company.save
Run Code Online (Sandbox Code Playgroud)

来源:这里这里

  • 你想得到什么? (2认同)

nkm*_*nkm 5

我怀疑你的params[:user]参数,否则你的代码看起来很干净.We can use build method with 1..n and n..n associations too,看到这里.

我建议你首先确保你的模型关联工作正常,为此打开console并尝试以下方法,

> company = Company.last
=> #<Tcompany id: 1....>
> company.users
=> []
> company.users.build(:name => "Jake")
=> > #<User id: nil, name: "Jake">
> company.save
=> true
Run Code Online (Sandbox Code Playgroud)

现在,如果记录保存得很好,请调试传递给构建方法的参数.

快乐调试:)