I have a 2 models Game
& Theme
and they have a has_and_belongs_to_many association. I have tried many solutions to prevent duplicate records in
Use:
validates_uniqueness_of :theme_id, :scope => :game_id
As follows:
class Theme < ActiveRecord::Base
has_many :games, through: :games_themes
end
class Game < ActiveRecord::Base
has_many :themes, through: :games_themes
end
class GamesThemes < ActiveRecord::Base
belongs_to :game
belongs_to :theme
validates_uniqueness_of :theme_id, :scope => :game_id
end
You should use database-level validation:
#new_migration
add_index :games_themes, [:game_id, :theme_id], :unique => true
HABTM
This will prevent you saving any duplicate data in the database. Takes the burden off Rails & ensures you only have game or theme. The problem is because HABTM doesn't have a model, there's no validation you can perform in Rails, meaning you need to make it db-level
As mentioned in the comments, this means you'll have to handle the exceptions raised from the db like this:
#app/controllers/games_controller.rb
def create
#creation stuff here
if @game.save
#successful save
else
#capture errors
end
end
To run validations on join table you should use has_many :through
association instead.
http://guides.rubyonrails.org/association_basics.html#the-has-many-through-association
Creating new Model GameTheme for validation purpose is not a good idea. We can validate itself in migration.
Theme Model:
class Theme < ActiveRecord::Base
has_and_belongs_to_many :games,
:association_foreign_key => 'theme_id',
:class_name => 'Theme',
:join_table => 'games_themes'
end
Game Model:
class Theme < ActiveRecord::Base
has_and_belongs_to_many :games,
:association_foreign_key => 'game_id',
:class_name => 'Game',
:join_table => 'games_themes'
end
games_themes migration: You can add uniqueness to join table, Have a look here for more detail.
class GamesThemesTable < ActiveRecord::Migration
def self.up
create_table :games_themes, :id => false do |t|
t.references :game
t.references :theme
end
add_index :games_themes, [:theme_id, :game_id], :unique => true
end
def self.down
drop_table :games_themes
end
end