web-dev-qa-db-ja.com

パスワードを必要とせずに「ユーザー」属性を更新する

現在、検証は次のように設定されているため、ユーザーはパスワードを入力せずに一部の属性を編集できます。

validates :password, :presence =>true, :confirmation => true, :length => { :within => 6..40 }, :on => :create
validates :password, :confirmation => true, :length => { :within => 6..40 }, :on => :update, :unless => lambda{ |user| user.password.blank? } 

ただし、ユーザーがこれを行うと、パスワードが削除されます。update_attributesはパスワードを「」に更新しています。これが私の更新定義です:

def update

    if @user.update_attributes(params[:user])
        flash[:success] = "Edit Successful."
        redirect_to @user
    else
        @title = "Edit user"
        render 'edit'
    end
end

また、代わりにupdate_attributeを使用する別の定義を使用してみました。

def save_ff
    @user = User.find(params[:id])
    @user.update_attribute(:course1, params[:user][:course1] )
    @user.update_attribute(:course2, params[:user][:course2] )
    @user.update_attribute(:course3, params[:user][:course3] )
    @user.update_attribute(:course4, params[:user][:course4] )
    redirect_to @user 
end 

しかし、何らかの理由で、これは同じことをしています。パスワードを変更せずに一部のユーザー属性を更新するにはどうすればよいですか?ありがとう!

27
steffi2392

昨日あなたに与えた解決策がこの問題につながるとは思いもしませんでした。ごめんなさい。

さて、インスピレーションを得て deviseから 、あなたは単にあなたのコントローラーをこのように更新するべきです:

def update
  params[:user].delete(:password) if params[:user][:password].blank?
  if @user.update_attributes(params[:user])
    flash[:success] = "Edit Successful."
    redirect_to @user
  else
    @title = "Edit user"
    render 'edit'
  end
end
22
apneadiving

このブログ投稿 あなたがやりたいことの原則を示しています。

表示されていませんが、役立つ場合がありますが、モデルにアクセサーを追加することです。

attr_accessor   :new_password, :new_password_confirmation
attr_accessible :email, :new_password, :new_password_confirmation

そして、ユーザーが新しいパスワードを提供したという条件の下で、必要なすべての検証を提供します。

  validates :new_password,  :presence => true, 
                            :length   => { :within => 6..40 }, 
                            :confirmation => true, 
                            :if       => :password_changed?

最後に、「password_changed?」かどうかを判断するために、encrypted_pa​​sswordが設定されているかどうかを確認するチェックを追加します。新しいレコードにパスワードを要求するため。

  def password_changed?
    !@new_password.blank? or encrypted_password.blank?
  end
12
cbedrosian

私はこれに苦労し、しばらくの間円を描いて回っていたので、ここにRails 4のソリューションを置くと思いました。

私がこれまでに見た答えはどれも私のユースケースを満たしていません。それらはすべて何らかの方法で検証をバイパスすることを含んでいるようですが、他のフィールドとパスワード(存在する場合)も検証できるようにしたいと思います。また、私は自分のプロジェクトでdeviseを使用していないので、それに特有のものを利用することはできません。

それは2つの部分の問題であることを指摘する価値があります:

ステップ1-コントローラーでパスワードが空白の場合は、強力なパラメーターからパスワードと確認フィールドを削除する必要があります。

if myparams[:password].blank?
  myparams.delete(:password)
  myparams.delete(:password_confirmation)
end

ステップ2-パスワードが入力されていない場合にパスワードが検証されないように、検証を変更する必要があります。空白に設定したくないので、以前にパラメーターから削除しました。

私の場合、これは私のモデルの検証としてこれを持っていることを意味します:

validates :password, :presence => true, :confirmation => true, length: {minimum: 7}, :if => :password

:if =>:passwordに注意してください-パスワードが設定されていないかどうかのチェックをスキップします。

10
Richard W
# It smells

def update
  if params[:user][:password].blank?
    params[:user].delete :password
    params[:user].delete :password_confirmation
  end

  if @user.update_attributes(params[:user])
    flash[:success] = "Edit Successful."
    redirect_to @user
  else
    @title = "Edit user"
    render 'edit'
  end
end

# Refactoring

class User < ActiveRecord::Base
  ...
  def update_attributes(params)
    if params[:password].blank?
      params.delete :password
      params.delete :password_confirmation
      super params
    end
  end
  ...
end

def update
  if @user.update_attributes(params[:user])
    flash[:success] = "Edit Successful."
    redirect_to @user
  else
    @title = "Edit user"
    render 'edit'
  end
end

# And little better

class User < ActiveRecord::Base
  ...
  def custom_update_attributes(params)
    if params[:password].blank?
      params.delete :password
      params.delete :password_confirmation
      update_attributes params
    end
  end
  ...
end

def update
  if @user.custom_update_attributes(params[:user])
    flash[:success] = "Edit Successful."
    redirect_to @user
  else
    @title = "Edit user"
    render 'edit'
  end
end
5
fl00r

私も同じ問題を抱えていましたが、上記の解決策はうまくいきませんでした。私の場合、本当の原因を見つけました。ユーザーモデルにencrypt_passwordコールバックがあり、毎回パスワードを空白に設定していました。

before_save:encrypt_password

このコールバックの最後に条件を追加して修正しました。

before_save:encrypt_password、:unless => Proc.new {| u | u.password.blank? }

4
James L.

2017年の回答:

Rails 5では、Michael Hartlの tutorial でも示されているように、モデルのこれらの線に沿って何かがあるだけで十分です。

