Using foreign_key in rails associations has_many - ruby-on-rails

Could somebody explain me is my code correct.
I'm trying to get foreign_key option in rails associations.
I have 2 models:
Book and Author
Book db schema:
name
user_id
Author db schema:
name
My models:
class Author < ApplicationRecord
has_many :books, foreign_key: :user_id
end
class Book < ApplicationRecord
belongs_to :author, foreign_key: :user_id
end
Here I don't understand why we should define foreign_key in both models. Is it necessarily?

If you have used the table and column names that Rails expects, then you do not need to explicitly define the foreign_key. In your case, if the foreign key column was named author_id, then you could get by quite simply:
class Author < ApplicationRecord
has_many :books
end
class Book < ApplicationRecord
belongs_to :author
end
However, in your case, the foreign key column is not named according to what Rails expects, so you have needed to explicitly define the foreign key column name. That's fine, but it does make a little more work for you.
In cases where you have explicitly defined the foreign key, you should define it for both associations. Your has_many association will not work without it.
In addition, you should define the inverse association:
class Author < ApplicationRecord
has_many :books, foreign_key: :user_id, inverse_of: :author
end
class Book < ApplicationRecord
belongs_to :author, foreign_key: :user_id, inverse_of: :books
end
Defining the inverse_of can cause ActiveRecord to make fewer queries, and gets rid of a few surprise behaviors. For an explanation of inverse_of, see Exploring the :inverse_of Option on Rails Model Associations by Ryan Stenberg

Related

Rails - Has many on model with multiple references

I have a model CompanyIntro which has two references to a Company:
class CompanyIntro < ApplicationRecord
belongs_to :company_one, class_name: "Company", foreign_key: "company_one_id"
belongs_to :company_two, class_name: "Company", foreign_key: "company_two_id"
...
I would like to do something like:
class Company < ApplicationRecord
has_many :company_intros, class_name: 'CompanyIntro', foreign_key: 'company_one_id'
has_many :company_intros, class_name: 'CompanyIntro', foreign_key: 'company_two_id'
...
But this is not valid
In my Company model, how to I create a has_many for both foreign keys? I am using Rails 6 which dos not allow custom sql for has_many (afaik). I also do not want to write a custom company_intros method on the Company model as I'm using another gem which looks for my has_many relationships.
You can't define has_many assocations where the foreign key is one of two columns. Its just not supported by ActiveRecord as the feature would add tons of complexity.
Using the same name for two assocations also just overwrites the previous assocation. If you want to have a single assocation here you need to add a join table.
class Company < ApplicationRecord
has_many :company_intro_participations
has_many :company_intros, through: :company_intro_participations
end
# for lack of a better name
class CompanyIntroParticipation < ApplicationRecord
belongs_to :company
belongs_to :company_intro
end
class CompanyIntro < ApplicationRecord
has_many :company_intro_participations
has_many :companies, through: :company_intro_participations
end
The alternative is creating a method which joins on company_one_id = companies.id OR company_two_id = companies.id but you will not be able to use that in the same way as an association when it comes to stuff like eager loading.

What is the difference between foreign_key and primary_key when defining an ActiveRecord association?

I am trying to understand the difference between foreign_key and primary_key when defining an association on an ActiveRecord model. Do you need to use both? What is the difference?
class SomeModel < ActiveRecord::Base
has_one :something_else, foreign_key: 'some_id', primary_key: 'some_id'
end
class SomethingElse < ActiveRecord::Base
self.primary_key = 'some_id'
belongs_to :some_model, foreign_key: 'some_id', primary_key: 'some_id'
end
primary_key defines the name of the database field to be used as primary key instead of the default id.
foreign_key defines the name of the database field which keeps references to the primary key field of another model.
See Active Record Associations docs for more.
If you want conceptual understanding on the role of primary and foreign keys, here is one writeup on this topic.
They are on the opposite sides of the association.
Rails takes value of foreign_key and searches model with class_name for records with primary_key of that value
For example:
class PhoneNumber < ActiveRecord::Base
belongs_to :user
end
class User < ActiveRecord::Base
has_one :phone, foreign_key: :this_users_phone_id_field
has_many :others_with_this_number, ->(user){where.not(id: user.id)},
class_name:'User',
foreign_key: :this_users_phone_id_field,
primary_key: :this_users_phone_id_field
end
See more in rails docs

Understanding use of foreign_key and class_name in association

