如何仅引用活动记录关联中的持久记录
在许多控制器的编辑方法中,您初始化一个新对象并编辑现有对象
class MagazinesController < ApplicationController def edit @magazine = Magazine.find(params[:magazine_id]) @page = Page.find(params[:id]) @new_page = @magazine.pages.new end end
但是在视图中,您通常需要循环遍历持久化对象并单独处理新对象
# magazines#edit %h4 Existing pages - @magazine.pages.each do |page| %p= link_to page, page.title
问题
…是pages
关联既包含现有(持久)页面,也包含我们通过@new_page = @magazine.pages.new
的新页面。
这很容易处理,但它很难看
%h4 Existing pages - @magazine.pages.each do |page| - if page.persisted? %p= link_to page, page.title
我想使用一些关联方法来只选择那些持久化的页面:
%h4 Existing pages - @magazine.pages.persisted.each do |page| %p= link_to page, page.title
有没有办法做到这一点?
您可以在页面模型中创建一个persisted
作用域: scope :persisted, -> { where "id IS NOT NULL" }
,这可以避免在每个关联页面上进行迭代,以检查它是否是新记录。
来自@ Florent2和@CDub的建议都是合理的。 然而,@ florent2的建议意味着再次访问数据库(并且可能放弃任何我不想做的预设急切加载)和@CDub的建议在代码方面没有完全解决。 这是我最终的目的:
仅返回特定关联的持久记录
class Magazine < ActiveRecord::Base has_many :pages do def persisted collect{ |page| page if page.persisted? } end end end
这允许您在与Magazine关联的页面的任何ActiveRecord关系上调用.persisted
。 它不会再次访问数据库,因为它只是过滤预先加载的对象,返回持久化的对象。
使代码可重用
由于我想定期重用这段代码,我可以把它拉到一个模块中
module PersistedExtension def persisted select{|item| item if item.persisted?} end end
然后可以使用lambda将其包含在关联方法中:
class Magazine < ActiveRecord::Base # ... has_many :pages, -> { extending PersistedExtension } end
我可以直观地称呼它:
@magazine = Magazine.first @magazine.pages.persisted # => array of pages which are persisted # the new persisted association extension works on any AR result set @magazine.pages.order('page ASC').persisted
你总是可以拒绝新记录的页面……
%h4 Existing pages - @magazine.pages.persisted.each do |page| %p= link_to page, page.title
你在Page
有什么:
def self.persisted reject {|page| page.new_record? } end
我以不同方式处理这个问题 我不在控制器中创建新对象,而是直接在表单中创建。
首先,要通过控制器运行,为什么要将page_id作为主要params[:id]
传递给Magazines控制器? 在我看来你想要这个:
class MagazinesController < ApplicationController def edit @magazine = Magazine.find(params[:id]).includes(:pages) end end
然后,在你的magazines#edit
view中,你会这样做:
%h4 Existing pages - @magazine.pages.each do |page| %p= link_to page, page.title = form_for @magazine do |f| = f.fields_for :pages, @magazine.pages.build do |builder| = builder.text_field :title # etc.
在那个fields_for
行中,你要求页面的杂志表单字段,然后告诉它只渲染特定的新页面的字段,你正在使用@magazine.pages.build
创建它。
参考文献:
fields_for
嵌套模型表格Railscast (另见第2部分)
Rails 4和5回答:
只需将此代码放入初始化程序( config/initializers
目录中的文件,扩展名为.rb
):
module MyApp module ActiveRecordExtensions extend ActiveSupport::Concern class_methods do def persisted select(&:persisted?) end end end end ActiveSupport.on_load :active_record do include MyApp::ActiveRecordExtensions end
您现在可以在任何模型和关联上调用persisted
。