在许多控制器的编辑方法中,您初始化一个新对象并编辑现有对象
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
有没有办法做到这一点?
答案 0 :(得分:12)
您可以在页面模型中创建persisted
范围:scope :persisted, -> { where "id IS NOT NULL" }
,这可以避免在每个关联页面上进行迭代,以检查它是否是新记录。
答案 1 :(得分:3)
来自@ 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
答案 2 :(得分:2)
您可以随时拒绝新记录的页面...
%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
答案 3 :(得分:1)
我以不同的方式处理这个问题。我不在控制器中创建新对象,而是直接在表单中创建。
首先,要通过控制器运行,为什么要将page_id作为主要params[:id]
传递给Magazines控制器?在我看来你想要这个:
class MagazinesController < ApplicationController
def edit
@magazine = Magazine.find(params[:id]).includes(:pages)
end
end
然后,在magazines#edit
视图中,您可以执行此操作:
%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
行中,您要求为页面设置杂志表单字段,然后告诉它仅渲染特定新页面的字段,您正在使用{{{ 1}}。
参考文献:
fields_for
Nested Model Form Railscast(另见第2部分)
答案 4 :(得分:1)
另一种更简洁的语法,使用ActiveRecord where.not
并仍返回ActiveRecord
集合:
- @magazine.pages.where.not(id: nil).each do |page|
...
答案 5 :(得分:0)
只需将此代码放入初始值设定项(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
。