创建失败时Ruby on Rails Active Record返回值?

时间:2014-06-01 00:55:41

标签: ruby-on-rails ruby activerecord rails-activerecord

我是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!时,我可以看到网站因验证错误而崩溃。任何人都可以帮助告诉我当记录未经过验证时应该返回create的内容吗?

感谢。

2 个答案:

答案 0 :(得分:28)

您的问题的答案是,User.create如果成功失败,则会返回User个实例。如果由于验证而失败,则实例将无效并且将出错:

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方法返回布尔值truefalse,因为您在现有实例上调用它。

但是,让我们通过其他几种方式让您走上正轨:

首先:你有这个:

if User.exists(vals[:username])

(我假设exits是您放在User模型上的一种方法,因为它不是Rails的东西。您可以在模型上使用另一个验证,而不是在控制器中进行检查:

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

现在,当您尝试创建用户时,如果您已拥有具有该名称的用户,则验证将失败。

第二:你有这个:

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

现在,您将始终拥有创建日期/时间以及上次更新用户模型,而无需其他代码。

第三:你有这个:

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

不要这样做。相反,改变这个:

vals = params[:user]

对此:

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

然后继续保护:

user = User.create(vals)

您可以将要从表单中添加的任何其他列添加到permit()来电。这非常重要,因为以后很难解决这类问题。 &#34;如果你走上黑暗的道路,它将永远支配你的命运。&#34;

第四:如果保存失败,则不应重定向到user_path,因为不会显示任何用户模型。相反,您应该重新呈现您的new表单。您也不需要Flash消息来处理错误。如果new表单呈现,则可以检查@user.errors并相应地报告错误消息。请参阅ActiveRecord error object documentation

最后:您提到即使您的密码已正确确认,您的验证也会失败。我无法在不查看您的表单代码的情况下说出来,但请确保您的密码字段名为password,并且确认字段名为password_confirmation。在验证确认时,Rails会专门查找此*_confirmation字段值。

如果不这样做,请发布您的表单代码并进行修改。

答案 1 :(得分:4)

答案是 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将失败,检查日志文件,它将是回滚,因为它无法创建两个。在这种情况下,One.create.valid?仍会返回true,但实际上创建失败,因此请使用One.create.persisted?替换它。

注意:代码在Rails 5.1中进行了测试。