validates :password, presence: true, length: { minimum: 6 }, allow_nil: true

allow_nil:trueは、ユーザーがパスワードの変更も必要とせずに自分の情報を編集できるようにするここでのキーです。

この時点で、これにより空のユーザーサインアップも可能になると考えるかもしれません。ただし、これは、パスワードの存在を自動的に検証するhas_secure_passwordを使用することで防止されますが、createメソッドのみが検証されます。

これは、説明を目的としたデモユーザーモデルです。

class User < ApplicationRecord
  attr_accessor :remember_token
  before_save { self.email = email.downcase }
  validates :name, presence: true, length: { maximum: 50 }
  VALID_EMAIL_REGEX = /\A[\w+\-.]+@[a-z\d\-.]+\.[a-z]+\z/i
  validates :email, presence: true, length: { maximum: 255 },
                format: { with: VALID_EMAIL_REGEX },
                uniqueness: { case_sensitive: false }
  has_secure_password
  validates :password, presence: true, length: { minimum: 6 }, allow_nil: true
  .
  .
  .
end

私はこれをデバイスで行う方法がわかりません。私の2セント。

3
kstratis

正解はRails 4では機能しなくなりました。私の答えは最もクリーンで最も用途が広いはいつでも機能すると思いますany属性(パスワードだけでなく)を省略したい。このアプローチは、モデルの個別の属性をさまざまな場所で更新する場合に必要になります。

たとえば、Stack Overflowが行うことを実行し、パスワードをsecurityページで更新できるようにしたい場合、プロファイル画像はユーザー表示ビューで更新可能であり、ユーザーの情報の大部分はユーザー編集ビューで更新可能です。

1)hash classをクラスメソッドで拡張して、空白の値を削除します。このメソッドを使用して、更新されていないがparamsハッシュにまだ存在している空白の値を削除します:

1a)libディレクトリの下のextディレクトリにhash.rbファイルを作成します。

コマンドライン

$ mkdir lib/ext
$ touch lib/ext/hash.rb 

1b)hash.rb内で、 'Hashクラスを作成し、.delete_blanks!メソッドを作成します。

lib/ext/hash.rb

class Hash
    def delete_blanks!
        delete_if { |k, v| v.nil? }
    end
end

1c)このファイル(およびlibディレクトリ全体)をRails初期化子で参照する:

config/boot.rb

# other things such as gemfiles are required here, left out for brevity

Dir['lib/**/*.rb'].each { |f| load(f) } # requires all .rb files in the lib directory 

2)users#updateアクション内に、光沢のある新しいdelete_blanksを実装します。更新していない属性をparamsハッシュから削除するクラスメソッド。次に、* updateメソッドではなく、update_attributesメソッドを介してユーザーインスタンスを更新します!

2a)まず、delete_blanksを使用しましょう! user_paramsハッシュを修正する方法:

app/controllers/users_controller.rb

new_params = user_params.delete_blanks!

2b)次に、update_attributesメソッドを使用してインスタンスを更新しましょう(ここでも、updateメソッドではありません):

app/controllers/users_controller.rb

@user.update_attributes(new_params)

終了したusers#updateアクションは次のようになります。

app/controllers/users_controller.rb

def update

    new_params = user_params.delete_blanks!

    if @user.update_attributes(new_params)
        redirect_to @user, notice: 'User was successfully updated.'
    else
        render action: 'edit' // or whatever you want to do
    end
end

3)Userモデルで、すべての検証にif: :<attribute>オプションを追加します。これは、属性がparamsハッシュに存在する場合にのみ検証がトリガーされるようにするためです。 delete_blanks!メソッドはparamsハッシュから属性を削除するため、たとえば、パスワードの検証は実行されません。 delete_blanks!は、値がnilのハッシュエントリのみを削除し、空の文字列を持つものは削除しないことにも注意してください。したがって、誰かがユーザー作成フォーム(またはパスワードのフィールドを持つ任意のフォーム)でパスワードを省略した場合、ハッシュの:passwordエントリはnilにならず、空になるため、プレゼンス検証が有効になります。文字列:

3a)すべての検証でif:オプションを使用します。

app/models/user.rb

VALID_EMAIL_REGEX = /[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\.[a-zA-Z0-9\-.]/

validates :first_name, presence: true, if: :first_name
validates :last_name, presence: true, if: :last_name
validates :user_name, presence: true, if: :user_name

validates :email, presence: true, 
                  uniqueness: { case_sensitive: false },
                  format: { with: VALID_EMAIL_REGEX }, if: :email 

validates :password, length: { minimum: 6, maximum: 10 }, if: :password

以上です。これで、ユーザーモデルは、アプリ全体のさまざまなフォームで更新できます。属性のプレゼンス検証は、その属性のフィールドを含むすべてのフォームで引き続き機能します。パスワードの存在の検証は、user#createビューで引き続き機能します。

これは他の回答よりも冗長に見えるかもしれませんが、これが最も堅牢な方法だと思います。 Userインスタンスの無数の属性を、無数の異なるモデルで個別に更新できます。新しいモデルでこれを実行する場合は、手順2a)2b)を繰り返す必要があることを覚えておいてください。 および3a)

2
Starkers
@user.username=params[:username]
if @user.update_attribute(:email,params[:email])

  flash[:notice]="successful"
else
  flash[:notice]="fail"
end

上記のコードは、ユーザー名とメールフィールドを更新できます。 update_attributeはダーティフィールドを更新できるためです。しかし、それは残念です。update_attributeは検証をスキップします。

0
andyshi