通过has_many:through with:conditions保存连接属性

时间:2011-08-11 18:42:12

标签: ruby-on-rails activerecord data-modeling relationship has-many-through

我有一个看起来像这样的Artist模型:

# app/models/artist.rb
class Artist < ActiveRecord::Base
  # Relationships
  has_many  :releases
  has_many  :songs, :through => :releases
  has_many  :featured_songs,  :through => :releases,
                              :class_name => "Song",
                              :source => :song,
                              :conditions => { 'releases.featured', true }                         

end

检索featured_songs非常有效。这里的问题是我无法向艺术家添加新的feature_song,因为出于某种原因,'featured'属性被设置为'nil'。

这就是我的尝试:

ruby-1.9.2-p180 :004 > a = Artist.first
ruby-1.9.2-p180 :005 > a.featured_songs.create(:title => "Title", :user => User.first)

实际结果是:

ruby-1.9.2-p180 :004 > a = Artist.first
ruby-1.9.2-p180 :005 > a.featured_songs.create(:title => "Title", :user => User.first)
  User Load (0.9ms)  SELECT `users`.* FROM `users` LIMIT 1
  SQL (1.0ms)  BEGIN
  SQL (5.5ms)  INSERT INTO `songs` (`created_at`, `title`, `updated_at`, `user_id`) VALUES (?, ?, ?, ?)  [["created_at", Thu, 11 Aug 2011 18:30:34 UTC +00:00], ["title", "Title"], ["updated_at", Thu, 11 Aug 2011 18:30:34 UTC +00:00], ["user_id", 1]]
  SQL (1.2ms)  INSERT INTO `releases` (`album_id`, `artist_id`, `created_at`, `featured`, `song_id`, `updated_at`) VALUES (?, ?, ?, ?, ?, ?)  [["album_id", nil], ["artist_id", 1], ["created_at", Thu, 11 Aug 2011 18:30:34 UTC +00:00], ["featured", nil], ["song_id", 6], ["updated_at", Thu, 11 Aug 2011 18:30:34 UTC +00:00]]
   (0.1ms)  COMMIT

请注意:["featured", nil]

知道我做错了什么吗?如何直接在我的连接上设置属性而不直接访问它?

谢谢你!

编辑: 为了使我的问题更清楚:

  • 从艺术家的实例中,我无法通过featured_songs关系创建新的精选歌曲

  • 保存似乎是设置所有歌曲属性除外(最重要的一个)featured

  • featured属性由于某种原因被设置为nil,这是真正的问题。

4 个答案:

答案 0 :(得分:2)

我在阅读完这篇文章后找到了工作解决方案:

https://github.com/rails/rails/issues/5178#issuecomment-4181551

诀窍是创建一个包含条件的新的一阶has_many关联,然后在 上运行has_many:through。

所以,在你的情况下,它将是:

class Artist < ActiveRecord::Base
  # Relationships
  has_many  :releases
  has_many  :songs, :through => :releases

  has_many  :featured_releases, :class_name => "Release", :conditions => { :featured => true }
  has_many  :featured_songs, :through => :featured_releases, :source => :song
end

答案 1 :(得分:0)

编辑:更新:releases_attributes以反映has_many关系。

杰德尔的回答是正确的。这是accepts_nested_attributes_for解决的问题。 has_many仅描述了一种查询记录的机制,而不是更新或创建它们。

您列出的属性正在更新的原因是因为它们是歌曲对象的一部分。您要更新的字段是特色字段,它是releases联接表的一部分。

accepts_nested_attributes_for是解决更新嵌套关系问题的工具。如果要在创建歌曲对象期间添加feature属性(就像在示例中那样),则需要将其添加为Song类的嵌套属性的一部分。

class Song < ActiveRecord::Base
  has_many :releases
  has_many :artists, :through => :releases
  accepts_nested_attributes_for :releases
end

您的create看起来像这样:

a.songs.create(:title => 'Title', :user => User.first, :releases_attributes => [{ :id => 123, :featured => true}])

注意添加_attributes。如果您使用嵌套表单,这正是Rails在视图中的作用。有关证据,请参阅ActiveRecord源文件nested_attributes.rb

如果您真的想要一种更简洁的方法来实现这一目标,您可以添加一个Song#create_featured方法,将:releases_attributes哈希值合并到#create的选项中。

答案 2 :(得分:0)

允许通过关联保存的关键是为关联模型上的字段提供attr_accessible。

在这种情况下,在发布模型上添加一行:

attr_accessible :song_id, :feature_id

答案 3 :(得分:-1)

您可以在模型中使用accepts_nested_attributes_for。我首先看一下关于该主题的Railscast,然后仔细阅读documentation本身的方法

编辑:

从下面的评论中可以清楚地看出,我并不完全明白你的要求。我你想知道如何从模型中创建和操作连接对象。当我最近需要这样做时,我有这两个模型:

class User < ActiveRecord::Base
  belongs_to :address

  accepts_nested_attributes_for :address
end

class Address < ActiveRecord::Base
  has_one :user
end

然后我可以这样做以在用户上创建一个新地址

u = User.new
u.address.build({:city => "bozoville", :zip => "22222", ...})
u.save!

我的地址在我的用户身上并且仍然存在。这是你在找什么?我会说,我并不完全有效地将其转化为多对多关联,但我认为这将是同样的事情。也许是因为一对一依赖于单个列的外键有效,但是当它涉及与实际连接表的更复杂的关联时,它不起作用?至少你可以进一步研究这种可能性......