Jeffrey Guenther
Jeffrey Guenther

Reputation: 901

Why can't I add an object to the relationship?

Just learning rails...

I have the following models:

class TimeSlot < ActiveRecord::Base
  has_and_belongs_to_many :users
end

class User < ActiveRecord::
   has_and_belongs_to_many :time_slots
end

I also have a model to join the two:

class TimeSlotsUsers < ActiveRecord::Base
  attr_accessible :time_slot_id, :user_id
end

In the console, I create a user object and I want to associate it with a TimeSlot. I have an variable ts that is a TimeSlot object, and u that is a user object. Both already exist in the database. When I do ts.users << u, I get a error saying "ActiveRecord::StatementInvalid: SQLite3::ConstraintException: time_slots_users.created_at may not be NULL: INSERT INTO "time_slots_users" ("time_slot_id", "user_id") VALUES (1, 1).

Why would created_at be null? Isn't it being automatically created when the record in TimeSlotsUsers is created? Do I need to use a has-many through relationship instead?

Upvotes: 1

Views: 146

Answers (2)

Mischa
Mischa

Reputation: 43298

The join table for a habtm relationship should not have created_at and updated_at columns. You also don't need the join model.

If you want to do something with the join tabel and/or add attributes to it you should change to has_many :through.

Upvotes: 1

Chris Aitchison
Chris Aitchison

Reputation: 4654

A join model is a good pattern if you want to have any information other than the user_id and timeslot_id as part of the relation ship. For example, if you have groups and users and you have a many-to-many relationship, but sometimes a user is an admin of a group, you might put the is_admin attribute on the join model.

To keep your join model, a better Rails patter is as follows...

class TimeSlot < ActiveRecord::Base
  has_many :time_slot_users
  has_many :users, through: :time_slot_users
end

class User < ActiveRecord::Base
  has_many :time_slot_users
  has_many :time_slots, through: :time_slot_users
end

class TimeSlotsUsers < ActiveRecord::Base
  belongs_to :users
  belongs_to :time_slots
end

I disagree with one of the other answers that advocates no primary key on the join table.
To have the unique id of a relationship is important when you want to delete or edit that relationship using a REST API, for example. Whether to have an updated_at or created_at depends on whether they are useful data in your problem domain. It might be useful information, for example, to know when a user was added to a group (or it might not be).

It is important to have a unique index on the join table on (user_id, time_slot_id) to ensure data integrity. Rails models can validate uniqueness reliably in 99.999% of instances, but not 100% of the time.

Upvotes: 2

Related Questions