未定义的方法`错误' for nil:传递User对象

时间:2018-01-11 16:08:42

标签: ruby-on-rails ruby

我的应用程序有用户帐户,用户可以登录查看应用程序。当管理员创建用户时,会生成随机密码,如果这是他们第一次登录,则会提示用户更改此密码。这是我的SessionsController:

class SessionsController < ApplicationController

  def new
  end

  def create
    user = User.find_by(user_name: params[:session][:user_name].to_s.downcase)
    if user && user.authenticate(params[:session][:password])
      # Log the user in and redirect to the user's show page.
      if user.created_at == user.updated_at
        # If this is a first time user, then they need to change their password.
        log_in user



        # This user object is correct at this point, but after this point it is nil.
        redirect_to change_password_path(user)
      else
        log_in user
        redirect_to root_path # Redirects to the main page (parts page)
      end
    else
      # Create an error message.
      flash.now[:danger] = 'Invalid User Name/Password combination'
      render 'new'
    end
  end

  def destroy
    log_out
    redirect_to login_path
  end

end

在重定向到change_password_path之前,用户对象不是nil。以下是重定向的路线。

Rails.application.routes.draw do

  get 'sessions/new'

  resources :parts
  resources :users
  root "parts#index"

  get    '/change_password', to: 'users#first_time_password_change'
  get    '/login',   to: 'sessions#new'
  post   '/login',   to: 'sessions#create'
  delete '/logout',  to: 'sessions#destroy'

end

重定向用户以更改其密码后,用户对象为nil并在_change_password.html.erb处抛出错误。这是错误和堆栈跟踪:

undefined method `errors' for nil:NilClass

