Pergunta

I have a has_many :through type association set up for my models:

class Place < ActiveRecord::Base

  has_many :place_subcategory_relations, dependent: :destroy
  has_many :place_subcategories, through: :place_subcategory_relations, uniq: true

end


class PlaceSubcategory < ActiveRecord::Base

  has_many :place_subcategory_relations, dependent: :restrict
  has_many :places, through: :place_subcategory_relations, uniq: true

end

class PlaceSubcategoryRelation < ActiveRecord::Base

  belongs_to :place
  belongs_to :place_subcategory

  validates :is_primary, uniqueness: {scope: :place_id}

end

The problem is, when I try to add another subcategory to a place:

place.place_subcategories << PlaceSubcategory.find(84)

I get this error:

   (0.2ms)  BEGIN
  PlaceSubcategoryRelation Exists (0.6ms)  SELECT 1 AS one FROM "place_subcategory_relations" WHERE ("place_subcategory_relations"."is_primary" IS NULL AND "place_subcategory_relations"."place_id" = 169) LIMIT 1
   (0.2ms)  ROLLBACK
ActiveRecord::RecordInvalid: Validation failed: Is primary has already been taken

I've also tried to create the PlaceCategoryRelation first but nothing is commited:

p.place_subcategory_relations << PlaceSubcategoryRelation.new(place_id: p.id, place_subcategory_id: PlaceSubcategory.find(84).id)

   (0.2ms)  BEGIN
  PlaceSubcategoryRelation Exists (0.6ms)  SELECT 1 AS one FROM "place_subcategory_relations" WHERE ("place_subcategory_relations"."is_primary" IS NULL AND "place_subcategory_relations"."place_id" = 169) LIMIT 1
   (0.2ms)  COMMIT

Am I using the uniqueness validator incorrectly? I can't figure out what the problem is here...

Foi útil?

Solução

Solved!

The problem was that, because is_primary is a boolean, its false values have to be unique as well, not only true ones. So when inserting a new association with is_primary = false when another one with is_primary = false existed, it fails like it's supposed to.

I solved it by using this for the validation:

  validates :is_primary, uniqueness: {scope: :place_id}, if: ->{self.is_primary && self.class.where(place_id: self.place_id, is_primary: true).length > 0}

EDIT: I guess this works too, and it's simpler:

validates :is_primary, uniqueness: {scope: :place_id}, if: :is_primary
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top