I have a Rails project with a “Product Variant” form. The Product variant model is called
Variant, and on the
Variant form users should be able to select one choice for each option available. For instance a T-Shirt might have an “Option” called “Size” with the “Choices” small, medium, or large, and another “Option” called one “Color” with the “Choices” red, green, blue. Thus, the
Variant created is a unique SKU such as a “T-shirt — Size: Small, Color: Green.” Or if it were a product that had 3 options instead of 2, the variant would require 3 choices per option, such as “Guitar Strap – Size: Long, Fabric Color: Red, Leather Color: Brown”.
I can’t figure out how to write a custom validation that only allows the user to save one choice per option. Each option should only have one choice selected for each variant. Here’s an illustration.
Here are my models with the relevant associations…
class Variant < ApplicationRecord has_many :selections has_many :choices, through: :selections validate :one_choice_per_option private def one_choice_per_option # can't figure out how to do this custom validation here end end
class Choice < ApplicationRecord has_many :variants, through: :selections belongs_to :option end
class Selection < ApplicationRecord belongs_to :choice belongs_to :variant end
class Option < ApplicationRecord has_many :choices, dependent: :destroy accepts_nested_attributes_for :choices, allow_destroy: true end
The best I’ve managed to do is get this custom validation working on in
def one_choice_per_option self.product.options.each do |option| if option.choices.count > 1 errors.add(:choice, 'Error: select one choice for each option') end end end
But that only allows one
Choice total through the variant form. What I want to do is allow one choice for each set of options.
What is a “Railsy” way to do this type of custom validation? Should I be trying to do a custom validation on the
Selection model? If so, how?
Based on the discussion in the comments. It seems that I need to do some combination of Active Record querying to make this work. @sevensidedmarble’s “EDIT 2” below is closer but that is giving me this error:
Type Error compared with non class/module
If I save the wrong behavior to the database and then call
Variant.last.choices in the console it feels like I’m getting closer:
So essentially, what I need to do is not allow the
Variant form to save if there is more than one
Selection with the same
option_id. A selection shouldn’t save unless the
option_id is unique to the associated
Something like this is what I’m trying to do:
validate :selections_must_have_unique_option private def selections_must_have_unique_option unless self.choices.distinct(:option_id) errors.add(:options, 'can only have one choice per option') end end
But that code doesn’t work. It just saves the form as if the validation weren’t there.