ruby-on-rails - 如何验证关联的每个实例的限制,而不是 Rails 中的关联本身
问题描述
我有一个Product
模型,每个产品都可以有很多选择,比如尺寸和颜色。每个Option
也可以有很多Choices
。因此,“尺寸”Option
可能有“小”、“中”和“大”,Choices
而“颜色”选项可能有“红色”和“蓝色”作为选项。
使用简单表单我实际上是在尝试在产品表单上做这样的事情:
问题是,如果用户有多个产品选项(例如尺寸和颜色),它只会在每组Options
. 例如,他们可以选择“Blue”,但不能选择“Blue”和“XL”。
我可以做的另一件事是使用as: :check_boxes
而不是,as: :radio_buttons
但是当每个选项只允许一个选择时,用户可以选择一种以上的颜色(例如红色和蓝色)。
那么验证关联的每个实例的限制而不是关联本身的最佳“Rails”方法是什么?如果必须,我可以在客户端的 javascript 中执行此操作,但这似乎不如在服务器端进行验证安全。
加上Product
应该可以有很多Choices
。因此,这并不是对 和 之间关联的真正验证Products
,Choices
而是对通过模型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
解决方案
如果假设客户订购/选择具有规格的产品,您实际上可能需要一个连接模型(选择/订购),而不是对产品模型应用验证。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 :featurable
为belongs_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
请注意,此验证将阻止您为您的产品选项创建多个选项。我确实希望它能让您更好地了解如何创建自定义验证。我强烈建议重新评估您的数据库结构以分离产品/选项/选择设置和用户选择。
如果此验证是您可能在其他模型中使用的东西,您可能需要考虑制作一个验证器。
推荐阅读
- c# - Moq 测试 - 模拟服务调用总是返回 Null
- asp.net - 无法在 asp.net 核心中绑定 int 类型的字段
- r - 基于数据/类类型的 if 语句映射
- amazon-web-services - CloudFormation:重复/现有的 Route53 记录集
- php - “警告:session_start():无法发送会话cookie - 标头已发送”的原因是什么?
- tensorflow - SageMaker 上的 TensorFlow 参数服务器
- c# - 在列表中强制输入值(搜索时键入)?
- python - 如何使用滑动窗口调整 PyTorch 张量的大小?
- python - 在数据框列 Python 中查找和删除子字符串
- r - 从 XML 文件中提取数据 - R