app/views/users/_change_password.html.erb:2:in `block in _app_views_users__change_password_html_erb__3382781422752759150_70150174196820'
actionview (5.1.4) lib/action_view/helpers/capture_helper.rb:39:in `block in capture'
actionview (5.1.4) lib/action_view/helpers/capture_helper.rb:203:in `with_output_buffer'
actionview (5.1.4) lib/action_view/helpers/capture_helper.rb:39:in `capture'
actionview (5.1.4) lib/action_view/helpers/form_helper.rb:758:in `form_with'
app/views/users/_change_password.html.erb:1:in `_app_views_users__change_password_html_erb__3382781422752759150_70150174196820'
actionview (5.1.4) lib/action_view/template.rb:157:in `block in render'
activesupport (5.1.4) lib/active_support/notifications.rb:168:in `instrument'
actionview (5.1.4) lib/action_view/template.rb:352:in `instrument_render_template'
actionview (5.1.4) lib/action_view/template.rb:155:in `render'
actionview (5.1.4) lib/action_view/renderer/partial_renderer.rb:342:in `block in render_partial'
actionview (5.1.4) lib/action_view/renderer/abstract_renderer.rb:42:in `block in instrument'
activesupport (5.1.4) lib/active_support/notifications.rb:166:in `block in instrument'
activesupport (5.1.4) lib/active_support/notifications/instrumenter.rb:21:in `instrument'
activesupport (5.1.4) lib/active_support/notifications.rb:166:in `instrument'
actionview (5.1.4) lib/action_view/renderer/abstract_renderer.rb:41:in `instrument'
actionview (5.1.4) lib/action_view/renderer/partial_renderer.rb:331:in `render_partial'
actionview (5.1.4) lib/action_view/renderer/partial_renderer.rb:310:in `render'
actionview (5.1.4) lib/action_view/renderer/renderer.rb:47:in `render_partial'
actionview (5.1.4) lib/action_view/helpers/rendering_helper.rb:35:in `render'
app/views/users/first_time_password_change.html.erb:1:in `_app_views_users_first_time_password_change_html_erb___4022541670546187761_70150174275000'
actionview (5.1.4) lib/action_view/template.rb:157:in `block in render'
activesupport (5.1.4) lib/active_support/notifications.rb:168:in `instrument'
actionview (5.1.4) lib/action_view/template.rb:352:in `instrument_render_template'
actionview (5.1.4) lib/action_view/template.rb:155:in `render'
actionview (5.1.4) lib/action_view/renderer/template_renderer.rb:52:in `block (2 levels) in render_template'
actionview (5.1.4) lib/action_view/renderer/abstract_renderer.rb:42:in `block in instrument'
activesupport (5.1.4) lib/active_support/notifications.rb:166:in `block in instrument'
activesupport (5.1.4) lib/active_support/notifications/instrumenter.rb:21:in `instrument'
activesupport (5.1.4) lib/active_support/notifications.rb:166:in `instrument'
actionview (5.1.4) lib/action_view/renderer/abstract_renderer.rb:41:in `instrument'
actionview (5.1.4) lib/action_view/renderer/template_renderer.rb:51:in `block in render_template'
actionview (5.1.4) lib/action_view/renderer/template_renderer.rb:59:in `render_with_layout'
actionview (5.1.4) lib/action_view/renderer/template_renderer.rb:50:in `render_template'
actionview (5.1.4) lib/action_view/renderer/template_renderer.rb:14:in `render'
actionview (5.1.4) lib/action_view/renderer/renderer.rb:42:in `render_template'
actionview (5.1.4) lib/action_view/renderer/renderer.rb:23:in `render'
actionview (5.1.4) lib/action_view/rendering.rb:103:in `_render_template'
actionpack (5.1.4) lib/action_controller/metal/streaming.rb:217:in `_render_template'
actionview (5.1.4) lib/action_view/rendering.rb:83:in `render_to_body'
actionpack (5.1.4) lib/action_controller/metal/rendering.rb:52:in `render_to_body'
actionpack (5.1.4) lib/action_controller/metal/renderers.rb:141:in `render_to_body'
actionpack (5.1.4) lib/abstract_controller/rendering.rb:24:in `render'
actionpack (5.1.4) lib/action_controller/metal/rendering.rb:36:in `render'
actionpack (5.1.4) lib/action_controller/metal/instrumentation.rb:44:in `block (2 levels) in render'
activesupport (5.1.4) lib/active_support/core_ext/benchmark.rb:12:in `block in ms'
/usr/local/rvm/rubies/ruby-2.4.1/lib/ruby/2.4.0/benchmark.rb:308:in `realtime'
activesupport (5.1.4) lib/active_support/core_ext/benchmark.rb:12:in `ms'
actionpack (5.1.4) lib/action_controller/metal/instrumentation.rb:44:in `block in render'
actionpack (5.1.4) lib/action_controller/metal/instrumentation.rb:87:in `cleanup_view_runtime'
activerecord (5.1.4) lib/active_record/railties/controller_runtime.rb:29:in `cleanup_view_runtime'
actionpack (5.1.4) lib/action_controller/metal/instrumentation.rb:43:in `render'
actionpack (5.1.4) lib/action_controller/metal/implicit_render.rb:33:in `default_render'
actionpack (5.1.4) lib/action_controller/metal/basic_implicit_render.rb:4:in `block in send_action'
actionpack (5.1.4) lib/action_controller/metal/basic_implicit_render.rb:4:in `tap'
actionpack (5.1.4) lib/action_controller/metal/basic_implicit_render.rb:4:in `send_action'
actionpack (5.1.4) lib/abstract_controller/base.rb:186:in `process_action'
actionpack (5.1.4) lib/action_controller/metal/rendering.rb:30:in `process_action'
actionpack (5.1.4) lib/abstract_controller/callbacks.rb:20:in `block in process_action'
activesupport (5.1.4) lib/active_support/callbacks.rb:131:in `run_callbacks'
actionpack (5.1.4) lib/abstract_controller/callbacks.rb:19:in `process_action'
actionpack (5.1.4) lib/action_controller/metal/rescue.rb:20:in `process_action'
actionpack (5.1.4) lib/action_controller/metal/instrumentation.rb:32:in `block in process_action'
activesupport (5.1.4) lib/active_support/notifications.rb:166:in `block in instrument'
activesupport (5.1.4) lib/active_support/notifications/instrumenter.rb:21:in `instrument'
activesupport (5.1.4) lib/active_support/notifications.rb:166:in `instrument'
actionpack (5.1.4) lib/action_controller/metal/instrumentation.rb:30:in `process_action'
actionpack (5.1.4) lib/action_controller/metal/params_wrapper.rb:252:in `process_action'
activerecord (5.1.4) lib/active_record/railties/controller_runtime.rb:22:in `process_action'
actionpack (5.1.4) lib/abstract_controller/base.rb:124:in `process'
actionview (5.1.4) lib/action_view/rendering.rb:30:in `process'
actionpack (5.1.4) lib/action_controller/metal.rb:189:in `dispatch'
actionpack (5.1.4) lib/action_controller/metal.rb:253:in `dispatch'
actionpack (5.1.4) lib/action_dispatch/routing/route_set.rb:49:in `dispatch'
actionpack (5.1.4) lib/action_dispatch/routing/route_set.rb:31:in `serve'
actionpack (5.1.4) lib/action_dispatch/journey/router.rb:50:in `block in serve'
actionpack (5.1.4) lib/action_dispatch/journey/router.rb:33:in `each'
actionpack (5.1.4) lib/action_dispatch/journey/router.rb:33:in `serve'
actionpack (5.1.4) lib/action_dispatch/routing/route_set.rb:834:in `call'
rack (2.0.3) lib/rack/etag.rb:25:in `call'
rack (2.0.3) lib/rack/conditional_get.rb:25:in `call'
rack (2.0.3) lib/rack/head.rb:12:in `call'
rack (2.0.3) lib/rack/session/abstract/id.rb:232:in `context'
rack (2.0.3) lib/rack/session/abstract/id.rb:226:in `call'
actionpack (5.1.4) lib/action_dispatch/middleware/cookies.rb:613:in `call'
activerecord (5.1.4) lib/active_record/migration.rb:556:in `call'
actionpack (5.1.4) lib/action_dispatch/middleware/callbacks.rb:26:in `block in call'
activesupport (5.1.4) lib/active_support/callbacks.rb:97:in `run_callbacks'
actionpack (5.1.4) lib/action_dispatch/middleware/callbacks.rb:24:in `call'
actionpack (5.1.4) lib/action_dispatch/middleware/executor.rb:12:in `call'
actionpack (5.1.4) lib/action_dispatch/middleware/debug_exceptions.rb:59:in `call'
web-console (3.5.1) lib/web_console/middleware.rb:135:in `call_app'
web-console (3.5.1) lib/web_console/middleware.rb:20:in `block in call'
web-console (3.5.1) lib/web_console/middleware.rb:18:in `catch'
web-console (3.5.1) lib/web_console/middleware.rb:18:in `call'
actionpack (5.1.4) lib/action_dispatch/middleware/show_exceptions.rb:31:in `call'
railties (5.1.4) lib/rails/rack/logger.rb:36:in `call_app'
railties (5.1.4) lib/rails/rack/logger.rb:24:in `block in call'
activesupport (5.1.4) lib/active_support/tagged_logging.rb:69:in `block in tagged'
activesupport (5.1.4) lib/active_support/tagged_logging.rb:26:in `tagged'
activesupport (5.1.4) lib/active_support/tagged_logging.rb:69:in `tagged'
railties (5.1.4) lib/rails/rack/logger.rb:24:in `call'
sprockets-rails (3.2.1) lib/sprockets/rails/quiet_assets.rb:13:in `call'
actionpack (5.1.4) lib/action_dispatch/middleware/remote_ip.rb:79:in `call'
request_store (1.4.0) lib/request_store/middleware.rb:19:in `call'
actionpack (5.1.4) lib/action_dispatch/middleware/request_id.rb:25:in `call'
rack (2.0.3) lib/rack/method_override.rb:22:in `call'
rack (2.0.3) lib/rack/runtime.rb:22:in `call'
activesupport (5.1.4) lib/active_support/cache/strategy/local_cache_middleware.rb:27:in `call'
actionpack (5.1.4) lib/action_dispatch/middleware/executor.rb:12:in `call'
actionpack (5.1.4) lib/action_dispatch/middleware/static.rb:125:in `call'
rack (2.0.3) lib/rack/sendfile.rb:111:in `call'
railties (5.1.4) lib/rails/engine.rb:522:in `call'
puma (3.11.0) lib/puma/configuration.rb:225:in `call'
puma (3.11.0) lib/puma/server.rb:624:in `handle_request'
puma (3.11.0) lib/puma/server.rb:438:in `process_client'
puma (3.11.0) lib/puma/server.rb:302:in `block in run'
puma (3.11.0) lib/puma/thread_pool.rb:120:in `block in spawn_thread'

