【问题标题】:Saving join attributes through a has_many :through with :conditions通过 has_many :through 和 :conditions 保存连接属性
【发布时间】:2011-10-25 06:12:46
【问题描述】:

我有一个这样的 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

检索 features_songs 非常有效。这里的问题是我无法向艺术家添加新的 features_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,这是真正的问题。

【问题讨论】:

  • 尝试将:conditions 中的has_many 更改为:conditions =&gt; { :releases =&gt; {:featured =&gt; true} }
  • 谢谢你,但不幸的是它没有工作。结果还是["featured", nil]
  • 认为has_many 的概念也适用于此。看看api.rubyonrails.org/classes/ActiveRecord/Associations/…,如果你还没有,也许你会发现一些有用的东西。
  • FWIW,到目前为止,没有一个答案对我有用。 :-\

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


【解决方案1】:

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

编辑:

从下面的 cmets 很明显,我不完全理解您的要求。我以为您想知道如何从模型中创建和操作连接对象。当我最近需要这样做时,我有这两个模型:

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!

而且我的地址在我的用户上并且一直存在。这是你要找的吗?我会说我并不完全知道这如何有效地转化为多对多关联,但我假设它会是同一件事。也许是因为一对一的外键依赖于单个列,但是当它涉及与实际连接表的更复杂的关联时,它就不起作用了?至少你可以进一步调查这种可能性......

【讨论】:

  • 我没有说任何关于accepts_nested_sets 的事情。 Accepted_nested_attributes_for 允许您通过关联保存连接的对象。创建,在获取哈希时接受表单中的参数,但如果您手动使用创建,它的工作方式相同。如果您有一些进一步的见解可以让我直截了当,请务必分享。
  • 当然是accepted_nested_attributes_for。这与accepted_nested_attributes_for 无关。作者写道,condition 相关声明没有执行。这是真的。如果我们使用accepted_nested_attributes_for,它也不会被执行,因为它会调用相同的方法。
  • 如果您知道答案,请回答问题。你有一个比我更大的“单位”,我们都同意,所以现在告诉我们正确的答案是什么......
  • 所以我现在实际上正在做我自己的项目。 model_1.model_2.build({...}) 相当于创建一个...我显然不明白这与他想要的有什么不同。你能指出来吗?不知何故,你知道我错了,我想知道的不仅仅是“你错了”。当然,您必须有一些理由认为/知道这是错误的。
  • 对您的第二条评论进行后期编辑会更有帮助。谢谢你,我期待着明天收到你的来信:P
【解决方案2】:

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

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

您列出的属性被更新的原因是它们是歌曲对象的一部分。您尝试更新的字段是特色字段,它是您的releases 连接表的一部分。

accepts_nested_attributes_for是解决更新嵌套关系问题的工具。如果您想在创建歌曲对象期间添加特色属性(如您在示例中所做的那样),则需要将其添加为 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 的选项中。

【讨论】:

  • 只是为了确认两件事: 1- accepts_nested_attributes_for :releases 应该只进入歌曲模型吗?换句话说,我实际使用(艺术家)的模型不接受嵌套属性(似乎有点奇怪?) 2-我尝试了您提供的代码并收到此错误:NoMethodError: undefined method 'to_i' for :featured:Symbol
  • 对于#1 - 是的。当您获取 Artist 实例(您的 a 变量)并调用 .songs 时,您实际上是在返回 ArraySong 对象。试试a.songs.first.class,你会看到一首歌。实际上,a.songs 返回一个 ActiveRelation 对象,但 Rails 通过 ruby​​ 的力量向你隐藏了这个事实(即尝试a.songs.class)。不要链接create,而是尝试仅使用标题和用户参数链接方法build。然后,您可以在控制台中玩耍并在保存之前检查对象。
  • 对于#2 - 你能发布一个堆栈跟踪吗?我可能把其中一些方法的复数搞混了,或者其他的东西可能有问题。
  • 考虑一下,对于#2,我可能给了你错误的参数:releases_attributes 参数(上面编辑过)。它需要包含在一个数组中,因为您可以在一次调用中添加多个版本。仅当您要更新现有记录时,才需要 id 参数。
  • 是的!谢谢你..这让事情正常/保存!但是有一种我不明白的疯狂的事情——INSERT 发生了两次......看看这个:pastie.org/private/dsubllg5jxnclvirzdr5g
【解决方案3】:

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

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

attr_accessible :song_id, :feature_id

【讨论】:

    【解决方案4】:

    我在阅读后找到了可行的解决方案:

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

    诀窍是创建一个包含条件的新一阶 has_many 关联,然后在 that 上运行 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
    

    【讨论】:

    • 我认为应该是 has_many :featured_songs, :through => :featured_releases, :source => :song
    猜你喜欢
    • 1970-01-01
    • 2019-10-29
    • 2012-09-18
    • 2013-09-18
    • 1970-01-01
    • 2012-03-10
    • 2013-08-14
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多