我有一个注册表单模型,用于在注册期间输入用户:
class RegForm
include ActiveModel::Model
include ActiveModel::Validations
attr_accessor :company_name, :email, :password
validates_presence_of # ...
end
在此注册过程中,我有多个需要创建的模型,我不确定如何正确显示错误消息以及如何将模型错误消息冒泡回UI。
if @reg_form.valid?
account = Account.create!(@reg_form)
else
...
Account.create!看起来像:
def self.create!(reg_form)
account = Account.create_from_reg!(reg_form)
location = location.create_from_reg!(account, reg_form)
..
..
account.location = location
..
account.save!
account
end
答案 0 :(得分:4)
让我们从头开始。
我们希望我们的注册表单对象具有与任何其他ActiveRecord模型相同的API:
// view.html
<%= form_for(@book) do |f| %>
<% end %>
# controller.rb
def create
@book = Book.new(book_params)
if @book.save
redirect_to @book, notice: 'Book was successfully created.'
else
render :new
end
end
为此,我们创建以下对象:
class RegForm
include ActiveModel::Model
attr_accessor :company_name, :email, :password
def save
# Save Location and Account here
end
end
现在,通过加入ActiveModel::Model
,我们的RegForm
获得了大量功能,包括显示错误和验证属性(是的,不必包括ActiveModel::Validations
)。在下一步中,我们添加一些验证:
validates :email, :password, presence: true
我们更改save
以便运行这些验证:
def save
validate
# Save Location and Account here
end
如果所有验证都通过,则 validate
会返回true
,否则会返回false
。
validate
还向@reg_form
添加了errors(所有ActiveRecord模型都有一个errors
哈希值,当验证失败时会填充该哈希值。这意味着在视图中我们可以执行以下任何操作:
@reg_form.errors.messages
#=> { email: ["can't be blank"], password: ["can't be blank"] }
@reg_form.errors.full_messages
#=> ["Email can't be blank", "Password can't be blank"]
@reg_form.errors[:email]
#=> ["can't be blank"]
@reg_form.errors.full_messages_for(:email)
#=> ["Email can't be blank"]
同时,我们的RegistrationsController
应该是这样的:
def create
@reg_form = RegForm.new(reg_params)
if @reg_form.save
redirect_to @reg_form, notice: 'Registration was successful'
else
render :new
end
end
我们可以清楚地看到,当@reg_form.save
返回false
时,会重新呈现new
视图。
最后,我们更改save
,以便我们的模型save
调用包含在transaction内:
def save
if valid?
ActiveRecord::Base.transaction do
location = Location.create!(location_params)
account = location.create_account!(account_params)
end
true
end
rescue ActiveRecord::StatementInvalid => e
# Handle database exceptions not covered by validations.
# e.message and e.cause.message can help you figure out what happened
end
值得注意的一点:
create!
代替create
。只有在引发异常时才会回滚transaction(通常会发生爆炸的方法)。
validate
is just an alias for valid?
。为避免所有缩进,我们可以使用save
方法顶部的保护:
return if invalid?
我们可以通过执行以下操作将数据库异常(like an email uniqueness constraint)转换为错误:
rescue ActiveRecord::RecordNotUnique
errors.add(:email, :taken)
end
我们可以使用符号:base
添加与属性无直接关联的错误:
errors.add(:base, 'Company and Email do not match')
答案 1 :(得分:0)
我认为交易和错误处理可以帮助您解决问题。
def save_or_rescue
ActiveRecord::Base.transaction do
account = Account.create_from_reg!(reg_form)
location = location.create_from_reg!(account, reg_form)
...
end
rescue ActiveRecord::RecordInvalid => exception
puts exception
end