以下是first_time_password_change.html.erb

<%= render 'change_password', user: @user %>
<%= logger.info "FUDGE " + @user.class.to_s %>
<div class="row">
    <div class="col-md-6 col-md-offset-3">
        <%= link_to 'Show', @user %> |
        <%= link_to 'Back', users_path %>
    </div>
</div>

这是_change_password.html.erb

<%= form_with(model: user, local: true) do |form| %>
  <% if user.errors.any? %>
    <div id="error_explanation">
      <h2 style="color: red;"><%= pluralize(user.errors.count, "error") %> prohibited this user from being saved:</h2>
      <ul>
      <% user.errors.full_messages.each do |message| %>
        <li style="color: red;"><%= message %></li>
      <% end %>
      </ul>
    </div>
  <% end %>

<div class="row">
  <div class="col-md-6 col-md-offset-3">
    <h1>Change Password</h1>
    <div class="control-group">
      <%= form.label :first_name, class: "control-label" %>
      <label style="font-size: 20px; font-weight:normal;">&nbsp;&nbsp;&nbsp;<%= @user.first_name %></label>
    </div><br />

    <div class="control-group">
      <%= form.label :last_name, class: "control-label" %>
      <label style="font-size: 20px; font-weight:normal;">&nbsp;&nbsp;&nbsp;<%= @user.last_name %></label>
    </div><br />

    <div class="control-group">
      <%= form.label :user_name, class: "control-label" %>
      <label style="font-size: 20px; font-weight:normal;">&nbsp;&nbsp;<%= @user.user_name %></label>
    </div><br />

    <div class="control-group">
      <%= form.label :email, class: "control-label" %>
      <label style="font-size: 20px; font-weight:normal;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<%= @user.email %></label>
    </div><br />

    <div class="control-group">
      <%= form.label :privledges, class: "control-label"%>
      <label style="font-size: 20px; font-weight:normal;">&nbsp;&nbsp;<%= @user.privledges %></label>
    </div><br />

    <div class="control-group">
      <%= form.label :new_password, class: "control-label" %>
      <%= form.text_field :password, id: :password, class: 'form-control', value: "" %>
    </div><br />

    <div class="control-group">
      <%= form.label :confirm_password, class: "control-label" %>
      <%= form.text_field :confirm_password, id: :confirm_password, class: 'form-control' %>
    </div><br />

    <div class="actions">
      <%= form.submit 'Change Password', class: "btn btn-primary" %>
    </div>
    <br />

  </div>
