HABTM duplicate records
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
has_and_belongs_to_many creating duplicate entries in model
First note, that has_and_belongs_to_many create a many to many relationship with the party that it connects. Therefore you can have two objects related to each other in this fashion:
issues.rb
class Issue < ActiveRecord::Base
has_and_belongs_to_many :labels
end
labels.rb
class Label < ActiveRecord::Base
has_and_belongs_to_many :issues
end
See section: 2.6 The has_and_belongs_to_many Association of this: http://guides.rubyonrails.org/association_basics.html
In the migration you create the join table, but you do not need to model it.
Now the way you are doing your create will always create a new label.
There is a difference in these two statements:
issue.labels.find_or_create_by(name: 'bug')
and
issue.labels << Label.find_or_create_by(name: 'bug')
The former looks inside the related labels to the issue and creates one if it doesn't exist. For instance, it only queries what issue.labels would return. Therefore when it can not find a related label to the issue with that name, it creates one. Now the latter query does this differently. It looks within the Labels model and says "is there any labels in here with the name 'bug'?" if there are, it finds that label and associates that a label of the issue. If not, it creates a new label before associating it with the issue.
In your case, the latter method will return the result you are looking for.
---- Sub question from Comments about deleting -----
has_and_belongs_to relationships are a 3 table join. When you call:
issue.labels.first
You are listing the first object within the labels collection. This object is a Label, not a join. Removing this, removes the actual object Label that is references in your join table.
issue.labels.first.delete
issue.labels.first.destroy
Both of these are equivalent to calling, assuming there was only one label in your collection (both remove the object, destroy triggers the callbacks):
Label.first.delete
Label.first.destroy
This is not what you are trying to achieve. What you need to do is call is this:
label = Label.first
issue.labels.delete(label)
This leaves the label as is, and just removes the association from the join table.
has_and_belongs_to_many, avoiding dupes in the join table
I worked around this by creating a before_save filter that fixes stuff up.
class Post < ActiveRecord::Base
has_and_belongs_to_many :tags
before_save :fix_tags
def tag_list= (tag_list)
self.tags.clear
tag_list.strip.split(' ').each do
self.tags.build(:name => tag)
end
end
def fix_tags
if self.tags.loaded?
new_tags = []
self.tags.each do |tag|
if existing = Tag.find_by_name(tag.name)
new_tags << existing
else
new_tags << tag
end
end
self.tags = new_tags
end
end
end
It could be slightly optimised to work in batches with the tags, also it may need some slightly better transactional support.
Ruby on Rails Active Admin - Duplicate Records showing for HABTM
has_and_belongs_to_many accepts a :uniq
option which ensures that only uniq records will be returned. Setting this in your model should do the trick.
class MyModel
has_and_belongs_to_many :things, :uniq => true
end
Rails nested form on HABTM: how to prevent duplicate entry?
in employee.rb:
before_save :get_phonenumbers
def get_phonenumbers
self.phonenumbers = self.phonenumbers.collect do |phonenumber|
Phonenumber.find_or_create_by_number(phonenumber.number)
end
end
I have found its working
Removing one of the many duplicate entries in a habtm relationship?
You should really be using a has_many :through
relationship here. Otherwise, yes, the only way to accomplish your goal is to create a method to count the current number of a particular stone, delete them all, then add N - 1
stones back.
class Bowl << ActiveRecord::Base
has_and_belongs_to_many :stones
def remove_stone(stone, count = 1)
current_stones = self.stones.find(:all, :conditions => {:stone_id => stone.id})
self.stones.delete(stone)
(current_stones.size - count).times { self.stones << stone }
end
end
Remember that LIMIT
clauses are not supported in DELETE
statements so there really is no way to accomplish what you want in SQL without some sort of other identifier in your table.
(MySQL actually does support DELETE ... LIMIT 1
but AFAIK ActiveRecord won't do that for you. You'd need to execute raw SQL.)
Rails will_paginate shows duplicates on HABTM models
I think I ran into this problem before. Try adding this to your query:
.group("id")
It's not a bug in will_paginate, because all that does is take the data it gives you and paginates it in the view. The solution lies in the data you provide it.
Related Topics
How to Use Rspec Expectations in Irb
How to Look Up Elevation Data by Lat/Lng
How to Update Gems in Ruby for Windows
Dynamically Creating Class in Ruby
No Such File to Load Bundler Error for Rails 3
Appending to Rake Db:Seed in Rails and Running It Without Duplicating Data
Removing Child Root Nodes in Rabl
Detect Browser Language in Rails
How to Convert a Ruby String Range to a Range Object
Ruby: Creating a Hash Key and Value from a Variable in Ruby
Set the Display Precision of a Float in Ruby
How to Get Ruby to Parse Time as If It Were in a Different Time Zone
How to Remove Repeated Spaces in a String
Strong Parameters Require Multiple