【问题标题】:Rails undefined method `model_name' for NilClass:Class用于 NilClass:Class 的 Rails 未定义方法“model_name”
【发布时间】:2015-07-08 02:40:05
【问题描述】:

我有一个表格,我想出现在每个页面的顶部,所以我将它包含在 /app/views/layouts/application.html.erb 文件中,我收到错误 undefined methodmodel_name' NilClass:Class` 尝试加载页面时。

这是 application.html.erb 中的 sn-p 格式

 <%= form_for @user do |f| %>
    <h3>Add new contact</h3>
    <%= f.text_field :first_name %><br />
    <%= f.text_field :last_name %>
    <%= f.text_field :email %>
    <hr />
    <%= f.submit "Add Contact" %>
<% end %>

这是我的 /app/controllers/user_controller.rb

class UserController < ApplicationController

    def index

    end

    def new
        @user = User.new
    end

end

我在想我遇到了这个错误,因为由于表单在 application.html.erb 文件中,我需要以某种方式指定路径,但是我对 rails 还是很陌生。

如果您需要发布任何其他内容,请告诉我。

编辑 按照 Ola 的建议,在我的 app/views/layouts/application.html.erb 文件中,我有这样的内容:

<div>
<%= yield :add_user %>
</div>

在我的 app/views/users/new.html.erb 文件中我有这个

<% content_for :add_user do %>
    <%= form_for @user do |f| %>
        <h3>Add new contact</h3>
        First Name<br />
        <%= f.text_field :first_name %><br />
        Last Name<br />
        <%= f.text_field :last_name %><br />
        Email<br />
        <%= f.text_field :email %>
        <hr />
        <%= f.submit "Add Contact" %>
    <% end %>
<% end %>

表单未呈现,因为我的网址是 http://localhost:3000/admin/index,因此它正在 app/views/admin/index.html.erb 中寻找 add_user content_for

【问题讨论】:

  • 我认为“尝试加载页面时”您的意思是 /users
  • 对不起,我正在加载这个页面http://localhost:3000/admin/index
  • 是的,我也这么认为,在上面的示例中 /users/new 应该可以工作(有点),尽管您的视图结构完全错误 - 请参阅下面的答案。
  • 恐怕你也把yield 指令颠倒过来了——相信我,我知道当你第一次遇到问题时,所有这些东西是多么令人困惑。 将呈现当前操作的输出,具体取决于您所在的页面。非常适合当您希望每个控制器/操作设置某些通用页面元素的内容(通常用于设置文档标题)但您实际上想要完全相反的情况 - 在每个页面上呈现相同的某些组件。为此,您应该在模板中使用&lt;%= render :partial =&gt; "thing/partial" %&gt;

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


【解决方案1】:

如果表单包含在多个操作(页面)中,您需要将@user 设置为某个值,否则form_for 指令将失败(它不会有任何内容来创建表单for )。

更好的方法(也是 Rails 的默认设置)是为每个操作设置单独的视图,每个视图仅包含该操作所需的元素,并使用 &lt;%= yield %&gt; 加载到您的 application.html.erb 布局中。所以你会有一个app/views/users/new.html.erb 视图,其中包含表单。你从不很少需要在 Rails 中定义任何加载路径,它们都来自模型、控制器和动作名称——或者来自你的routes.rb。这是 convention over configuration 范式的核心部分,它在 Rails 中运行得如此之深。

编辑:如果您确实需要有一个用于在每个页面上创建新对象的表单(例如,通常用于 UserSessions),您可以重写您的 @987654329 @ 使其不依赖于存在的对象:

form_for(User.new)

或者如果您需要强制它发布到create 方法

form_for(User.new, :url => { :action => "create" }) 

您可以在Ruby On Rails API docs. 中阅读更多关于资源驱动的form_for

【讨论】:

  • 因此将表单放入 app/views/users/new.html.erb 会加载页面,但现在表单不会出现。我已详细更新了我的问题。
  • 是的,因为我不知道您的需求是什么,我只是假设您有一个标准的 CRUD 页面结构 - 如果您确实需要在用户范围之外的页面上显示表单,请参阅上面的编辑.
  • 在这种情况下,我会将表单放在部分视图中(比如/app/views/users/_inline_form.hmtl.erb)并使用&lt;%= render :partial =&gt; 'users/inline_form' %&gt; 将其加载到application.html.erb 布局中(注意没有前导“_”) .
  • 刚刚这样做,现在它又给了我一个 undefined_model 名称错误。
  • 如果我尝试您建议的其他方式和用户 form_for(User.new),我会收到 undefined method users_path'` 错误。
