Rails在提交空表单时不返回验证错误

问题描述:

简而言之:我有一个设计模型用户我为用户添加一个选项,以便能够在其个人档案页面中更改其密码,而无需通过“忘记密码”选项。总之,因为这需要有领域的另一种形式::old_password, :new_password and :new_password_confirmation未原先模型,我不得不为他们创造新的独立验证,Rails在提交空表单时不返回验证错误

形式:

<%= form_for(@user, :url => {:controller => :members, :action => :editpass}, :html => {:method => :post}) do |f| %> 

    <input name="authenticity_token" type="hidden" value="<%= form_authenticity_token %>"> 
    <table class="tables no-border"> 
     <tr> 
      <%= f.label :old_password, "Old password" %> 
     </tr> 
     <tr> 
      <%= f.password_field :old_password, :autofocus => :true %> 
     </tr> 
     <tr> 
      <%= f.label :new_password, "New password" %> 
     </tr> 
     <tr> 
      <%= f.password_field :new_password %> 
     </tr> 
     <tr> 
      <%= f.label :new_password_confirmation, "Repeat new password" %> 
     </tr> 
     <tr> 
      <%= f.password_field :new_password_confirmation %> 
     </tr> 
     <tr> 
      <input type="submit" value="Change" class="btn" /> 
     </tr> 
    </table> 
</form> 

    <%= @user.errors.full_messages %> 

<% end %> 

控制器:

class MembersController < ApplicationController 

    before_filter :authenticate_user! 
    skip_before_filter :check_for_main 

    def index 
    @users = User.all 
    end 

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

    def editpass 

    current_user.change_password(params[:old_password], params[:new_password]) 
    redirect_to member_path(current_user.id) 

    end 

    # User access restriction 
    def ban 
    end 

    def unban 
    end 



end 

型号:查看:验证领域:old_password /:new_password ..

class User < ActiveRecord::Base 

    # Virtual attributes 
    # _password attributes serve in-profile password change and are not part of the model 
    attr_accessor :login, :apiid, :vcode, :old_password, :new_password, :new_password_confirmation 

    # Include default devise modules. Others available are: 
    # :token_authenticatable, :confirmable, 
    # :lockable, :timeoutable and :omniauthable 
    devise :database_authenticatable, :registerable, :confirmable, 
      :recoverable, :rememberable, :trackable, :validatable, :authentication_keys => [:login] 

    # Registration show/edit form validation 
    validates :username, :presence => true, 
          :length  => { :minimum => 6, :maximum => 255 }, 
          :uniqueness => true 

    validates :apiid,  :presence => true, 
          :numericality => { :only_integer => true }, 
          :acc_api => true, 
          :on   => :create 

    validates :vcode,  :presence => true, 
          :length  => { :minimum => 20, :maximum => 255 }, 
          :on   => :create 

    # In profile password 
    validates :old_password, :presence => true, 
           :length  => { :minimum => 8, :maximum => 255 }, 
           :if   => :password_changed? 

    validates :new_password, :presence => true, 
           :length  => { :minimum => 8, :maximum => 255 }, 
           :if   => :password_changed?, 
           :confirmation => true 

    validates :new_password_confirmation, 
           :presence => true 


    attr_accessible :login, :username, :group, :apiid, :vcode, :email, :password, :password_confirmation, :remember_me 

    # Register character belonging to user 
    after_create :register_characters 

    # Model association 
    has_many :apis 
    has_many :characters 

    # Allows user to reset password in profile 
    # for Forgot Password look at Devise 
    def change_password(oldpass, newpass) 
     if self.valid_password?(oldpass) 
      # User is logged out automatically by devise upon 
      # password change 
      self.password = newpass 
      self.save 
     else 
      return false 
     end 
    end 

    # Register account characters 
    def register_characters 
     require 'nokogiri' 
     require 'open-uri' 

     # Fetch account level xml 
     uri = "https://api.eveonline.com/account/Characters.xml.aspx?keyID=#{self.apiid}&vCode=#{self.vcode}" 
     xml = Nokogiri::XML(open(uri)) 
     row = xml.xpath("//row") 

     # Create characters bound to user 
     row.each do |entry| 
      # Register new character 
      character = Character.new(
       :charid  => entry['characterID'].to_i, 
       :user_id => self.id, 
       :name  => entry['name'], 
       :corp  => entry['corporationName'] 
       ) 
      character.save 

      # Register character associated api credentials 
      api = Api.new(
       :user_id  => self.id, 
       :character_id => character.id, 
       :apiid   => self.apiid, 
       :vcode   => self.vcode 
       ) 
      api.save 

      character.update_character 
     end 
    end 

    # Check if user is banned before login 
    def active_for_authentication? 
     super && self.banned == false 
    end 

    # Redefine authentication procedure to allow login with username or email 
    def self.find_for_database_authentication(warden_conditions) 
     conditions = warden_conditions.dup 
     if login = conditions.delete(:login).downcase 
     where(conditions).where("username = '#{login}' OR email = '#{login}'").first 
     else 
     where(conditions).first 
     end 
    end 

end 

基本上,你的密码验证不被达到。

使用change_password方法,您只是将值从控制器传递到该方法并使用它们来设置password。该模型没有看到old_passwordnew_password被设置,所以它不能验证它们。另外,由于您没有通过password_confirmation并直接在模型中设置密码(self.password = newpass),所以也没有使用password_confirmation

正如在另一个答案中提到的,你最好使用update_attributes或Devise文档中推荐的任何东西。希望我的解释能够为您的方法不起作用添加一些洞见。

如果您想坚持您的change_password方法,您必须将值传递给属性,以便它们可以通过模型进行验证。尝试是这样的:

#controller 

    @user = current_user 

    if @user.change_password(params[:old_password], params[:new_password], params[:new_password_confirmation]) 
    redirect_to member_path(current_user.id) 
    else 
    @errors = @user.errors.full_messages 
    render 'edit_password_view' # not sure what this action is named 
    # you now have an array of errors (@errors) that you can render however you see fit in the view 
    end 


    #model 
    def change_password(oldpass, newpass, newpass_conf) 
    self.password = newpass 
    self.old_password = oldpass 
    self.new_password = newpass 
    self.new_password_confirmation = newpass_conf 
    return self.save 
    end 
+0

我之所以说是因为如果我提交表单只有那些密码字段我将获得的用户名,VCODE,apiid验证错误.. – 2013-03-27 21:44:39

+0

看着深入到你的代码,更新了我的答案。希望有所帮助。 – aguynamedloren 2013-03-27 21:54:19

+0

change_mathod不是它工作的问题。问题是与old_password/new_password等。我如何设置这些来验证它们? – 2013-03-27 21:56:17

我认为正确的方法是更新用户属性,而不是使用更改密码。也许这个指南能有所帮助:

https://github.com/plataformatec/devise/wiki/How-To:-Allow-users-to-edit-their-password