y9v / activerecord-jsonb-associations

Use PostgreSQL JSONB fields to store foreign ids on your models.
MIT License
69 stars 25 forks source link

Bi Directional association not working. #1

Closed zaidakram closed 6 years ago

zaidakram commented 6 years ago
class Profile < ActiveRecord::Base
  belongs_to :user, store: :extra
end

class User < ActiveRecord::Base
  has_one :profile, foreign_store: :extra
end
pry(main)> u = User.find 1
=> # There is a valid user with id 1
pry(main)> u.profile
=> # Works fine.
pry(main)> p = Profile.find 1
=> # There exists a profile with id 1 which has a valid profile.
pry(main)> p.user 
=> nil
# Doesn't even execute the query and returns nil. No error, nothing...

ENV

Bundler             1.16.2
  Platforms         ruby, x86_64-linux
Ruby                2.3.6p384 (2017-12-14 revision 61254) [x86_64-linux]
  Full Path         /usr/share/rvm/rubies/ruby-2.3.6/bin/ruby
  Config Dir        /usr/share/rvm/rubies/ruby-2.3.6/etc
RubyGems            2.7.7
  Gem Home          /home/user/.rvm/gems/ruby-2.3.6@project
  Gem Path          /home/user/.rvm/gems/ruby-2.3.6@project:/home/user/.rvm/gems/ruby-2.3.6@global
  User Path         /home/user/.gem/ruby/2.3.0
  Bin Dir           /home/user/.rvm/gems/ruby-2.3.6@project/bin
Tools               
  Git               2.17.1
  RVM               1.29.4 (manual)
  rbenv             not installed
  chruby            not installed
  rubygems-bundler  (1.4.5)
Rails Version: 5.1.6
OS: Ubuntu 18.04 LTS
y9v commented 6 years ago

Thank you for reporting! I'll take a look

y9v commented 6 years ago

@zaidakram Can you please also add information on how those records have been created?

zaidakram commented 6 years ago

Investigated it a little more and found a pattern. When you build/create association like this user.build_profile(...) or user.create_profile(...) it works fine.

But if I explicitly create a profile with a valid user_id like this, p = Profile.create extra: {user_id: 1} it gives the association blank error

 :019 > u = User.create name: 'Akram'
   (0.3ms)  BEGIN
  SQL (0.5ms)  INSERT INTO "users" ("name", "created_at", "updated_at") VALUES ($1, $2, $3) RETURNING "id"  [["name", "Akram"], ["created_at", "2018-08-06 16:04:50.502651"], ["updated_at", "2018-08-06 16:04:50.502651"]]
   (3.2ms)  COMMIT
 => #<User id: 2, name: "Akram", created_at: "2018-08-06 16:04:50", updated_at: "2018-08-06 16:04:50"> 
 :020 > u.create_profile
   (0.7ms)  BEGIN
  SQL (1.2ms)  INSERT INTO "profiles" ("extra", "created_at", "updated_at") VALUES ($1, $2, $3) RETURNING "id"  [["extra", "{\"user_id\":2}"], ["created_at", "2018-08-06 16:04:56.754955"], ["updated_at", "2018-08-06 16:04:56.754955"]]
   (3.4ms)  COMMIT
  Profile Load (0.7ms)  SELECT  "profiles".* FROM "profiles" WHERE "profiles"."extra" ->> 'user_id' = $1 LIMIT $2  [["user_id", "2"], ["LIMIT", 1]]
 => #<Profile id: 2, extra: {"user_id"=>2}, created_at: "2018-08-06 16:04:56", updated_at: "2018-08-06 16:04:56", user_id: nil> 
 :021 > p = Profile.create({user_id: 1, extra: {user_id: 1}})
   (0.8ms)  BEGIN
   (1.0ms)  ROLLBACK
 => #<Profile id: nil, extra: {"user_id"=>1}, created_at: nil, updated_at: nil, user_id: nil> 
 :022 > p.errors
 => #<ActiveModel::Errors:0x0000559c6f15a160 @base=#<Profile id: nil, extra: {"user_id"=>1}, created_at: nil, updated_at: nil, user_id: nil>, @messages={:user=>["must exist"]}, @details={:user=>[{:error=>:blank}]}>

Likewise if I query from has_one/has_many side, it works fine as reported earlier i,e user.profile... But profile.user gives nil.

y9v commented 6 years ago

yes, that's the reason then; The thing is: we also have to write { extra: { "profile_id": ... }} to the User model, so we avoid querying or joining the second table. I'm not sure how I can address this, I'll check whether it makes sense to add additional logic to extra= setter

y9v commented 6 years ago

@zaidakram this issue is fixed in 0.2.0

It was actually a bug, and you can set the foreign id via the extra attribute directly.