Rails: Self joins - ruby-on-rails

I'm trying design a model which has relation to itself
Model:
class Department < ActiveRecord::Base
belongs_to :organization
has_many :positions
has_many :sub_departments, class: 'Department', foreign_key: 'parent_id'
end
Migration:
class CreateDepartments < ActiveRecord::Migration
def change
create_table :departments do |t|
t.string :name
t.references :parent, index: true
t.references :organization, index: true
t.timestamps
end
end
end
When I call Department.first.sub_departments I get an error: NoMethodError: undefined method 'relation_delegate_class' for "Department":String. What am I doing wrong?
Thanks!

I think you should use class_name: instead of class:.

Related

What kind of relationship should be used for tables Transaction and User - Ruby on Rails?

I am new learner. I just started learning more about Backend with Ruby on Rails.
I have the following tables - User and User_Transaction.
So basically I want to have a transaction which holds information about the sender and the receiver. This personally sounds to me more like a has_and_belongs_to_many relation. However, I am really confused in how to approach this and how should I include the 2 foreign keys.
I am curious to learn more about this and I will be really happy if someone helps me :).
Migrations
User
class CreateUsers < ActiveRecord::Migration[6.0]
def change
create_table :users do |t|
t.integer :username
t.integer :password
t.timestamps
end
end
end
Transaction
class CreateTransactions < ActiveRecord::Migration[6.0]
def change
create_table :transactions do |t|
t.string :sender
t.string:receiver
t.decimal :amount
t.timestamps
end
end
end
Models
Transaction
class ::Transaction < ApplicationRecord
#We have two users per transaction 'Sender' and 'Receiver'
has_and_belongs_to_many :users
# belongs_to :sender, :class_name => 'User'
# belongs_to :receiver, :class_name => 'User'
end
User
class User < ApplicationRecord
# has_many :accounts
# has_many :transactions
has_and_belongs_to_many :transactions
end
how about this:
migrations
class CreateUsers < ActiveRecord::Migration[6.0]
def change
create_table :users do |t|
t.string :username
t.string :password
t.timestamps
end
end
end
class CreateTransactions < ActiveRecord::Migration[6.0]
def change
create_table :transactions do |t|
t.references :sender, index: true, null: false, foreign_key: {to_table: :users}
t.references :receiver, index: true, null: false, foreign_key: {to_table: :users}
t.decimal :amount
t.timestamps
end
end
end
models
class User < ApplicationRecord
has_many :send_transactions, class_name: "Transaction", foreign_key: :sender, inverse_of: :sender
has_many :receive_transactions, class_name: "Transaction", foreign_key: :receiver, inverse_of: :receiver
end
class Transaction < ApplicationRecord
belongs_to :sender, class_name: "User", inverse_of: :send_transactions
belongs_to :receiver, class_name: "User", inverse_of: :receive_transactions
end

Two foreign keys in a table referencing one primary key in another table using rails migration

Im trying to create a table with 2 FK Referencing 1 PK in another table.
class CreateJobapps < ActiveRecord::Migration[5.1]
def change
create_table :jobapps do |t|
t.references :job, foreign_key: { job: :id }, index: { unique: true}
t.references :user, foreign_key: { user: :id }, index: { unique: true}
t.timestamps
end
end
end
Is this method correct? if so, how can I get an output if I provide a FK of respected table.
Here is how my Jobapp table looks like
I tried using Jobapp.joins(:user) but to no avail
Am I supposed to write belongs_to or has_many in the model file?
class CreateJobapps < ActiveRecord::Migration[5.1]
def change
create_table :jobapps do |t|
t.references :job, foreign_key: { job: :id }
t.references :user, foreign_key: { user: :id }
t.timestamps
end
# Add a compound index instead - you may need to switch the order to
# tweak the index depending on how it is used.
add_index :jobapps, [:job_id, :user_id], unique: true
end
end
class Jobapp < ApplicationRecord
belongs_to :user
belongs_to :job
end
class User < ApplicationRecord
has_many :jobapps
has_many :jobs, through: :jobapps
end
class Job < ApplicationRecord
has_many :jobapps
has_many :users, through: :jobapps
end

Rails ActiveRecord two fields each referencing the same table