I am new to rails and this is a very basic question. I am trying to understand the need of foreign key and class_name.
has_many :task, foreign_key: "created_by"
has_many :memberships, class_name: "TaskMembership"
Can anyone explain the need of foreign_key & class_name.
Here is the answer of my question
Suppose you have a User model and Post model.And you have to set an association like User has many post
User Model
has_many :posts
Post Model
belongs_to :user
Now suppose your user is some author so we have to set some meaningful name so instead of user we will use author but have to specify which class it is referring
Post Model
belongs_to :author, class_name: 'User'
Now problem will occur because rails will look for author_id column in posts table .So here foreign key will come into picture.We will have to find user_id
Post Model
belongs_to :author, class_name: 'User', foreign_key: 'user_id'
See more better explanation association
has_many association is used for for one-to-many type relationships in rails. For instance, if you have a model User which can has many profiles, your User to Profile association will be has many.
class User < ActiveRecord::Base
has_many :profiles
end
class Profile < ActiveRecord::Base
belongs_to :user
end
If you have a foreign key different than user_id in profiles table, you explicitly specify foreign_key. Same is the case with class name. If your association name is different than actual model name, you explicitly specify class name after association (as you did for memberships).
Hope it helps.
in your model
class First < ActiveRecord::Base
has_many :seconds
end
class Second < ActiveRecord::Base
belongs_to :first
end
and in your second class table,create first_id column

Custom association for Rails models

I'm trying to define a relation between 2 models Customer and CustomerProduct, linking customer.custom_id on customerproduct.customer_id so that mycustomer.customer_products.create() ends up with customerproduct.customer_id = customer.custom_id instead of customer.id.
This is my Customer model:
class Customer < ActiveRecord::Base
belongs_to :organisation
has_many :customer_products, foreign_key: 'customer_id'
end
And my CustomerProduct model:
class CustomerProduct < ActiveRecord::Base
belongs_to :customer
end
I'm not sure how to define the custom_id bit - I tried adding it as a foreign key in belongs_to :customer but this doesn't seem to do anything - Rails still takes customer.id.
Thanks in advance!
Let's just say the answer was quite obvious so I must have been blind!
has_many :customer_products, primary_key: 'custom_id', foreign_key: 'customer_id'

Please explain rails has_many through association

I'm trying to understand rails associations
I've the following tables and I've to define their relations, can any one please help me understand.
The tables are Products, ProductDistributors, and Distributors.
Every product has a distributor, a distributor carries multiple products
I defined these as
class Product < ActiveRecord::Base
has_one :product_distributor
has_one :distributor, through: :product_distributor
end
class ProductDistributor < ActiveRecord::Base
belongs_to :products
belongs_to :distributors
end
class Distributor < ActiveRecord::Base
has_many :product_distributors
has_many :products, through: :product_distributors
end
Is this correct? If not, how can I correct it?
I feel the problem lies in the Distributors class name because it's plural. when you say something like has_many :distributors, Rails by default will link to the Distributor class, but in your case the class name is Distributors.
Adding class_name option to your relationship declarations should work:
class Product < ActiveRecord::Base
has_one :product_distributor
has_one :distributor, through: :product_distributor, class_name: 'Distributors'
end
class ProductDistributor < ActiveRecord::Base
belongs_to :product
belongs_to :distributor, class_name: 'Distributors'
end
class Distributors < ActiveRecord::Base
has_many :product_distributors
has_many :products, through: :product_distributors
end
Also note that your belongs_to should be singular not plural. Please go through the Association guide for details: http://guides.rubyonrails.org/association_basics.html.
As I see, Product has one (belongs to) Distributor, and Distributor has many Products. So you not need to use ProductDistributor
class Product < ActiveRecord::Base
belongs_to :distributor
end
class Distributors < ActiveRecord::Base
has_many :products
end
Just add column *distributor_id* to products table
I recomend you to read Active Record Associations Guid to understand associations
The reason to use has_many through: is that you need to use a join table that isn't named in the standard Rails naming convention. In this case, ProductDistributor has such a table associated (product_distributors) because the Rails convention puts the tables in the name in lexicographic order and pluralizes them. The Rails table name would be distributors_products. If you created such a table with foreign keys to the ids of the distributors and products tables you wouldn't need to specify the join table and you could just say has_and_belongs_to_many :distributors on Products and has_and_belongs_to_many :products on Distributors.
What you have isn't quite right for what you are trying accomplish, frankly. You have a many_to_many relationship between Product and Distributors. If you really want a many_to_one relationship between Product and Distributor you should make the following changes:
schema changes: Drop the product_distributors table and add a foreign key from products to distributors.
drop_table :product_distributors
change_table :products do |t|
t.references :distributors
end
model changes: Delete the model for ProductDistributor and change Product and Distributor to reference each other directly. Also note that Distributors is renamed to Distributor.
class Product < ActiveRecord::Base
belongs_to :distributor
end
class Distributor < ActiveRecord::Base
has_many :products
end

Resources