user3162553 user3162553 - 1 month ago 11
Ruby Question

Setting up Foreign Keys in Rails with Postgres

I am attempting to track mutual likes where a user likes a user that likes them. To check if the like is mutual, I call an method after the like is created. If that liker has been a likee than it is considered mutual.

The problem however, is that I run into some odd errors that I believe are related to how the foreign keys have been set up. I have set up a has many association in Rails but any time I attempt to access it, I get a

PG::InFailedSqlTransaction: ERROR: current transaction is aborted, commands ignored until end of transaction block
: RELEASE SAVEPOINT active_record_1
error.

User:

class User < ApplicationRecord
has_many :likes
end


Like:

class Like < ApplicationRecord
belongs_to :likee, class_name: 'User', foreign_key: 'likee_id'
belongs_to :liker, class_name: 'User', foreign_key: 'liker_id'
belongs_to :connection, optional: true

after_save :mutual_like?

private

def mutual_like?

if liker.likes.where(likee: liker)
# fails
end
end
end


I've tried changing the hook to be around save, etc. but that doesn't work either.

Also, I cannot even call
Like.first
else I get the same error.

I've tried printing out
liker.likes.where(likee: liker)
and i get
PG::UndefinedColumn: ERROR: column likes.user_id does not exist
which I think is the problem.

I can however, access the liker like this:
self.likee
inside of the hook but I cannot call
self.likee.likes
without it returning
#<Like::ActiveRecord_Associations_CollectionProxy:0x3fd91ca5ca78>


Schema:

create_table "likes", force: :cascade do |t|
t.integer "liker_id", null: false
t.integer "likee_id", null: false
t.integer "connection_id"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.index ["likee_id"], name: "index_likes_on_likee_id", using: :btree
t.index ["liker_id"], name: "index_likes_on_liker_id", using: :btree
end





create_table "users", force: :cascade do |t|
t.string "email"
t.string "username"
t.string "password_digest"
t.boolean "admin", default: false
t.index ["email"], name: "index_users_on_email", using: :btree
t.index ["username"], name: "index_users_on_username", using: :btree
end


Tests:

RSpec.describe Like, type: :model do
let(:liker) { Fabricate(:user) }
let(:likee) { Fabricate(:user) }
let(:like) { Fabricate(:like, liker: liker, likee: likee) }
let(:pending_like) { Fabricate.build(:like, liker: liker, likee: likee)}

context 'relations' do
it { should belong_to :likee }
it { should belong_to :liker }

describe 'liking another user' do

it '#liker references first user' do
expect(like.likee).to eq likee
end

it '#likee references second user' do
expect(like.liker).to eq liker
end

end
end
...

RSpec.describe User, type: :model do
context 'relations' do
it { should have_many :likes }
end
...


Is this caused because I'm attempting to lookup a record that is being saved?

Answer

liker.likes.where(likee: liker) returns only a relation. You have to execute your query like this liker.likes.where(likee: liker).exists?

has_many :likes within your User model expects a column user_id within your table likes. With the code given I would think you have to change it to

class User < ApplicationRecord
  has_many :other_likes, foreign_key: :liker_id 
  has_many :likes, foreign_key: :likee_id 
end

belongs_to :connection is a bad name in my point of view. ActiveRecord::Base has a class method with the same name. Maybe you will run into issues with such an association name some day.

Comments