Validate uniqueness of name in a HABTM association

爷,独闯天下 提交于 2019-12-23 04:49:12

问题


I have an Artist model that has many Albums in a HABTM association. Though I would like to permit two different albums to have the same name I'd like to ensure that there aren't two in the collection of one artist. So far example:

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

I first thought of validating the uniqueness of the name in the Album model but get this error when I try to create a new object:

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

I then thought of putting the validation in my join model, AlbumArtist but get the error undefined method 'name' (name is one of the album's attributes):

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

How can I get this to work?

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

Schema

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

回答1:


The most straightforward way in this case would be to put a custom validator in your relation model:

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

You may also want to add an index to the name column if you don't already have one.



来源:https://stackoverflow.com/questions/34667213/validate-uniqueness-of-name-in-a-habtm-association

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!