</div>
<% end %>

这是UserController

require 'rubygems'
require 'random_password_generator'

class UsersController < ApplicationController
  # Check that the person accessing the application is logged in:
  before_action :authenticate_user!
  before_action :set_user, only: [:show, :edit, :update, :destroy]

  # GET /users
  # GET /users.json
  def index
    @users = User.all
  end

  # GET /users/1
  # GET /users/1.json
  def show
  end

  # GET /users/new
  def new
    @user = User.new
  end

  # GET /users/1/edit
  def edit
  end

  # GET /changepassword
  def first_time_password_change
  end

  # POST /users
  # POST /users.json
  def create
    @user = User.new(user_params)
    @user.password = RandomPasswordGenerator.generate(10, :skip_symbols => true).to_s
    respond_to do |format|
      if @user.save 
        format.html { redirect_to @user, notice: 'User was successfully created.' }
        format.json { render :show, status: :created, location: @user }
      else
        format.html { render :new }
        format.json { render json: @user.errors, status: :unprocessable_entity }
      end
    end
  end

  # PATCH/PUT /users/1
  # PATCH/PUT /users/1.json
  def update
    password_is_valid = password_update_is_valid(@user)
    respond_to do |format|
      if password_is_valid
        if @user.update(user_params)
          format.html { redirect_to @user, notice: 'User was successfully updated.' }
          format.json { render :show, status: :ok, location: @user }
        else
          format.html { render :edit }
          format.json { render json: @user.errors, status: :unprocessable_entity }
        end 
      else
        format.html { render :edit, notice: '' }
        format.json { render json: @user.errors, status: :unprocessable_entity }
      end
    end
  end

  # DELETE /users/1
  # DELETE /users/1.json
  def destroy
    @user.destroy
    respond_to do |format|
      format.html { redirect_to users_url, notice: 'User was successfully destroyed.' }
      format.json { head :no_content }
    end
  end

  private 
  def password_update_is_valid(user)
    is_valid = true
    user_pass = params[:user][:password]
    user_conf = params[:user][:confirm_password]
    # Check if the parameters were passed as nil objects
    if user_pass.nil? or user_conf.nil?
      is_valid = false
      user.errors[:base] << "Please fill both the Password and Confirm Password fields."
    else
      # Check if the password and confirmation passwords are empty
      if user_pass == "" or user_conf == ""
        user.errors[:base] << "Please fill both the Password and Confirm Password fields."
        is_valid = false
      end
      # Check if the password length is less than 5 characters long
      if user_pass.length < 5
        user.errors[:base] << "Password must be at least 5 characters long."
        is_valid = false
      end
      # Check if the password and confirm password fields match.
      if user_pass != user_conf
        user.errors[:base] << "Password and Confirm Password must match."
        is_valid = false
      end
    end
    return is_valid
  end

  private
    def set_user
      @user = User.find(params[:id])
    end

    def user_params
      params.require(:user).permit(:first_name, :last_name, :user_name, :email, :password, :privledges, :password_digest)
    end

