【问题标题】:Add association (<<) without committing to database添加关联(<<)而不提交到数据库
【发布时间】:2012-11-02 14:36:15
【问题描述】:

是否可以在 Rails 中将关联添加到现有记录而不立即将此更改提交到数据库? 例如。如果我有 Post has_many :tags

post.tags << Tag.first

这将立即提交到数据库。我尝试了其他方法而不是

post.tags.build name: "whatever"

我认为这在 Rails 中有点不一致,在某些情况下,有一个选项会很有用。

换句话说,我想要

post.tags << Tag.first # don't hit the DB here!
post.save # hit the DB here!

【问题讨论】:

  • 在父对象上使用build 有什么问题?您能否更详细地解释您认为不一致的地方和/或您希望它在理想世界中的样子? build 似乎是为此目的“构建”(如果你会原谅双关语) - 添加一个子/关联对象而不立即提交它。
  • 构建工作正常,但在尝试将关联添加到现有记录时却不行。我没有看到类似的方法来构建以将关联添加到现有记录(我的意思是这里的现有子记录)。如果现在更清楚,我更新了这个问题。
  • 我想理想情况下不破坏现有代码我想要类似 post.tags.build id: Tag.first.id 的东西。想不出更好的语义,因为
  • 不,你不能拥有你想要的语法。 &lt;&lt; 提交更改
  • 这不是我想要的语法,而是功能。我知道

标签: ruby-on-rails ruby-on-rails-3


【解决方案1】:

这应该适用于 Rails 3.2 和 Rails 4:

post.association(:tags).add_to_target(Tag.first)

请参阅此要点:https://gist.github.com/betesh/dd97a331f67736d8b83a

请注意,保存父项会保存子项,并且在保存之前不会设置 child.parent_id。

编辑 2015 年 12 月 6 日: 对于多态记录:

post.association(:tags).send(:build_through_record, Tag.first)
# Tested in Rails 4.2.5

【讨论】:

  • post.association(:tags).send(:build_through_record, Tag.first) 在 4.2.9 上不适合我,但 post.association(:tags).add_to_target(Tag.first) 效果很好
  • @inye 这是多态关联吗?
  • 那么 add_to_target 应该可以工作,而 build_through_record 不应该,正如您所观察到的那样。
【解决方案2】:
post_tag = post.post_tags.find_or_initialize_by_tag_id(Tag.first.id)
post_tag.save

【讨论】:

  • 它确实有效。我已经用代码的正确用法更新了答案。 Rails 4 有不同的语法 find_or_initialize_by(attr_hash)
【解决方案3】:

添加到 Isaac 的答案中,post.association(:tags).add_to_target(Tag.first) 适用于 has_many 关系,但您可以将 post.association(:tag).replace(Tag.first, false) 用于 has_one 关系。第二个参数(false)告诉它不要保存;如果您将参数留空,它将默认将其提交到数据库。

【讨论】:

    【解决方案4】:

    PREFACE 这并不完全是对这个问题的回答,但是搜索这种种类功能的人可能会发现这很有用。在大肆将其投入生产环境之前,请仔细考虑此选项和其他选项。

    在某些情况下,您可以利用 has_one 关系来获得您想要的。同样,在使用它之前,请认真考虑您要完成的工作。

    需要考虑的代码 您有一个从 TrunkBranchhas_many 关系,并且您想添加一个新分支。

    class Trunk
      has_many :branches
    end
    
    class Branch
      belongs_to :trunk
    end
    

    我还可以将它们单独关联起来。我们将在Trunk 中添加has_one 关系

    class Trunk
      has_many :branches
      has_one :branch
    end
    

    此时,您可以执行Tree.new.branch = Branch.new 之类的操作,并且您将建立一个不会立即保存的关系,但在保存后,可以从Tree.first.branches 获得。

    然而,当新开发人员查看代码并思考“好吧,到底应该是一个还是多个?”时,这会给新开发人员带来相当混乱的情况。

    为了解决这个问题,我们可以与scope 建立更合理的has_one 关系。

    class Trunk
      has_many :branches
    
      # Using timestamps
      has_one :newest_branch, -> { newest }, class_name: 'Branch'
    
      # Alternative, using ID. Side note, avoid the railsy word "last"
      has_one :aftmost_branch, -> { aftmost }, class_name: 'Branch'
    end
    
    class Branch
      belongs_to :trunk
    
      scope :newest, -> { order created_at: :desc }
      scope :aftmost, -> { order id: :desc }
    end
    

    注意这一点,但它可以完成 OP 中要求的功能。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2020-06-12
      • 1970-01-01
      • 2012-03-08
      • 1970-01-01
      • 1970-01-01
      • 2014-08-21
      • 2013-03-26
      • 1970-01-01
      相关资源
      最近更新 更多