0

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: "test@test.com", 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: "test@test.com", 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?

s_dolan
  • 1,196
  • 1
  • 9
  • 21

2 Answers2

1

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.

Darpa
  • 396
  • 1
  • 8
0

It's my bad. I should have read your question more carefully.

You are using event.organizers. I guess it is because you have to set inverse_of in your model.

rxing
  • 363
  • 1
  • 2
  • 7
  • I thought associations were supposed to be built with the new() action the same way they used to be built with the build() action in Rails 3. At least, that's what I'm reading in the StackOverflow posts I've seen [here](http://stackoverflow.com/questions/4954313/build-vs-new-in-rails-3), which links to the alias of build() to new() [here](https://github.com/rails/rails/blob/master/activerecord/lib/active_record/relation.rb) – s_dolan Jun 18 '15 at 02:55
  • Yes, you are right. I did't read your question carefully the first place. I have edited my answer. Please take a look. BTW, the second link you gave is not for association and it is for ActiveRecord::Relation. They are different. – rxing Jun 18 '15 at 03:12
  • Where would I add `inverse_of`? Wouldn't it have to be to both the `User` and `Event` model, and point to the `EventOrganizer` model? That option is one of the reasons I'm confused about constructing these relationships, as the docs don't really seem to explain why it's needed. – s_dolan Jun 18 '15 at 03:16
  • Seems inverse_of doesn't support has_many through. http://guides.rubyonrails.org/association_basics.html#bi-directional-associations – rxing Jun 18 '15 at 03:21
  • You are using "has_many through". I guess event = User.first.organized_events.new cannot create one EventOrganizer since you don't have one id for event yet. So event.organizers (also with :through) cannot get the organizer – rxing Jun 18 '15 at 03:30
  • I think that there is a way to do this in Rails through `new()`, otherwise there would never have been a point to having `build()` – s_dolan Jun 18 '15 at 03:33