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

zyl*_*024 14 ruby activerecord ruby-on-rails rails-activerecord

我是ruby on rails的新手,无法完成这项工作.基本上我有一个用户注册页面,其中有密码确认.在User类中,我有以下验证:

validates :password, confirmation: true
Run Code Online (Sandbox Code Playgroud)

在控制器中我有

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
Run Code Online (Sandbox Code Playgroud)

问题是,当密码确认符合时,我仍然收到通知消息,表明注册成功.正如你所看到的,我已经尝试了几个返回值,create但它们似乎都没有成功.我很确定验证是有效的,因为如果密码与确认不匹配,我就看不到我刚刚创建的用户.另外,当我使用时create!,我可以看到网站因验证错误而崩溃.任何人都可以帮助告诉我create在未经过验证的情况下应该返回什么内容吗?

谢谢.

gwc*_*fey 33

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

user.valid? # <= returns false
user.errors.count # <= will be > 0
user.errors.blank? # <= will be false
Run Code Online (Sandbox Code Playgroud)

所以你的代码会改变:

if user==false or user==nil or user==vals
Run Code Online (Sandbox Code Playgroud)

对此:

if !user.valid?
Run Code Online (Sandbox Code Playgroud)

您也可以使用此模式:

user.attributes = vals
if user.save
   ... save succeeded ...
else
   ... save failed ...
end
Run Code Online (Sandbox Code Playgroud)

save方法返回一个布尔值,true或者false因为您在现有实例上调用它.

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

第一:你有这个:

if User.exists(vals[:username])
Run Code Online (Sandbox Code Playgroud)

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

class User < ActiveRecord::Base
   ...
   validates :username, unique: true
   ...
end
Run Code Online (Sandbox Code Playgroud)

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

第二:你有这个:

vals[:create_date] = DateTime.current
Run Code Online (Sandbox Code Playgroud)

这是不必要的.如果向模型添加一个名为的列,created_at它将自动保存创建日期(由ActiveRecord管理).您可以updated_at在迁移中将此及其合作伙伴添加到模型中,如下所示:

create_table :users do |t|
   ...
   t.timestamps # <= tells rails to add created_at and updated_at
end
Run Code Online (Sandbox Code Playgroud)

或者,因为你已经有一张users桌子:

add_column :users, :created_at, :datetime
add_column :users, :updated_at, :datetime
Run Code Online (Sandbox Code Playgroud)

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

第三:你有这个:

user = User.create(vals, :without_protection => :true)
Run Code Online (Sandbox Code Playgroud)

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

vals = params[:user]
Run Code Online (Sandbox Code Playgroud)

对此:

vals = params.require(:user).permit(:username, :password, :password_confirmation)
Run Code Online (Sandbox Code Playgroud)

然后继续保护:

user = User.create(vals)
Run Code Online (Sandbox Code Playgroud)

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

第四:user_path如果保存失败,则不应重定向到,因为不会显示用户模型.相反,你应该重新渲染你的new表格.您也不需要错误消息.如果new表单呈现,它可以相应地检查@user.errors和报告错误消息.请参阅ActiveRecord错误对象文档.

最后:即使您的密码已得到正确确认,您也提到您的验证失败.如果没有看到您的表单代码,我无法确定,但请确保调用您的密码字段并调用password确认字段password_confirmation.*_confirmation在验证确认时,Rails会专门查找此字段值.

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


son*_*gcn 5

答案是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
Run Code Online (Sandbox Code Playgroud)

如何判断它成功还是失败

真正的答案是persisted?

if user.persisted?
  # Success
else
  # Failed
end
Run Code Online (Sandbox Code Playgroud)

为什么不习惯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
Run Code Online (Sandbox Code Playgroud)

现在,我们执行One.create将失败,检查日志文件,这将是一个回滚,因为它无法创建两个。在这种情况下,One.create.valid?仍然会返回true,但实际上创建失败,因此One.create.persisted?有必要使用它来替换它。

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