I have a Player class that I want to have high_school_team and club_team properties. So then I figure Player will have high_school_team_id and club_team_id properties that point to the corresponding team. I try to do this in the following migration, but it doesn't work.
class CreatePlayers < ActiveRecord::Migration[6.0]
def change
create_table :players do |t|
t.string :first_name
t.string :middle_name
t.string :last_name
t.decimal :height
t.decimal :weight
t.date :birthday
t.references :team, :high_school_team, foreign_key: true
t.references :team, :club_team, foreign_key: true
t.decimal :gpa
t.string :class_year
t.string :intended_major
t.string :email
t.string :phone_number
t.text :notes
t.timestamps
end
end
end
It gives the following error:
code/scout-db [master●] » rails db:migrate
== 20191218003854 CreatePlayers: migrating ====================================
-- create_table(:players)
rails aborted!
StandardError: An error has occurred, this and all later migrations canceled:
you can't define an already defined column 'team_id'.
/Library/Ruby/Gems/2.6.0/gems/activerecord-6.0.2.1/lib/active_record/connection_adapters/abstract/schema_definitions.rb:372:in `column'
...
HighSchoolTeam and ClubTeam are models that do single table inheritance with Team.
I don't see why I'm getting the error. The docs seem to say that the first argument for t.referenes is table_name and the second ref_name. :team is the name of the table and I want the references to be high_school_team_id and club_team_id.
When I switch the order of the arguments to t.references, it still doesn't work. Somehow it gives the same error: you can't define an already defined column 'team_id'..
It looks you you are confusing SchemaStatement#add_reference and TableDefinition#references which have completely different signatures.
If you want to setup a foreign key column where the table can't be derived from the name of the column (the first argument) you just pass foreign_key: { to_table: :teams}.
class CreatePlayers < ActiveRecord::Migration[6.0]
def change
create_table :players do |t|
t.references :high_school_team, foreign_key: { to_table: :teams}
t.references :club_team, foreign_key: { to_table: :teams}
end
end
end
t.references :high_school_team, index: true as recommended by the other answers is NOT equivilent. That just adds a index to the column but no foreign key constraint.
You can then setup the assocations on Player as:
class Player < ApplicationRecord
belongs_to :high_school_team, class_name: 'Team'
belongs_to :club_team, class_name: 'Team'
end
You can't use a single has_many :players assocation on the other end though as the foreign key column can be either players.high_school_team_id or players.club_team_id.
class Team
has_many :high_school_team_players,
foreign_key: :high_school_team_id,
class_name: 'Player'
has_many :club_team_players,
foreign_key: :club_team_id,
class_name: 'Player'
end
But really a better alternative in the first place would have been to setup a join table:
class Player
has_many :placements
has_one :high_school_team,
through: :placements,
source: :team
class_name: 'Team'
has_one :club_team,
through: :placements,
source: :team
class_name: 'Team'
end
class Placement
belongs_to :player
belongs_to :team
end
class Team
has_many :placements
has_many :players, through: :placements
end
The doc you mentioned talks about the case when you need to add reference to an existing table.
For adding a refernence to a new table:
t.references :team, :high_school_team, foreign_key: true
This piece of code is wrong. Instead, it should be
t.references :high_school_team, foreign_key: {to_table: :teams}
to_table is needed to add database referential integrity
So your migration will be like this:
class CreatePlayers < ActiveRecord::Migration[6.0]
def change
create_table :players do |t|
....
t.references :high_school_team, foreign_key: {to_table: :teams}
t.references :club_team, foreign_key: {to_table: :teams}
....
end
end
end

has_many or belongs_to works wrong

I have this two models:
class Answer < ActiveRecord::Base
belongs_to :user
belongs_to :question
has_many :edits, dependent: :destroy
end
class Question < ActiveRecord::Base
belongs_to :user
has_many :answers, dependent: :destroy
end
But when I write in rails console following:
q=Question.new
q.save
a=Answer.new
a.question = q
a.save
q.answers.size
It gives me zero.
irb(main):026:0> q.answers.size
=> 0
But when I write this:
Answer.where(:question_id => q.id).size
it gives me 1
SO WHAT DO I DO?
In case you need it - answers and question migrations:
class CreateAnswers < ActiveRecord::Migration
def change
#execute "DROP TABLE #{:answers} CASCADE"
create_table :answers do |t|
t.text :body
t.references :user, index: true, foreign_key: true
t.references :question, index: true, foreign_key: true
t.timestamps null: false
end
end
end
class CreateQuestions < ActiveRecord::Migration
def change
#execute "DROP TABLE #{:questions} CASCADE"
create_table :questions do |t|
t.string :title
t.text :body
t.references :user, index: true, foreign_key: true
t.timestamps null: false
end
end
end
You need to use inverse_of option in your relationship.
class Answer < ActiveRecord::Base
belongs_to :user
belongs_to :question, inverse_of: answers
has_many :edits, dependent: :destroy
end
class Question < ActiveRecord::Base
belongs_to :user
has_many :answers, inverse_of: question, dependent: :destroy
end
So when you do:
a.question = q
Rails will do this for you(in memory):
q.answers << a
And you don't need to reload the q again.

Self Association in Rails with adding foreign key error

I am doing self association in rails. I have Request model which should reference itself. Here is what i have:
class Request < ActiveRecord::Base
has_many :sub_requests, class_name: 'Request',
foreign_key: 'parent_request_id'
belongs_to :parent_request, class_name: 'Request'
end
and my migration:
class CreateRequests < ActiveRecord::Migration
def change
create_table :requests do |t|
t.references :parent_request, index: true
end
end
end
But I get an error as follows:
PG::UndefinedColumn: ERROR: column requests.parent_request_id does not exist
Have you run rake db:migrate? If you did, than try this one:
class Request < ActiveRecord::Base
has_many :sub_requests, class_name: 'Request', foreign_key: 'parent_request_id', primary_key: 'id'
belongs_to :parent_request, class_name: 'Request', foreign_key: 'parent_request_id', primary_key: 'id'
end
class CreateRequests < ActiveRecord::Migration
def change
create_table :requests do |t|
t.integer :parent_request_id, index: true
end
end
end

Resources