validations validates validate uniqueness rails multiple greater_than custom columns ruby-on-rails validation activerecord model unique

ruby on rails - validates - Validar la unicidad del nombre en una asociación HABTM



rails validates if (1)

Tengo un modelo de Artist que tiene muchos Album en una asociación HABTM. Aunque me gustaría permitir que dos álbumes diferentes tengan el mismo nombre, me gustaría asegurarme de que no haya dos en la colección de un artista. Hasta ahora ejemplo:

artist_1 = Artist.create(name: "Jay-Z") artist_2 = Artist.create(name: "The Beatles") album_1 = Album.create(name: "The Black Album", artist_ids: [1]) album_2 = Album.create(name: "The Black Album", artist_ids: [1]) => Should return error album_2 = Album.create(name: "The Black Album", artist_ids: [2]) => Should not return an error

Primero pensé en validar la singularidad del nombre en el modelo de Album , pero obtuve este error cuando intento crear un nuevo objeto:

SQLite3::SQLException: no such column: albums.artist_id: SELECT 1 AS one FROM "albums" WHERE ("albums"."name" = ''The Black Album'' AND "albums"."artist_id" IS NULL) LIMIT 1

Luego pensé en poner la validación en mi modelo de unión, AlbumArtist pero obtengo el undefined method ''name'' error undefined method ''name'' (el name es uno de los atributos del álbum):

undefined method `name'' for #<AlbumArtist:0x007f8e1fc335e8>

¿Cómo puedo hacer que esto funcione?

class Album < ActiveRecord::Base has_many :album_artists has_many :artist, through: :album_artists end class AlbumArtist < ActiveRecord::Base belongs_to :album belongs_to :artist # validates_uniqueness_of :name, scope: [:artist_id] end class Artist < ActiveRecord::Base has_many :album_artists has_many :albums, through: :album_artists # validates_uniqueness_of :name, scope: [:artist_id] end

Esquema

create_table "albums", force: :cascade do |t| t.string "name" end create_table "album_artists", force: :cascade do |t| t.integer "album_id" t.integer "artist_id" t.datetime "created_at", null: false t.datetime "updated_at", null: false end add_index "album_artists", ["album_id"], name: "index_album_artists_on_album_id" add_index "album_artists", ["artist_id"], name: "index_album_artists_on_artist_id" create_table "artists", force: :cascade do |t| t.string "name" end


La forma más directa en este caso sería poner un validador personalizado en su modelo de relación:

class AlbumArtist < ActiveRecord::Base belongs_to :album belongs_to :artist validates_presence_of :album, :artist validate :ensure_unique, on: :create private def ensure_unique if self.artist.albums.where(name: self.album.name).any? errors[:base] << ''Artist already has an album by this name'' end end end

Es posible que también desee agregar un índice a la columna de nombre si aún no tiene uno.