【解决方案2】:

您是否先尝试调试?我问是因为你说你是 Rails 的新手。如果没有,这是我的方法。我想分享它,因为我很惊讶有多少人(无论出于何种原因)不仔细调试。

(我创建了一个测试应用来解决您的问题。首先,我访问了http://localhost:3000/users。)

首先,查看堆栈跟踪。

Users#index 中的 NoMethodError

显示 /Users/david/dev/testapp/app/views/layouts/application.html.erb 其中第 11 行 提出:

NilClass:Class 的未定义方法 `model_name' 提取的源代码(第 11 行附近):

8: </head>
9: <body>
10: 
11: <%= form_for @user do |f| %>
12:     <h3>Add new contact</h3>
13:     <%= f.text_field :first_name %><br />
14:     <%= f.text_field :last_name %>

这告诉您代码中的第 11 行是问题所在。想想 - 可能是什么问题?路由不太可能,因为您只是在呈现页面。不是语法错误,这会触发另一个错误。好的,请继续阅读:

Rails.root:/Users/david/dev/testapp

应用程序跟踪 |框架跟踪 |全程跟踪

现在点击“framework trace”,你会看到:

activemodel (3.2.6) lib/active_model/naming.rb:163:in model_name_from_record_or_class' activemodel (3.2.6) lib/active_model/naming.rb:158:inparam_key' actionpack (3.2.6) lib/action_view/helpers/form_helper.rb:369:in `form_for'

你可能不明白这一切——很少有人明白——但你会看到错误来自model_name_from_record_or_class,来自param_key,来自form_for。这是一个很大的线索!

我建议使用open_gem gem 以便于挖掘源代码:

gem install open_gem

现在,看看 activemodel,因为它是堆栈跟踪中最顶层的宝石:

gem open activemodel

现在,在启用正则表达式搜索的情况下进行搜索:

def .*model_name_from_record_or_class

(为什么?你想找到类方法——例如以self开头的定义。)

这会将您带到naming.rb 中的第 162 行。

def self.model_name_from_record_or_class(record_or_class)
  (record_or_class.is_a?(Class) ? record_or_class : convert_to_model(record_or_class).class).model_name
end

即使不了解所有这些,它也确实给出了一个很大的提示......该方法试图从记录或类中返回模型名称。为什么不能呢?因为您传递的参数@user 尚未设置。 unset 实例变量的值是 nil,所以在你尝试对它做某事之前它不会抱怨。

我希望这对这个问题和未来的问题有所帮助。

【讨论】:

    【解决方案3】:

    这个答案实际上只是 Someth 的答案更正,但解决方案是为每个控制器操作设置 @user,而不仅仅是与特定控制器相关的操作。这可以通过在 ApplicationController 中使用 before_filter 来完成,即

    class ApplicationController < ActionController::Base
      before_filter :initialize_user
    
      def initialize_user
        @user = User.new
      end
    end
    

    OR 通过在 form_for 帮助器本身中创建一个新模型(注意:这在关注点分离方面很丑陋,但它有效......)

     <%= form_for User.new do |f| %>
       <h3>Add new contact</h3>
       <%= f.text_field :first_name %><br />
       <%= f.text_field :last_name %>
       <%= f.text_field :email %>
       <hr />
       <%= f.submit "Add Contact" %>
     <% end %>
    

    【讨论】:

      【解决方案4】:

      您的问题是您试图为页面上的每个操作添加表单,但没有在所有操作中初始化用户。尝试在所有操作中初始化用户:

      class UserController < ApplicationController
          before_filter :initialize_user
      
          def index
      
          end
      
          def initialize_user
              @user = User.new
          end
      
      end
      

      【讨论】:

      • 那行不通——因为他在application.hmtl.erb 中有表单,所以他需要将代码放在 ApplicationController 中,否则 UserController 范围之外的任何页面仍然会给出错误。但那是学术性的;他的视图结构很混乱,应该重构!
      • Ola 很到位。过滤器应该在 ApplicationController 中,因为它应该在每个页面上执行。
      【解决方案5】:

      在用户控制器中:

      def index
         @user = User.new
      end
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-12-30
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多