是否有任何 Rails 函数来检查部分是否存在?

当我呈现一个不存在的部分时,我会得到一个异常。我想在渲染它之前检查部分是否存在,如果它不存在,我将渲染其他内容。我在。Erb 文件,但我认为应该有一个更好的方法来做到这一点:

    <% begin %>
<%= render :partial => "#{dynamic_partial}" %>
<% rescue ActionView::MissingTemplate %>
Can't show this data!
<% end %>
33899 次浏览

I was struggling with this too. This is the method I ended up using:

<%= render :partial => "#{dynamic_partial}" rescue nil %>

Basically, if the partial doesn't exist, do nothing. Did you want to print something if the partial is missing, though?

Edit 1: Oh, I fail at reading comprehension. You did say that you wanted to render something else. In that case, how about this?

<%= render :partial => "#{dynamic_partial}" rescue render :partial => 'partial_that_actually_exists' %>

or

<%= render :partial => "#{dynamic_partial}" rescue "Can't show this data!" %>

Edit 2:

Alternative: Checking for existence of the partial file:

<%= render :partial => "#{dynamic_partial}" if File.exists?(Rails.root.join("app", "views", params[:controller], "_#{dynamic_partial}.html.erb")) %>

From inside a view, template_exists? works, but the calling convention doesn't work with the single partial name string, instead it takes template_exists?(name, prefix, partial)

To check for partial on path: app/views/posts/_form.html.slim

Use:

lookup_context.template_exists?("form", "posts", true)

Currently, I'm using the following in my Rails 3/3.1 projects:

lookup_context.find_all('posts/_form').any?

The advantage over other solutions I've seen is that this will look in all view paths instead of just your rails root. This is important to me as I have a lot of rails engines.

This also works in Rails 4.

In Rails 3.2.13, if you're in a controller, you can use this :

template_exists?("#{dynamic_partial}", _prefixes, true)

template_exists? is delegated to lookupcontext, as you can see in AbstractController::ViewPaths

_prefixes gives the context of the controller's inheritance chain.

true because you're looking for a partial (you can omit this argument if you want a regular template).

http://api.rubyonrails.org/classes/ActionView/LookupContext/ViewPaths.html#method-i-template_exists-3F

I know this has been answered and is a million years old, but here's how i ended up fixing this for me...

Rails 4.2

First, i put this in my application_helper.rb

  def render_if_exists(path_to_partial)
render path_to_partial if lookup_context.find_all(path_to_partial,[],true).any?
end

and now instead of calling

<%= render "#{dynamic_path}" if lookup_context.find_all("#{dynamic_path}",[],true).any? %>

i just call <%= render_if_exists "#{dynamic_path}" %>

hope that helps. (haven't tried in rails3)

I have used this paradigm on many occasions with great success:

<%=
begin
render partial: "#{dynamic_partial}"
rescue ActionView::MissingTemplate
# handle the specific case of the partial being missing
rescue
# handle any other exception raised while rendering the partial
end
%>

The benefit of the code above is that we can handle tow specific cases:

  • The partial is indeed missing
  • The partial exists, but it threw an error for some reason

If we just use the code <%= render :partial => "#{dynamic_partial}" rescue nil %> or some derivative, the partial may exist but raise an exception which will be silently eaten and become a source of pain to debug.

What about your own helper:

def render_if_exists(path, *args)
render path, *args
rescue ActionView::MissingTemplate
nil
end

This works for me in Rails 6.1:

<% if lookup_context.exists?("override_partial", ['path/after/app/views'], true) %>
<%= render partial: "path/after/app/views/override_partial" %>
<% else %>
<%= render partial: "default_partial" %>
<% end %>

Here I have my partial nested some levels deeper than normal (app/views/path/after/app/views/_override_partial) so that's why I'm adding it as the prefixes array, but you can use lookup_context.prefixes instead if you don't need it.

I could have also used prepend_view_path on the controller. It's up to you :)