我在理解如何使用Rails的委托类型时遇到了一个问题,当涉及到委托上的验证失败时。
具有以下代码
inbox.rb
class Inbox < ApplicationRecord
delegate :name, to: :inboxable
delegated_type :inboxable, types: %w[ Mailbox Messagebox ], dependent: :destroy
end
class Mailbox < ApplicationRecord
include Inboxable
belongs_to :inbox_domain
validates :alias, presence: true, uniqueness: true
def name
"#{self.alias}@#{self.domain.name}"
end
end
messagees_controller.rb
def create
@mailbox = Inbox.create inboxable: Mailbox.new(mailbox_params)
if @mailbox.save
redirect_to @mailbox.inboxable, notice: "<b>#{@mailbox.name}</b> was created."
else
render :new
end
end
private
def mailbox_params
params.require(:mailbox).permit(:alias, :inbox_domain_id)
end
当我想要创建别名已被采用的邮箱时,会抛出以下错误,因为邮箱。New验证失败
ActiveRecord::NotNullViolation (PG::NotNullViolation: ERROR: null value in column "inboxable_id" violates not-null constraint
DETAIL: Failing row contains (13, 2021-09-26 20:48:53.970799, 2021-09-26 20:48:53.970799, Mailbox, null, f).
):
尝试解决方案
处理这种情况的正确方法是什么?我已尝试显式检查邮箱。新的第一个,像这样:
mailbox = Mailbox.new(mailbox_params)
if mailbox.valid?
@inbox = Inbox.create inboxable: mailbox
......
虽然它在技术上是有效的,但一旦你还必须验证收件箱本身的属性,它就会变得一团糟
使用validates_associated
触发关联记录的验证:
class Inbox < ApplicationRecord
delegate :name, to: :inboxable
delegated_type :inboxable, types: %w[ Mailbox Messagebox ], dependent: :destroy
validates_associated :inboxable
end
这将在此模型上的errors对象中添加一个错误("Inboxable is invalidate "),并且如果关联的邮箱无效,则阻止保存。
你想要的控制器是:
def create
# .create both instanciates and saves the record - not what you want here
@mailbox = Inbox.new(inboxable: Mailbox.new(mailbox_params))
if @mailbox.save
redirect_to @mailbox.inboxable, notice: "<b>#{@mailbox.name}</b> was created."
else
render :new
end
end
如果你想显示相关项的错误,你需要访问并遍历其中的errors对象:
# app/views/shared/_errors.html.erb
<ul>
<% object.errors.each do |attribute, message| %>
<li><%= message %>
<% end %>
</ul>
<%= form_with(model: @inbox) do |form| %>
<% if form.object.invalid? %>
<%= render(partial: 'shared/errors', object: form.object) %>
<% if form.object.inboxable.invalid? %>
<%= render(partial: 'shared/errors', object: form.object.inboxable) %>
<% end %>
<% end %>
# ...
<% end %>