web-dev-qa-db-ja.com

Ruby on Rails Active Record return value when create failed ??

私はRuby on Railsに不慣れで、この作業を取得するのに問題があります。基本的には、パスワードの確認ができるユーザー登録ページがあります。Userクラスでは、次の検証があります:

validates :password, confirmation: true

そして、コントローラーには

def create
    vals = params[:user]
    if(User.exists(vals[:username])) 
        flash[:warning] = "#{vals[:username]} already exists! Please try a new one. "
    else
        vals[:create_date] = DateTime.current
        user = User.create(vals, :without_protection => :true)
        if user==false or user==nil or user==vals
            flash[:warning] = "#{vals[:username]} has not been registered successfully. "
        else
            flash[:notice] = "#{vals[:username]} has been registered. "
        end
    end
    redirect_to users_path
end

問題は、パスワードが確認と一致しても、登録が成功したことを示す通知メッセージが引き続き表示されることです。ご覧のとおり、createの戻り値をいくつか試しましたが、どれも成功していないようです。パスワードが確認と一致しない場合、作成したばかりのユーザーが表示されないため、検証が機能していると確信しています。また、create!、検証エラーでWebサイトがクラッシュするのを確認できます。レコードが検証されない場合、createが何を返すべきかを誰かに教えてもらえますか?

ありがとう。

15
zyl1024

あなたの質問への答えは、_User.create_は、成功するとUserインスタンスを返しますorは失敗します。検証のために失敗した場合、インスタンスは無効になり、エラーが発生します。

_user.valid? # <= returns false
user.errors.count # <= will be > 0
user.errors.blank? # <= will be false
_

したがって、コードは次のように変更されます。

_if user==false or user==nil or user==vals
_

これに:

_if !user.valid?
_

次のパターンも使用できます。

_user.attributes = vals
if user.save
   ... save succeeded ...
else
   ... save failed ...
end
_

saveメソッドは、既存のインスタンスで呼び出しているため、ブール値trueまたはfalseを返します。

しかし、他のいくつかの方法で正しい軌道に乗ることができます:

最初:あなたはこれを持っています:

_if User.exists(vals[:username])
_

(私はexitsUserモデルにRailsのことではないので、あなたがあなたのモデルに置いた方法だと思います。)コントローラでそのチェックを行う代わりに、モデルに対して別の検証を使用できます。

_class User < ActiveRecord::Base
   ...
   validates :username, unique: true
   ...
end
_

ユーザーを作成しようとすると、その名前のユーザーがすでに存在する場合、検証に失敗します。

2番目:あなたはこれを持っています:

_vals[:create_date] = DateTime.current
_

これは不要です。 _created_at_というモデルに列を追加すると、作成日が自動的に保持されます(ActiveRecordによって管理されます)。これとそのパートナー_updated_at_を次のように移行のモデルに追加できます。

_create_table :users do |t|
   ...
   t.timestamps # <= tells Rails to add created_at and updated_at
end
_

または、すでにusersテーブルがあるので:

_add_column :users, :created_at, :datetime
add_column :users, :updated_at, :datetime
_

これで、追加のコードを必要とせずに、ユーザーモデルの作成日時と最終更新日時が常に得られます。

3番目:あなたはこれを持っています:

_user = User.create(vals, :without_protection => :true)
_

これを行わないでください。代わりに、これを変更します。

_vals = params[:user]
_

これに:

_vals = params.require(:user).permit(:username, :password, :password_confirmation)
_

そして保護を維持します:

_user = User.create(vals)
_

フォームからpermit()呼び出しに持ち込む列を追加できます。この種のものを後で修正するのは難しいので、これは非常に重要です。 「いったんあなたが暗い道を進んだら、それは永遠にあなたの運命を支配するでしょう。」

4番目:保存に失敗した場合、表示するユーザーモデルがないため、_user_path_にリダイレクトしないでください。代わりに、newフォームを再レンダリングする必要があります。エラーのフラッシュメッセージも必要ありません。 newフォームがレンダリングされる場合、_@user.errors_をチェックし、それに応じてエラーメッセージを報告できます。 ActiveRecordエラーオブジェクトのドキュメント を参照してください。

最後に:あなたは、パスワードが正しく確認されても検証が失敗すると述べました。フォームコードを確認しないとわかりませんが、パスワードフィールドがpasswordで、確認フィールドが_password_confirmation_であることを確認してください。 Railsは、確認のための検証時に、特にこの_*_confirmation_フィールド値を探します。

それでうまくいかない場合は、フォームコードを投稿してください。修正します。

33
gwcoffey

答えはActiveRecord objectです。公式のソースコードは、オブジェクトが成功または失敗した場合にcreateがオブジェクトを返すことを示しています。

# File activerecord/lib/active_record/persistence.rb, line 29
def create(attributes = nil, &block)
  if attributes.is_a?(Array)
    attributes.collect { |attr| create(attr, &block) }
  else
    object = new(attributes, &block)
    object.save
    object
  end
end

成功か失敗かを判断する方法

本当の答えはpersisted?

if user.persisted?
  # Success
else
  # Failed
end

なぜuser.valid?を使用しなかったのですか?他のモデルを操作するいくつかのコールバックが失敗した場合、それだけでは不十分な場合があるためです:

class One < ActiveRecord::Base
  has_many :twos
  after_create :create_twos_after_create
  def create_twos_after_create
    # Use bang method in callbacks, than it will rollback while create  two failed 
    twos.create!({})    # This will fail because lack of the column `number`
  end
end

class Two < ActiveRecord::Base
  validates :number, presence: true
end

ここで、One.createを実行すると失敗し、ログファイルを確認します。2つを作成できなかったため、ロールバックになります。この場合、One.create.valid?は引き続きtrueを返しますが、実際には作成に失敗したため、One.create.persisted?を使用して置き換える必要があります。

注意:コードはRails 5.1。

5
songhuangcn