设计after_sign_in_path_for不工作;当模型具有以下验证时被忽略:: update

时间:2018-02-21 19:13:40

标签: ruby-on-rails devise ruby-on-rails-5

我的方法正在执行,但Devise根本没有使用返回值。在登录页面上,它只是通过“成功登录”通知重新加载页面。它不会重定向到从方法返回的值。

日志

Started POST "/users/sign_in" for 127.0.0.1 at 2018-03-05 22:19:50 -0500
Processing by Users::SessionsController#create as HTML
  Parameters: {"utf8"=>"√", "authenticity_token"=>"tQd5a43StP85oyyCpEmFU8cAkFXdJL2OLpuAK1+sqQC6/rIqcd+fB2iE4RT0RoPKPCqreNBYlv2bxjl9gZFrWg==", "user"=>{"email"=>"test11@example.com", "password"=>"[FILTERED]", "remember_me"=>"0"}, "commit"=>"Log in"}
  User Load (2.0ms)  SELECT  "users".* FROM "users" WHERE "users"."email" = $1 ORDER BY "users"."id" ASC LIMIT $2  [["email", "test11@example.com"], ["LIMIT", 1]]
   (5.0ms)  BEGIN
  User Exists (3.0ms)  SELECT  1 AS one FROM "users" WHERE "users"."email" = $1 AND ("users"."id" != $2) LIMIT $3  [["email", "test11@example.com"], ["id", 23], ["LIMIT", 1]]
  Sector Load (0.0ms)  SELECT "sectors".* FROM "sectors" INNER JOIN "sectors_users" ON "sectors"."id" = "sectors_users"."sector_id" WHERE "sectors_users"."user_id" = $1  [["user_id", 23]]
  Region Load (0.0ms)  SELECT "regions".* FROM "regions" INNER JOIN "regions_users" ON "regions"."id" = "regions_users"."region_id" WHERE "regions_users"."user_id" = $1  [["user_id", 23]]
  Criterium Load (0.0ms)  SELECT "criteria".* FROM "criteria" INNER JOIN "criteria_users" ON "criteria"."id" = "criteria_users"."criterium_id" WHERE "criteria_users"."user_id" = $1  [["user_id", 23]]
  AssetType Load (0.0ms)  SELECT "asset_types".* FROM "asset_types" INNER JOIN "asset_types_users" ON "asset_types"."id" = "asset_types_users"."asset_type_id" WHERE "asset_types_users"."user_id" = $1  [["user_id", 23]]
  Company Load (1.0ms)  SELECT  "companies".* FROM "companies" WHERE "companies"."id" = $1 LIMIT $2  [["id", 42], ["LIMIT", 1]]
   (5.0ms)  ROLLBACK
############### /users/23/edit
  Rendering users/sessions/new.haml within layouts/application
  Rendered users/shared/_links.html.erb (3.0ms)
  Rendered users/sessions/new.haml within layouts/application (251.2ms)
  Rendered layouts/_footer.haml (15.0ms)
Completed 200 OK in 6554ms (Views: 3364.9ms | ActiveRecord: 86.1ms)

请注意,它正在渲染users/sessions/new.haml而不是编辑页面?

代码

class ApplicationController < ActionController::Base
...
  def after_sign_in_path_for(resource)
    logger.debug '############### ' + edit_user_path(resource) if resource.is_a?(User) && resource.signature.blank?
    return edit_user_path resource if resource.is_a?(User) && resource.signature.blank?
    stored_location_for(resource) ||
      if resource.is_a?(User)
        dashboard_path
      elsif resource.is_a?(Facilitator) && resource.name.nil?
        edit_facilitator_path resource
      elsif resource.is_a?(Facilitator)
        facilitator_path resource
      else
        super
      end
  end

我完全注释掉了该方法,但仍然重新加载了登录页面。

Started POST "/users/sign_in" for 127.0.0.1 at 2018-03-05 22:25:21 -0500
...
  Rendering users/sessions/new.haml within layouts/application

设计4.4.0

文档:

https://github.com/plataformatec/devise/wiki/How-To%3A-Redirect-to-a-specific-page-on-successful-sign-in-and-sign-out

http://www.rubydoc.info/github/plataformatec/devise/master/Devise/Controllers/Helpers:after_sign_in_path_for

我添加了

  def after_sign_in_path_for(resource)
    logger.debug '############# ' + resource.errors.full_messages.join(', ')

确实发现了验证错误,例如

 ############# Title can't be blank, Country can't be blank, Signature can't be blank, ...

但确实显示了通知

