I'm hoping that this question will lead to me understanding more about how to construct many-to-many relationships with foreign keys that don't have inherant "Rails magic", as I've been Googling for hours and still don't fully understand.
Basically, Users
can organize Events
, and Events
can be organized by many Users
.
I have the following models:
class User < ActiveRecord::Base
has_many :event_organizers, foreign_key: "organizer_id"
has_many :organized_events, through: :event_organizers, source: "event"
class EventOrganizer < ActiveRecord::Base
belongs_to :event
belongs_to :organizer, class_name: "User", foreign_key: "organizer_id"
class Event < ActiveRecord::Base
has_many :event_organizers
has_many :organizers, through: :event_organizers
In case you have questions about the columns on event_organizers
:
event_organizers
event_id (references an event)
organizer_id (references a user)
Now to the fun part, which I noticed was happening in my Controller but will show in the console to make this post shorter:
2.2.0 :001 > User.first
User Load (0.1ms) SELECT "users".* FROM "users" ORDER BY "users"."id" ASC LIMIT 1
=> #<User id: 1, email: "[email protected]", password_digest: "$2a$10$SCOFgQTF4krBYlqKVHSSMOtVP8ad/vTFPN/60WjX8s....", first_name: "test", last_name: "user", admin: false, created_at: "2015-06-18 02:36:21", updated_at: "2015-06-18 02:36:21">
2.2.0 :002 > event = User.first.organized_events.new( name: "test", location: "test", start_time: Time.now, end_time: 2.hours.from_now )
User Load (0.2ms) SELECT "users".* FROM "users" ORDER BY "users"."id" ASC LIMIT 1
=> #<Event id: nil, name: "test", location: "test", start_time: "2015-06-18 02:37:15", end_time: "2015-06-18 04:37:15", created_at: nil, updated_at: nil>
2.2.0 :003 > event.organizers
=> #<ActiveRecord::Associations::CollectionProxy []>
2.2.0 :004 > event = User.first.organized_events.create( name: "test", location: "test", start_time: Time.now, end_time: 2.hours.from_now )
User Load (0.5ms) SELECT "users".* FROM "users" ORDER BY "users"."id" ASC LIMIT 1
(0.2ms) begin transaction
SQL (0.3ms) INSERT INTO "events" ("name", "location", "start_time", "end_time", "created_at", "updated_at") VALUES (?, ?, ?, ?, ?, ?) [["name", "test"], ["location", "test"], ["start_time", "2015-06-18 02:37:37.781981"], ["end_time", "2015-06-18 04:37:37.782003"], ["created_at", "2015-06-18 02:37:37.788761"], ["updated_at", "2015-06-18 02:37:37.788761"]]
User Load (0.1ms) SELECT "users".* FROM "users" WHERE "users"."id" = ? LIMIT 1 [["id", 1]]
EventOrganizer Exists (0.1ms) SELECT 1 AS one FROM "event_organizers" WHERE ("event_organizers"."organizer_id" = 1 AND "event_organizers"."event_id" = 1) LIMIT 1
SQL (0.1ms) INSERT INTO "event_organizers" ("organizer_id", "event_id", "created_at", "updated_at") VALUES (?, ?, ?, ?) [["organizer_id", 1], ["event_id", 1], ["created_at", "2015-06-18 02:37:37.800400"], ["updated_at", "2015-06-18 02:37:37.800400"]]
(37.2ms) commit transaction
=> #<Event id: 1, name: "test", location: "test", start_time: "2015-06-18 02:37:37", end_time: "2015-06-18 04:37:37", created_at: "2015-06-18 02:37:37", updated_at: "2015-06-18 02:37:37">
2.2.0 :005 > event.organizers
User Load (0.6ms) SELECT "users".* FROM "users" INNER JOIN "event_organizers" ON "users"."id" = "event_organizers"."organizer_id" WHERE "event_organizers"."event_id" = ? [["event_id", 1]]
=> #<ActiveRecord::Associations::CollectionProxy [#<User id: 1, email: "[email protected]", password_digest: "$2a$10$SCOFgQTF4krBYlqKVHSSMOtVP8ad/vTFPN/60WjX8s....", first_name: "test", last_name: "user", admin: false, created_at: "2015-06-18 02:36:21", updated_at: "2015-06-18 02:36:21">]>
So,my question is this: Why is it that when I use user.organized_events.new()
, the association doesn't exist, but when I use user.organized_events.create()
, it does?
As you will be able to notice, when you are using 'new', the id for the new event is not generated. The id for a record is generated only at the time it is saved to the database and two records are linked through their ids only if you haven't mentioned any other attribute explicitly. Since no id has been generated for the new event yet, a user cannot be linked to it. Hence, when you use the query 'event.organizers', you get an empty array.