1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
Database tables look like so:

items:
+----+---------
| id | title
+----+---------
|  1 | Test 1
|  2 | Test 2

tags:
+----+---------+
| id | tag     |
+----+---------+
|  1 | tag1    |
|  2 | tag2    |

items_tags:
+----+---------+--------+
| id | item_id | tag_id |
+----+---------+--------+
|  1 |       1 |      1 |
|  2 |       2 |      2 |


Models look like:

models/item.rb
class Item < ActiveRecord::Base
  has_and_belongs_to_many :tags

  def tags_s=(tags_s)
    for tag in tags_s.split(' ')
      self.tags.push(Tag.find_or_create_by_tag(tag))
    end
  end

  def tags_s
    tags.join(' ')
  end
end

models/tag.rb
class Tag < ActiveRecord::Base
  has_and_belongs_to_many :items

  def to_s
    return tag
  end
end


On the console this happens:
>> newitem = Item.new
=> #<Item:0xb750ba74 @new_record=true, @attributes={"created_on"=>nil, "text"=>nil, "title"=>"", "updated_on"=>nil, "type"=>""}>
>> newitem.title = "console test"
=> "console test"
>> newitem.text = "test"
=> "test"
>> newitem.save
=> true
>> newitem
=> #<Item:0xb750ba74 @new_record=false, @new_record_before_save=true, @attributes={"created_on"=>Sun Nov 26 18:21:09 GMT 2006, "text"=>"test", "title"=>"console test", "updated_on"=>Sun Nov 26 18:21:09 GMT 2006, "type"=>"", "id"=>3}, @errors=#<ActiveRecord::Errors:0xb75067a4 @base=#<Item:0xb750ba74 ...>, @errors={}>>
>> newitem.tags_s=("tag2")
ActiveRecord::StatementInvalid: Mysql::Error: Duplicate entry '2' for key 1: INSERT INTO items_tags (`item_id`, `tag_id`, `id`) VALUES (3, 2, 2)
.... trace snipped ...
>> newitem.tags_s=("tag3")
=> "tag3"

That is, if the tag doesn't exist then this works fine, if it exists then for some reason it tries to add an entry to the join table with an id that already exists (looks like the id of the first entry which references the required tag). Why does it do this? It doesn't need to save an id at all as the database will generate it automatically. How can I fix it?