Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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?

like image 674
user3162553 Avatar asked Oct 30 '22 19:10

user3162553


1 Answers

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.

like image 74
slowjack2k Avatar answered Nov 15 '22 05:11

slowjack2k