首页 > 解决方案 > 如何验证关联的每个实例的限制,而不是 Rails 中的关联本身

问题描述

我有一个Product模型,每个产品都可以有很多选择,比如尺寸和颜色。每个Option也可以有很多Choices。因此,“尺寸”Option可能有“小”、“中”和“大”,Choices而“颜色”选项可能有“红色”和“蓝色”作为选项。

使用简单表单我实际上是在尝试在产品表单上做这样的事情:

问题是,如果用户有多个产品选项(例如尺寸和颜色),它只会在每组Options. 例如,他们可以选择“Blue”,但不能选择“Blue”和“XL”。

我可以做的另一件事是使用as: :check_boxes而不是,as: :radio_buttons但是当每个选项只允许一个选择时,用户可以选择一种以上的颜色(例如红色和蓝色)。

那么验证关联的每个实例的限制而不是关联本身的最佳“Rails”方法是什么?如果必须,我可以在客户端的 javascript 中执行此操作,但这似乎不如在服务器端进行验证安全。

加上Product应该可以有很多Choices。因此,这并不是对 和 之间关联的真正验证ProductsChoices而是对通过模型Choice可用的每组选择限制为 1 的验证。Options

例如,一件 T 恤可能是红色和 XL,但不应该是 Red & Blue + Small & XL?

这是我的模型:

class Product < ApplicationRecord
  has_many :choices,  through: :options
end

class Option < ApplicationRecord
  belongs_to :product

  has_many :choices
end

class Choice < ApplicationRecord
  belongs_to :option
end

标签: ruby-on-railsformsvalidationsimple-form

解决方案


如果假设客户订购/选择具有规格的产品,您实际上可能需要一个连接模型(选择/订购),而不是对产品模型应用验证。Product 模型似乎只是供您设置用户可以为该产品选择的选项和选择。

如果这是实际情况,您只需创建连接模型并使用多态“特征”进行设置。像这样的东西:

class Order
  belongs_to :product
  belongs_to :featurable, polymorphic: true
  belongs_to :user

  validates_inclusion_of :featurable_type, in: %w[Option Choice]
end

较新的 Rails 版本将验证belongs_to字段是否存在。

我说多态是因为我假设该选项可能没有选择,有时您可以只选择选项本身。如果所有选项都有选择,那么只需将其更改belongs_to :featurablebelongs_to :choice并删除包含验证。这belongs_to :user是因为我假设特定用户会输入此订单/选择。

如果您可能为您的产品选择了多个选项,那么您可以将它的结构更像这样:

class ProductOrder
  belongs_to :product
  belongs_to :user
  has_many :choice_selections
end

class ChoiceSelection
  belongs_to :product_order
  belongs_to :featurable, polymorphic: true

  validates_inclusion_of :featurable_type, in: %w[Option Choice]
  validate :unique_option

  def option
    featurable.is_a?(Option) ? featurable : featurable.option
  end

  def unique_option
    return unless product_order.choice_selections.find_by(option: option).present?
    errors.add(:base, 'choice option must be unique')
  end
end

如果所有选项都有选择:

您不需要多态关联。

class ProductOrder
  belongs_to :product
  belongs_to :user
  has_many :choice_selections
end

class ChoiceSelection
  belongs_to :product_order
  belongs_to :choice
  belongs_to :option

  validates_uniqueness_of :option, scope: :product_order
end

但是,要回答您在上面发布的问题:

我要做的第一件事是在 Product 模型中创建自定义验证。

请务必has_many :options在 Product 模型中添加该行,使其看起来更像这样:

class Product < ApplicationRecord
  has_many :options
  has_many :choices, through: :options
end

否则,这through可能行不通。

然后,像这样添加验证:

class Product < ApplicationRecord
  # ...

   validate :one_choice_per_option

   private

   def one_choice_per_option
     if options.any? { |option| option.choices.count > 1 }
       errors.add(:options, 'can only have one choice')
     end
   end

  # ...
end

请注意,此验证将阻止您为您的产品选项创建多个选项。我确实希望它能让您更好地了解如何创建自定义验证。我强烈建议重新评估您的数据库结构以分离产品/选项/选择设置和用户选择。

如果此验证是您可能在其他模型中使用的东西,您可能需要考虑制作一个验证器


推荐阅读