Lighthouse has a new layout. Prefer the old one? Return to the old layout, and switch back any time from the link at the top of each page.

This project is archived and is in readonly mode.

[Rails3 b4] has_and_belongs_to_many (habtm) with string ids don't work

#5152

Hi,

I have a problem with the has_and_belongs_to_many association. I don't use integer id, but a string uuid for ids. I have 3 tables: users and roles with a users_roles between. If I do this:

user = User.first
role = Role.first
user.roles << role
user.save!

=> It's work with 2.3.8 but not with 3b4. The uuids for user and role are ok, but not the foreign keys is users_roles.

If I have a uuid for user like "123456abc", in users_roles.user_id or users_roles.role_id, I have "'123456abc'" with the '' included.

Regards,
noe

Reported by noe · July 19th, 2010 @ 02:56 PM

State: resolved
Milestone: 3.x
Assigned to: José Valim José Valim
Importance: Low

Activity

  1. Neeraj Singh
    Neeraj Singh
    • Milestone set to 3.x
    • State changed from new to open
    • Assigned user set to Neeraj Singh
    • Importance changed from to Low

    July 19th, 2010 @ 06:00 PM

  2. Neeraj Singh
    Neeraj Singh
    • Assigned user changed from Neeraj Singh to José Valim

    Attached is code and test patch.

    July 19th, 2010 @ 08:34 PM

  3. noe
    noe

    Thanks for the patch, it work well for foreign keys :)

    I have a related issue if I add created_at and/or updated_at columns in users_roles table. It's the same problem: AR try to insert "'a date'" instead of "a date".

    July 20th, 2010 @ 07:53 AM

  4. Neeraj Singh
    Neeraj Singh

    I am using rails edge and surprisingly I am getting nil for both created_at and updated_at in my join table. Not sure what I am missing.

    ActiveRecord::Schema.define(:version => 20100719150531) do
    
      create_table "countries", :id => false, :force => true do |t|
        t.string "country_id"
        t.string "name"
      end
    
      create_table "countries_treaties", :id => false, :force => true do |t|
        t.string   "country_id", :null => false
        t.string   "treaty_id",  :null => false
        t.datetime "updated_at"
        t.datetime "created_at"
      end
    
      create_table "treaties", :id => false, :force => true do |t|
        t.string "treaty_id"
        t.string "name"
      end
    
    end
    
    class Country < ActiveRecord::Base
      set_primary_key :country_id
      has_and_belongs_to_many :treaties
    
      def self.lab
        Country.delete_all
        Treaty.delete_all
        c = Country.new(:name => 'india')
        c.country_id = 'c1'
        c.save!
    
        t = Treaty.new(:name => 'peace')
        t.treaty_id = 't1'
        c.treaties << t
        c.save!
        puts Country.first.treaties.inspect
    
        con = ActiveRecord::Base.connection
        sql = 'select * from countries_treaties'
        records = con.select_rows(sql)
        puts records.inspect
        record = records.last
        puts record.inspect
        puts record[0]
        puts record[1]
        puts record[2]
        puts record[3]
      end
    
    end
    
    class Treaty < ActiveRecord::Base
      set_primary_key :treaty_id
    
      has_and_belongs_to_many :countries
    
      def self.lab
      end
    
    end
    
    Country.lab 
    
    ree-1.8.7-2010.01 > Country.lab
      SQL (0.4ms)   SELECT name
     FROM sqlite_master
     WHERE type = 'table' AND NOT name = 'sqlite_sequence'
    
      SQL (3.4ms)  DELETE FROM "countries" WHERE 1=1
      SQL (1.8ms)  DELETE FROM "treaties" WHERE 1=1
      SQL (0.4ms)  INSERT INTO "countries" ("country_id", "name") VALUES ('c1', 'india')
      SQL (0.4ms)  INSERT INTO "treaties" ("name", "treaty_id") VALUES ('peace', 't1')
      SQL (0.2ms)  INSERT INTO "countries_treaties" ("country_id", "treaty_id") VALUES ('c1', 't1')
      Country Load (0.5ms)  SELECT "countries".* FROM "countries" LIMIT 1
      Treaty Load (0.3ms)  SELECT * FROM "treaties" INNER JOIN "countries_treaties" ON "treaties".treaty_id = "countries_treaties".treaty_id WHERE ("countries_treaties".country_id = 'c1' )
    [#<Treaty treaty_id: "t1", name: "peace">, #<Treaty treaty_id: "t1", name: "peace">, #<Treaty treaty_id: "t1", name: "peace">]
      SQL (0.2ms)  select * from countries_treaties
    [["c1", "t1", nil, nil], ["c1", "t1", nil, nil], ["c1", "t1", nil, nil]]
    ["c1", "t1", nil, nil]
    c1
    t1
    nil
    nil
    

    July 20th, 2010 @ 11:13 AM

  5. Samuel Kadolph
    Samuel Kadolph

    The has_and_belongs_to_many association doesn't store created_at and updated_at because they would be useless since there is no join model to access them. And since they are created and updated only when assigning one model to another, the times would be exactly the same.

    A habtm join table row is never updated even when you add the same model to another. A new row is always created.

    c.treaties << t
    c.treaties << t
    

    would give 2 additional join table rows.

    July 20th, 2010 @ 03:43 PM

  6. Neeraj Singh
    Neeraj Singh

    timestamps should always be updated to reduce any element of surprise. Created ticket #5161 timestamp columns do not get update in the HABTM join table to fix that issue.

    July 20th, 2010 @ 04:20 PM

  7. José Valim
    José Valim

    @Samuel, I would expect c.treaties << t to create two rows unless you specify :uniq in the association.

    Besides, it is documented that all columns in the join table are available in the model instance, overwriting the model ones. So if you do post.tags, where posts and tags have a HABTM relationship, the created_at and updated_at fields for each tag object in post.tags will return the created_at and updated_at value in the join table, and not in the tag attribute. This work for all columns and this is the reason you should not have id in join tables (otherwise it would overwrite your model id).

    July 20th, 2010 @ 04:25 PM

  8. Repository
  9. José Valim
    José Valim
    • State changed from open to resolved

    July 21st, 2010 @ 01:56 PM