Signed in successfully.

我确实有一个会话,可以在其他地方导航。我的验证是on: :update

  validates :email, :name, :title, :phone, :address1, :city, :state, :zip, :country, :type, :signature, presence: true, on: :update

这不应导致登录行为错误。

我评论了模型上的所有验证,它确实有效,但这非常不寻常!验证不应影响登录行为。必须有一个解决方法。

Started POST "/users/sign_in" for 127.0.0.1 at 2018-03-05 23:11:43 -0500
  SQL (15.0ms)  UPDATE "users" SET "current_sign_in_at" = $1, "last_sign_in_at" = $2, "current_sign_in_ip" = $3, "sign_in_count" = $4, "updated_at" = $5 WHERE "users"."id" = $6  [["current_sign_in_at", "2018-03-06 04:11:44.225501"], ["last_sign_in_at", "2017-11-09 01:22:28.245231"], ["current_sign_in_ip", "127.0.0.1/32"], ["sign_in_count", 6], ["updated_at", "2018-03-06 04:11:44.230506"], ["id", 23]]
Redirected to http://localhost:3000/users/23/edit
Completed 302 Found in 2183ms (ActiveRecord: 48.0ms)

3 个答案:

答案 0 :(得分:2)

由于您只需要更新验证,我猜您只需要特定表单,因为即使没有此验证,您的用户仍然有效。 在这种情况下,我将使用一个所谓的表单对象,它为您执行更新验证并删除用户模型的更新验证。在这种情况下,您的验证不会影响您应用的其他部分。

只需使用ActiveModel,

Here就如何做到这一点是一个很好的指南。

示例:

应用程序/模型/ user.rb

class User < ApplicationRecord
  # remove the validations here
end

应用程序/形式/ user_edit_form.rb

class UserEditForm
    include ActiveModel::Model

    ATTRIBUTES = :email, :name, :title, :phone, 
                 :address1, :city, :state, :zip, 
                 :country, :type, :signature
    attr_accessor *ATTRIBUTES

    validates *ATTRIBUTES, presence: true

    def update(user)
      if valid?
        user.update(self.attributes)
      end
    end

    def self.for_user(user)
      new(user.slice(*ATTRIBUTES)
    end
  end

users_controller.rb

class UsersController
  def edit
    @user = User.find(params[:id])
    @user_edit_form = UserEditForm.for_user(@user)
  end

  def update
    @user = User.find(params[:id])
    @user_edit_form = UserEditForm.new(user_update_params).update(@user)
    if @user_edit_form.errors?
      render :edit
    else 
      redirect_to user_path(@user)
    end
  end

 def user_update_params
    # ...
 end
end

edit.html.erb

<%= form_for @user_edit_form, url: user_path(@user), method: :patch do |f| %>
  # ...

  <%= f.submit %>
<% end %>

<强>替代

另一种方法是将虚拟属性添加到模型中,并在用户控制器中有条件地运行验证。

class User < ApplicationRecord
  attr_accessor :profile_complete

  with_options if: -> { profile_complete } do
    validates :email, :name, :title, :phone, :address1, :city, :state, :zip, :country, :type, :signature, presence: true
  end
end

users_controller.rb

class UsersController < ApplicationController
  def update
    @user = User.find(params[:id])
    @user.profile_complete = true 
    if @user.update(user_update_params)
      redirect_to @user
    else 
      render :edit
    end

    # ...
  end
end

注意:您也可以使用真实数据库属性,而不是使用虚拟属性(attr_accessor),这样您实际上也可以知道哪些用户完全填写了他们的个人资料。

备选方案2

在其他一些项目中,我还使用了状态机宝石(有一对,例如aasmstatemachines-activerecord)来做类似的事情。一些状态机宝石甚至支持仅针对某些状态或转换进行验证。

答案 1 :(得分:-1)

请查看此文档https://github.com/plataformatec/devise/wiki/How-To:-redirect-to-a-specific-page-on-successful-sign-in。他们已经明确提到你什么时候进入循环和解决方案。检查上述文档中的防止重定向循环部分。

答案 2 :(得分:-1)

您可能需要在模型上进行条件验证。像这样:

 validates :email, :name, :title, :phone, :address1, :city, :state, :zip, :country, :type, :signature, presence: true, on: :update, unless: Proc.new {|user| user.current_sign_in_at.present? }

每当sign_in发生时,设计都会更新sign_in_at。这将触发更新操作和相关验证。

此外,文档说allow_nil: true指示模型仅在提交的表单上存在字段时验证字段。