如何验证关联的每个实例的限制,而不是 Rails 中的关联本身
How to validate a limit on each instance of the association, but not the association itself in Rails
我有一个 Product
模型,每个产品可以有很多选项,例如尺寸和颜色。每个Option
也可以有很多Choices
。所以 "Size" Option
可能有 "Small," "Medium," 和 "Large" 作为 Choices
而 "Color" 选项可能有 "Red" 和 "Blue" 作为选择。
使用 Simple Form 我实际上是在尝试在产品表单上做这样的事情:
问题是,如果用户有多个产品选项(例如尺寸和颜色),它只会在每组 Options
中为他们提供一个单选按钮。所以他们可以 select "Blue" 但不能 "Blue" 和 "XL," 例如。
我可以做的另一件事是使用 as: :check_boxes
而不是 as: :radio_buttons
但是用户可以 select 不止一种颜色(例如红色和蓝色),而只有一个选择应该允许每个选项。
那么什么是最好的 "Rails" 方法来验证关联的每个实例的限制,而不是关联本身?如果必须的话,我可以在客户端 javascript 中执行此操作,但这似乎不如在服务器端进行验证安全。
加上Product
应该可以有很多Choices
。因此,这并不是对 Products
和 Choices
之间关联的真正验证,而是对通过 Options
可用的每组选择限制为 1 Choice
的验证型号。
例如,一件 T 恤可能是红色和 XL,但它不应该是红色和蓝色 + 小号和 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
如果客户假设 order/select 具有规格的产品,您实际上可能需要一个连接模型 (Selection/Order),而不是对产品模型应用验证。 Product 模型似乎只是供您设置用户可以 select 该产品的选项和选择。
如果这是这里的实际情况,您只需创建连接模型并使用多态设置它 "feature." 像这样:
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
字段是否存在。
我说多态是因为我假设选项可能没有选择,有时您可以 select 选项本身。如果所有选项都有选择,那么只需将 belongs_to :featurable
更改为 belongs_to :choice
并删除包含验证。 belongs_to :user
在那里,因为我假设特定用户会输入这个 order/selection.
如果您的产品可能有多种选择 select,那么您的结构可能更像这样:
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
但是,要回答您在上面发布的问题:
我要做的第一件事是在产品模型中创建自定义验证。
务必在产品模型中添加 has_many :options
行,使其看起来更像这样:
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
请注意,此验证将阻止您为产品选项创建多个选择。我确实希望它能让您更好地了解如何创建自定义验证。我强烈建议重新评估您的数据库结构以将 product/option/choice 设置和用户 select 分开。
如果您可能会在其他模型中使用此验证,您可能需要考虑制作一个 validator。
我有一个 Product
模型,每个产品可以有很多选项,例如尺寸和颜色。每个Option
也可以有很多Choices
。所以 "Size" Option
可能有 "Small," "Medium," 和 "Large" 作为 Choices
而 "Color" 选项可能有 "Red" 和 "Blue" 作为选择。
使用 Simple Form 我实际上是在尝试在产品表单上做这样的事情:
问题是,如果用户有多个产品选项(例如尺寸和颜色),它只会在每组 Options
中为他们提供一个单选按钮。所以他们可以 select "Blue" 但不能 "Blue" 和 "XL," 例如。
我可以做的另一件事是使用 as: :check_boxes
而不是 as: :radio_buttons
但是用户可以 select 不止一种颜色(例如红色和蓝色),而只有一个选择应该允许每个选项。
那么什么是最好的 "Rails" 方法来验证关联的每个实例的限制,而不是关联本身?如果必须的话,我可以在客户端 javascript 中执行此操作,但这似乎不如在服务器端进行验证安全。
加上Product
应该可以有很多Choices
。因此,这并不是对 Products
和 Choices
之间关联的真正验证,而是对通过 Options
可用的每组选择限制为 1 Choice
的验证型号。
例如,一件 T 恤可能是红色和 XL,但它不应该是红色和蓝色 + 小号和 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
如果客户假设 order/select 具有规格的产品,您实际上可能需要一个连接模型 (Selection/Order),而不是对产品模型应用验证。 Product 模型似乎只是供您设置用户可以 select 该产品的选项和选择。
如果这是这里的实际情况,您只需创建连接模型并使用多态设置它 "feature." 像这样:
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
字段是否存在。
我说多态是因为我假设选项可能没有选择,有时您可以 select 选项本身。如果所有选项都有选择,那么只需将 belongs_to :featurable
更改为 belongs_to :choice
并删除包含验证。 belongs_to :user
在那里,因为我假设特定用户会输入这个 order/selection.
如果您的产品可能有多种选择 select,那么您的结构可能更像这样:
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
但是,要回答您在上面发布的问题:
我要做的第一件事是在产品模型中创建自定义验证。
务必在产品模型中添加 has_many :options
行,使其看起来更像这样:
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
请注意,此验证将阻止您为产品选项创建多个选择。我确实希望它能让您更好地了解如何创建自定义验证。我强烈建议重新评估您的数据库结构以将 product/option/choice 设置和用户 select 分开。
如果您可能会在其他模型中使用此验证,您可能需要考虑制作一个 validator。