So basically I like to know what association i need to link my routes table with employees table. I have a routes table with an employees column (array type) which holds employee id's. I also have an employee table that has (first_name, last_name, phone_number).
A has_many :employees, foreign_key: :employees, class_name: :Employee does not work and gives an error. Any ideas?
This error is given
ActiveRecord::StatementInvalid (PG::UndefinedColumn: ERROR: column employees.employees does not exist)
LINE 1: SELECT "employees".* FROM "employees" WHERE "employees"."emp...
Using an array column here is a just bad idea:
Violates first normal form.
Doesn't let you use foreign keys to maintain referential integrity.
Doesn't work with ActiveRecord assocations that expect you to model your data in a sane way.
You will need to write queries by hand.
Instead you most likely want is a self-referential assocation:
class AddManagerToEmployees < ActiveRecord::Migration[6.1]
def change
add_reference :employees, :manager,
null: true,
foreign_key: { to_table: :employees }
end
end
class Employee < ApplicationRecord
belongs_to :manager,
class_name: 'Employee'
has_many :subordinates,
class_name: 'Employee',
foreign_key: :manager_id
end
If the manager-employee relation should be many to many instead of one to many use a join table.
Related
I have a two models 1. category primary key is cust_number , 2. product primary key is id . Now i want to make a HABTM relationship between category and product. Here the problem is how to create join table with cust_number and product_id as a keys?
For this use case you want to use has_many through: and not has_and_belongs_to_many. They both acheive the same goal (a m2m association) but has_and_belongs_to_many is very limited.
AFAIK HABTM only takes the foreign_key option which is used to set the foreign key on the join table, there does not seem to be a way to tell it what PK that FK points to.
Run the generator to create the join model:
rails g model category_product product:references
Open up the migration and change the foreign key so that it points to your custom primary key:
create_table :category_products do |t|
t.references :product, foreign_key: true
t.references :category, foreign_key: { primary_key: "cust_number" }
end
Change the foreign key in the assocation:
class CategoryProduct < ApplicationRecord
belongs_to :category, foreign_key: "cust_number"
belongs_to :product
end
Then add the has_many through: assocations to each end:
class Category < ApplicationRecord
has_many :category_products
has_many :products, through: :category_products
end
class Product < ApplicationRecord
has_many :category_products
has_many :categories, through: :category_products
end
I have two models: person.rb and relationship.rb
I need my :relationships table to reference two rows from the :people table as foreign keys.
Here are the migrations for both tables:
class CreatePeople < ActiveRecord::Migration[5.1]
def change
create_table :people do |t|
t.string :first_name
t.string :second_name
t.integer :age
t.string :gender
t.timestamps
end
end
end
class CreateRelationships < ActiveRecord::Migration[5.1]
def change
create_table :relationships do |t|
t.references :person_a
t.references :person_b
t.string :status
t.timestamps
end
end
end
The idea is the :person_a and :person_b fields will both be individual records from the :people table referenced as foreign keys, while the :status field will just be a description of their relationship ("Married", "Friends", "Separated", etc.)
I'm trying to find out:
1) What is the additional code I have to write in the CreateRelationships migration above in order to set :person_a and :person_b up as foreign keys from the :people table?
2) What code do I need to write in the model files (person.rb and relationship.rb) for both tables below to define the relationship structure I'm talking about?
class Person < ApplicationRecord
end
class Relationship < ApplicationRecord
end
I've found one other question on here that deals with this issue, but the answers given were conflicting, some incomplete, and others working with older versions of Rails. None of them have worked for me.
I'm using Rails 5.1.4
You have defined you migration correctly just add the following in your model to define the relationship between the model.
class Person < ApplicationRecord::Base
has_many :relationships, dependent: :destroy
end
class Relationship < ApplicationRecord::Base
belongs_to :person_a, :class_name => 'Person'
belongs_to :person_b, :class_name => 'Person'
end
This allows you to access the Person that a Relationship belongs to like this:
#relationship.person_a #user assigned as the first person.
#relationship.person_b #user assigned as the second person.
Hope this works.
EDIT: Apologies for a rushed and wrong answer.
Initially, I thought that simple has_many/belongs_to association is possible and sufficient.
class Person < ApplicationRecord
has_many :relationships #dependent: :destroy
end
class Relationship < ApplicationRecord
belongs_to :person_a, class_name: "Person"
belongs_to :person_b, class_name: "Person"
enum status: [:married, :friends, :separated]
end
As #engineersmnky pointed out, has_many association can't work here because there is no person_id column in relationships table. Since we can declare only one custom foreign key in has_many association, it's not possible to declare it here this way. belongs_to will work, but I don't think that's enough.
One way is to skip declaring has_many and stick to custom method for querying relationships:
class Person < ApplicationRecord
def relationships
Relationship.where("person_a_id = ? OR person_b_id = ?", id, id)
end
end
It will give you an ActiveRecord::Relation to work with, containing exactly the records you need. The drawbacks of this solution are numerous - depending on your needs, you will probably need more code for inserting data, starting with a setter method to assign relationships to people...
What could be a real solution, is to have a composite primary key in Relationship model - composed of :person_a_id and :person_b_id. ActiveRecord doesn't support composite primary keys, but this gem seems to fill the gap. Apparently it allows to declare such key and use it as a foreign key in a has_many association. The catch is that your person_a/person_b pairs would have to be unique across relationships table.
I had to do the same for a chat module, this is an example to how you can do it:
class Conversations < ApplicationRecord
belongs_to :sender, foreign_key: :sender_id, class_name: 'User'
belongs_to :recipient, foreign_key: :recipient_id, class_name: 'User'
end
class User < ApplicationRecord
...
has_many :conversations
has_many :senders, through: :conversations, dependent: :destroy
has_many :recipients, through: :conversations, dependent: :destroy
...
end
More explanations at complex-has-many-through
Hope it helps,
You can do like this
In relationship model write
belongs_to : xyz, class_name: "Person", foreign_key: "person_a"
belongs_to : abc, class_name: "Person", foreign_key: "person_b"
In Person model write
has_many :relationships, dependent: :destroy
hope it will help
(I'm translating the code as I write, so I apologize for any mistakes!)
I'm trying to implement a bidirectional self referential relation involving the Associates table and a join table Property. I tried following this post but something is still wrong.
The way it's supposed to work is, an associate can be the proprietor of zero or more associates, and consequently an associate may have zero or more proprietors (makes more sense in the context of the application).
So I created the Property model:
class CreateProperties < ActiveRecord::Migration
def change
create_table :properties do |t|
t.integer :proprietor_id
t.integer :property_id
t.timestamps null: false
end
end
end
So the table contains only the ids of one proprietor and one property, both associates, per entry.
Following the tutorial linked above, I came to this configuration:
Associate.rb:
...
has_many :properties
has_many :related_properties, :through => :properties
has_many :proprietors, :class_name => "Property", :foreign_key => "proprietor_id"
has_many :related_proprietors :through => :proprietors, :source => :associate
...
Property.rb:
belongs_to :associate
belongs_to :related_properties, :class_name => "Associate"
However when I try to use these relations (<% #associate.related_properties.each do |property| %>), I get this error:
PG::UndefinedColumn: ERROR: column properties.related_properties_id does not exist
LINE 1: ... INNER JOIN "propriedades" ON "associados"."id" = "proprieda...
^
: SELECT "associates".* FROM "associates" INNER JOIN "properties" ON "associates"."id" = "properties"."related_properties_id" WHERE "properties"."associate_id" = $1
Basically, the column names are wrong in the generated SQL: properties.related_properties_id should be properties.proprietor_id, and properties.associate_id should be properties.proprietor_id as well.
What have I done wrong, and how can I fix this code to get the correct relations?
You need to setup two seperate associations since the foreign key on Property depends on what the Associates role is.
class Associate
# defines relations where Associate is the "owning" party
has_many :properties_as_proprietor,
class_name: 'Property',
foreign_key: 'proprietor_id'
has_many :properties,
through: :properties_as_property,
source: :property # what to select on Property
# defines relations where Associate is the "owned" party
has_many :properties_as_property,
class_name: 'Property',
foreign_key: 'property_id'
has_many :proprietors,
through: :properties_as_proprietor,
source: :proprietor # what to select on Property
end
class Property
belongs_to :proprietor, class_name: 'Associate'
belongs_to :property, class_name: 'Associate'
end
I have three models: Book, genre, BookGenre, and here are relationships:
class BookGenre < ActiveRecord::Base
belongs_to :book
belongs_to :genre
end
class Book < ActiveRecord::Base
has_many :book_genres
has_many :genres, through: :book_genres
end
class Genre < ActiveRecord::Base
has_many :book_genres
has_many :books, through: :book_genres
end
And then I use seed file to put data into these tables.
But when I want to do rake db:seed again, it showed this error
ActiveRecord::InvalidForeignKey: PG::ForeignKeyViolation: ERROR: update or delete on table "books" violates foreign key constraint "fk_rails_4a117802d7" on table "book_genres"
DETAIL: Key (id)=(10) is still referenced from table "book_genres".
In my seed.rb
Book.destroy_all
Genre.destroy_all
...create data
Add dependent: :destroy option to your has_many definitions.
Check docs
Yet better option to respect data integrity is to set the CASCADE DELETE on the database level: say, you have comments table and users table. User has many comments You want to add a foreign_key to table comments and set deleting the comment whenever the user is destroyed you would go with the following (the on_delete: :cascade option will ensure it):
add_foreign_key(
:comments,
:users,
column:
:user_id,
on_delete: :cascade
)
Try this:
ActiveRecord::Base.connection.disable_referential_integrity do
Book.destroy_all
Genre.destroy_all
# ...create data
end
For example, I have 2 models:
ticket.rb
belongs_to :user
user.rb
has_many :tickets, dependent: :destroy
In the table Tickets I have
t.integer "user_id"
This all works fine. But what if I wish to add a new field assignee_id, and that should be a user from the same table Users?
I can add just a field assigne_id and populate it with user_id I need, but I would like to use a construction like #ticket.assignee.name like I use for #ticket.user.name. Please could you help me?
You can do, Rails allows you to pass bunch of parameters to belongs_to. In your case, You need to specify the foreign key and the class name of the association.
This is how you can join assignee to user table
#ticket.rb
belongs_to :assignee, foreign_key: "assignee_id", class_name: "User"
source: edgeguides.rubyonrails.org
source: api.rubyonrails.org