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.