customink / secondbase

Seamless second database integration for Rails.
MIT License
220 stars 31 forks source link

has_many :through association issue #53

Closed ph0rque closed 6 years ago

ph0rque commented 6 years ago

I have the following models:

class User < ActiveRecord::Base
  has_many :enrollments, dependent: :destroy
  has_many :courses, through: :enrollments
end
class Enrollment < ActiveRecord::Base  
  belongs_to :user
  belongs_to :course
end
class Course < SecondBase::Base
  has_many :enrollments
  has_many :users, through: :enrollments
end

I get the following error when starting with a user and specifying a course:

[3] pry(main)> User.first.courses.first
  User Load (0.5ms)  SELECT  "users".* FROM "users"  ORDER BY "users"."id" ASC LIMIT 1
ActiveRecord::StatementInvalid: PG::UndefinedTable: ERROR:  relation "enrollments" does not exist
LINE 5:                WHERE a.attrelid = '"enrollments"'::regclass
                                          ^
:               SELECT a.attname, format_type(a.atttypid, a.atttypmod),
                     pg_get_expr(d.adbin, d.adrelid), a.attnotnull, a.atttypid, a.atttypmod
                FROM pg_attribute a LEFT JOIN pg_attrdef d
                  ON a.attrelid = d.adrelid AND a.attnum = d.adnum
               WHERE a.attrelid = '"enrollments"'::regclass
                 AND a.attnum > 0 AND NOT a.attisdropped
               ORDER BY a.attnum
from ...gems/activerecord-4.2.10/lib/active_record/connection_adapters/postgresql_adapter.rb:592:in `async_exec'
metaskills commented 6 years ago

Most has or belongs to associations will work just fine. However User#courses and Course#users will most likely not work since Rails will often do a JOIN when using those association methods. And JOINs that span multiple DB are just not going to work.

ph0rque commented 6 years ago

Update: This turned out to be irrelevant to the issue.

Hmmm actually Course.first.users worked just fine, despite the INNER JOIN...

Course.first.users
  Course Load (0.5ms)  SELECT  "courses".* FROM "courses"  ORDER BY "courses"."id" ASC LIMIT 1
  User Load (3.8ms)  SELECT "users".* FROM "users" INNER JOIN "enrollments" ON "users"."id" = "enrollments"."user_id" WHERE "enrollments"."enrollable_id" = $1 AND "enrollments"."enrollable_type" = $2  [["enrollable_id", 1], ["enrollable_type", "Course"]]
=> [# all the users enrolled in the first course]

It seems the problem is with this code snippet: source: :enrollable, source_type: 'Course'. I'll investigate and see if I can modify to make it work.

metaskills commented 6 years ago

Thanks for the followup! I see now, the through association is on the same DB so it is mostly safe. I still have a feeling that in some cases User#courses could be an issue in some cases. But I do not know of any off-hand without doing some testing. Keep on the lookout tho and I'm glad you diagnosed this. Cheers!

ph0rque commented 6 years ago

So, for anyone working around this, this is the workaround I came up with:

class User < ActiveRecord::Base
  has_many :enrollments, dependent: :destroy
  # has_many :courses, through: :enrollments

  def courses
    Course.find(id: self.enrollments.pluck(:course_id))
  end
end
class Enrollment < ActiveRecord::Base  
  belongs_to :user
  belongs_to :course
end
class Course < SecondBase::Base
  has_many :enrollments
  # has_many :users, through: :enrollments
  def users
    User.find(id: self.enrollments.pluck(:user_id))
  end
end
# instead of user.courses << course
user.enrollments.create! course: course 

# instead of course.users << user
course.enrollments.create! user: user

# or...
Enrollment.create!(user: user, course: course)