我在尝试重置密码时会触发validation错误

我正在使用设计进行身份validation,并且在设置设计后我已经在users表中添加了一些其他字段。 用户只能通过输入电子邮件和密码进行注册,注册后用户可以编辑他的个人资料。 为此,我使用了:on => update。 但是现在当我试图重置密码validation时,触发错误就像名字不能空白而等等等等。 我正在使用设计和使用注册#edit来重置密码。 以下是我的用户模型。

class User  { :small => "150x150>", :medium => "300x300>", :large => "500x500>" } has_many :blogs, :dependent => :destroy has_many :comments, :dependent => :destroy has_many :followers, :through => :blogs has_many :followings, :class_name => 'Follower', :foreign_key => 'user_id' has_many :following_blogs, :through => :followings, :source => :blog has_many :blog_followers, :through => :followers, :source => :user phone_regex = /^[0-9]+$/ validates_attachment_size :photo, :less_than => 3.megabytes validates_attachment_content_type :photo, :content_type => ['image/jpeg','image/png','image/jpg','image/gif'] validates :name, :presence => true, :on => :update validates :dob, :presence => true, :on => :update validates :phone, :format => { :with => phone_regex }, :length => { :is => 10 }, :on => :update validates :address, :presence => true, :on => :update validates :state, :presence => true,:length => { :maximum => 30 }, :on => :update validates :city, :presence => true, :length => { :maximum => 30 }, :on => :update validates :country, :presence => true, :length => { :maximum => 30 }, :on => :update end 

实际上,问题是您正在使用可validation的设计模块。 即使在更新时,它也会对电子邮件和密码进行自动validation。 更好的选择是在创建时在电子邮件和密码上添加自己的validation,并在模型中进行这些更改或完全删除它。

 User Model def email_required? false end def password_changed? false end 

希望会有所帮助。

我遇到过同样的问题。 这是我找到的解决方案。

它在设计内部挖掘了一点,所以我建议你测试一下。 我发现在尝试实现这一点时我打破了很多东西(包括不需要匹配的密码确认,或者更改密码!)

这里有一个要点,显示我的测试套件的片段,用于检查密码重置: https : //gist.github.com/samstickland/9744ee29d0028162a7a8

问题是设计使用validation来检查密码匹配,它是有效密码(即不是太短)并且密码令牌有效。 这意味着一旦停止使用有效? 您必须寻找特定错误消息的方法。

首先,您必须覆盖用户模型中的reset_password方法。 如果错误消息不包含任何“密码”错误,则此新方法将直接写入加密密码(作为password =方法的一部分创建)。

最初的设计实现可以在这里找到: https : //github.com/plataformatec/devise/blob/18a8260535e5469d05ace375b3db3bcace6755c1/lib/devise/models/recoverable.rb#L39 (注意:我没有实现after_password_reset,因为它已被弃用)

  def reset_password(new_password, new_password_confirmation) self.password = new_password self.password_confirmation = new_password_confirmation if valid? save elsif errors.messages.select { |k,v| k[/password/] }.empty? # No password errors, so we write the password directly to the DB update_attribute(:encrypted_password, encrypted_password) true else false end end 

接下来,您必须实现自己的PasswordsController,因为更新方法还将检查错误是否为空。 将其更改为仅查找密码错误。 这是我的。

我还必须更改为response_with到redirect_to,以便在重置无效模型上的密码时使重定向工作。 我真的不明白为什么这是必要的。

 # # This is identical to the original devise password controller except # that it allows resetting of passwords in invalid models (ie # confirmed users without a valid profile # class Users::PasswordsController < Devise::PasswordsController # GET /resource/password/new # def new # super # end # POST /resource/password # def create # super # end # GET /resource/password/edit?reset_password_token=abcdef # def edit # super # end # PUT /resource/password def update self.resource = resource_class.reset_password_by_token(resource_params) yield resource if block_given? # Was: # if resource.errors.empty? # It's been changed to allow resetting passwords of invalid models if resource.errors.messages.select { |k,v| k[/password/] }.empty? resource.unlock_access! if unlockable?(resource) if Devise.sign_in_after_reset_password flash_message = resource.active_for_authentication? ? :updated : :updated_not_active set_flash_message(:notice, flash_message) if is_flashing_format? sign_in(resource_name, resource) # Was: # respond_with resource, location: after_resetting_password_path_for(resource) # but this didn't seem to redirect User's with invalid attributes so I've changed # it to a redirect redirect_to after_resetting_password_path_for(resource) else set_flash_message(:notice, :updated_not_active) if is_flashing_format? respond_with resource, location: new_session_path(resource_name) end else respond_with resource end end # protected # def after_resetting_password_path_for(resource) # super(resource) # end # The path used after sending reset password instructions # def after_sending_reset_password_instructions_path_for(resource_name) # super(resource_name) # end end 

我不确定是否有一个简单的答案。 我相信以某种方式迫使用户在注册后更新这些字段(如帐户“激活”)是个好主意。 从技术上讲,如果您已经定义了这些validation,则用户确实无效。

尽管如此,我猜您可以通过以下解决方法避免validation:添加:unless选项(请参阅此 )到validation:

 validates :name, :presence => true, :on => :update, :unless => :resetting_password? 

然后定义resetting_password? 当你这样做时,让它返回true。 您可以通过在devise的注册控制器中设置变量来完成此操作。 我认为使用attr_accessor可能就足够了。

可能有更好的解决方案,但这就是我现在能想到的。

基于Sam Stickland回答……尽管保存了update_attribute(:encrypted_password, encrypted_password)我认为使用save(validate: false)更好更明确。 此外,您可以避免扩展更新控制器操作实现您的重置密码,如

 def reset_password(new_password, new_password_confirmation) byebug if new_password.present? self.password = new_password self.password_confirmation = new_password_confirmation if valid? save elsif errors.messages.select { |k,_v| k[/password/] }.empty? errors.clear save(validate: false) else false end else errors.add(:password, :blank) false end end