end

我对rails有点新,但控制台有一些奇怪的输出。正在呈现first_time_change_password.html.erb,然后是_change_password.html.erb,这是预期的。奇怪的是,first_time_change_password.html.erb再次呈现并发生错误。

tarted GET "/change_password.21" for 107.213.96.34 at 2018-01-11 15:48:10 +0000
Cannot render console from 107.213.96.34! Allowed networks: 127.0.0.1, ::1, 127.0.0.0/127.255.255.255
Processing by UsersController#first_time_password_change as 
  User Load (0.1ms)  SELECT  "users".* FROM "users" WHERE "users"."id" = ? LIMIT ?  [["id", 21], ["LIMIT", 1]]
  Rendering users/first_time_password_change.html.erb within layouts/application
  Rendered users/_change_password.html.erb (4.6ms)
  Rendered users/first_time_password_change.html.erb within layouts/application (8.1ms)
Completed 500 Internal Server Error in 17ms (ActiveRecord: 0.1ms)



ActionView::Template::Error (undefined method `errors' for nil:NilClass):
    1: <%= form_with(model: user, local: true) do |form| %>
    2:   <% if user.errors.any? %>
    3:     <div id="error_explanation">
    4:       <h2 style="color: red;"><%= pluralize(user.errors.count, "error") %> prohibited this user from being saved:</h2>
    5:       <ul>

app/views/users/_change_password.html.erb:2:in `block in _app_views_users__change_password_html_erb__3382781422752759150_70150174196820'
app/views/users/_change_password.html.erb:1:in `_app_views_users__change_password_html_erb__3382781422752759150_70150174196820'
app/views/users/first_time_password_change.html.erb:1:in `_app_views_users_first_time_password_change_html_erb___4022541670546187761_70150174275000'

我对rails有点新,我正在研究网站开发,所以请怜悯我。

另外,我在Cloud 9上运行Rails 5.1.4。

感谢所有愿意花时间看这个问题的人,对所有代码感到抱歉。

3 个答案:

答案 0 :(得分:0)

在之前的UsersController行动中 before_action :set_user, only: [:show, :edit, :update, :destroy] 添加first_time_password_change方法。 before_action :set_user, only: [:show, :edit, :update, :destroy, :first_time_password_change] 由于未在该方法中设置@user,因此用户为零。

答案 1 :(得分:0)


   由于您要求解释,您看到undefined method 'errors' for nil:NilClass,因为user对象实际上是nil。当您在<%= render 'change_password', user: @user %>的行动@user中未设置first_time_password_change时,所有内容都会启动。如果用户已登录,则Devise会提供帮助current_user。由于您仅对authenticated before_action :authenticate_user!用户current_user启用此操作,因此建议您使用@user而不是设置另一个实例current_user。您可以直接在部分中使用<script> function myFunction() { var x = document.getElementById("mydiv"); if (x.style.display === "none") { x.style.display = "none"; } else { x.style.display = "block"; } } </script> ,而无需在本地传递。希望这会有所帮助。

答案 2 :(得分:0)

首先,这不是服务器端验证,这称为客户端验证,这种匹配的用户数据不会通过验证创建数据

<强>登录

允许用户使用她/他的有效电子邮件/用户名和密码登录。通过匹配数据库中的电子邮件/用户名和密码来进行身份验证过程,仅当给定信息与记录值成功匹配时,才允许用户访问受保护的操作。如果没有,用户将再次被重定向到登录页面。

这不是通过模型验证向数据库创建数据这是与数据匹配的数据库,该数据库在创建会话以保存id之后已经存在。

这就是为什么在删除此代码后,您只需要为登录表单删除此客户端错误代码

<% if user.errors.any? %>
    <div id="error_explanation">
      <h2 style="color: red;"><%= pluralize(user.errors.count, "error") %> prohibited this user from being saved:</h2>
        <ul>
            <% user.errors.full_messages.each do |message| %>
                <li style="color: red;"><%= message %></li>
            <% end %>
        </ul>
    </div>
<% end %>

添加闪存部分,其中包含您的登录错误,如下所示

<% flash.each do |name, msg| %>
    <section class="<%="#{name}" %>-message-section"> 
        <div class="container"> 
            <div class="row"> 
                <div class="col-md-12"> 
                    <div class="alert-message <%="#{name}" %>-message">
                        <p><%= msg if msg.is_a?(String) %></p>
                        <i class="icofont icofont-close"></i>
                    </div>
                </div>
            </div>
        </div>
    </section>
<% end %>

希望能提供帮助