首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >多模型保存,如何包装事务和报告错误

多模型保存,如何包装事务和报告错误
EN

Stack Overflow用户
提问于 2016-08-01 01:29:12
回答 2查看 1.4K关注 0票数 9

我有一个注册表单模型,它在注册过程中接受用户的输入:

代码语言:javascript
复制
class RegForm
    include ActiveModel::Model
    include ActiveModel::Validations

    attr_accessor :company_name, :email, :password
    validates_presence_of # ...

end

在此注册过程中,需要创建多个模型,我不知道如何正确地显示错误消息,以及如何将模型错误消息泡回UI。

代码语言:javascript
复制
if @reg_form.valid?
   account = Account.create!(@reg_form)
else 
...

Account.create!看上去:

代码语言:javascript
复制
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
  1. 所以我很困惑如何显示所有这些保存的模型的错误消息
  2. 如果reg_form没有所有其他模型的正确数据,如何显示或失败验证。
  3. 如何确保这是包装在一个事务,以便我不保存任何东西,如果任何模型未能保存在注册期间。
EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2016-08-07 01:21:45

让我们从头开始。

我们希望我们的注册表单对象具有与任何其他ActiveRecord模型相同的API:

代码语言:javascript
复制
// 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

为此,我们创建以下对象:

代码语言:javascript
复制
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)。在接下来的步骤中,我们添加了一些验证:

代码语言:javascript
复制
validates :email, :password, presence: true

我们修改save,以便运行验证:

代码语言:javascript
复制
def save
  validate
  # Save Location and Account here
end

如果所有验证都通过,则validate返回true,否则返回false

validate还将错误添加到@reg_form (所有ActiveRecord模型都有一个errors哈希,当验证失败时会填充该哈希)。这意味着我们可以做到以下任何一项:

代码语言:javascript
复制
@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应该是这样的:

代码语言:javascript
复制
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调用封装在事务中。

代码语言:javascript
复制
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

值得注意的几点:

  1. 使用create!而不是create。只有当异常被引发时,事务才会被回滚(通常使用bang的方法会这样做)。
  2. valid?。为了避免所有这些缩进,我们可以在save方法的顶部使用一个保护: 如果无效返回?
  3. 通过执行以下操作,我们可以将数据库异常(就像电子邮件唯一性约束一样)转换为错误: 拯救ActiveRecord::RecordNotUnique errors.add(:email,:taken)结束
  4. 通过使用符号:base,我们可以添加与属性没有直接关联的错误: errors.add(:base,“公司和电子邮件不匹配”)
票数 5
EN

Stack Overflow用户

发布于 2016-08-06 15:50:19

我认为事务处理和错误处理将帮助您解决问题。

代码语言:javascript
复制
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
票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/38689867

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档