So I have a DB relationship where a User has a PreferenceList. I have set up a preference_list_id in the database table, and I have set up a belongs_to :user relationship with PreferenceList and a has_one :preference_list relationship with User.
Here's the weird part. If I do user.preference_list = preference_list, I can access user.preference_list and it will give me the correct instance of preference_list. However, if I do user.preference_list_id (a valid, column, I've checked), it gives me nothing even after I have added preference_list to user.preference_list. I need the id to be updated for various database operations. What am I doing wrong?
The foreign key goes on the table for the class declaring the belongs_to association. So if you have
class PreferenceList < ActiveRecord::Base
belongs_to :user
end
class User < ActiveRecord::Base
has_one :preference_list
end
table preferences_lists should have user_id columns. Rails will handle this columns properly. You can add preference_list_id column to users table as well. But this column has no special meaning, Rails will do nothing with it by default. Try to change belongs_to/has_one sides if you really need to operate preference_list_id. Or use user.preference_list.id
Related
In a Rails 4 application, I have an STI model that stores metadata in a jsonb column.
Base Class:
class Post < ActiveRecord::Base
...
end
Subclass:
class JobPost < Post
# has a jsonb column for metadata
end
One of the data attributes in the metadata column of a JobPost is a foreign_key reference to another table (company_id). I'd like to add a belongs_to :company reference in the JobPost model. It seems like this should be possible by doing something like
class JobPost < Post
belongs_to :company do
Company.find_by_id self.metadata['company_id']
end
end
but that doesn't appear to work. Help?
Note: I am not necessarily intent on using belongs_to rather than writing needed methods like def company by hand, but I do need a way to eager load companies when listing job posts. If there's a way to do that eager loading without a belongs_to relation I'm all ears.
Update1
I have also tried the following, which doesn't appear to work either:
class JobPost < Post
belongs_to :company, foreign_key: "(posts.metadata->>'company_id')::integer".to_sym
end
Update2
To be more clear about my intentions and need:
1) A JobPost belongs_to a Company, but a Post (and other subclasses of Post) does not. I'd prefer not to jankily add the company_id column to the posts table when it won't be used by the other subclasses.
2) A JobPost could justify having it's own table (perhaps the relationship with a Company is enough to justify it). There are reasons why this wouldn't be ideal, but if that's the only answer I'm open to it. I'd, however, like a more definitive "what you're trying to do can't be done" response before going down this road, though.
The primary question is whether you can customize belongs_to so that it uses the metadata column rather than expecting the foreign key to be a column in the table.
The secondary question is whether you can eager load companies alongside job posts without having that belongs_to relation set up.
EDIT
UPD 2
You need to add "company_id" column to the base class of your STI table. If JobPost inherits from Post, and it should have "company_id" then add the "company_id" column to Post (base table).
Remember STI stands for "Single Table Inheritance" so there is only one table on database schema level. Imagine a column of a Post table, where few data records are the foreign key entries for Companies with company_id and what about the other records of this column with non JobPost subclass types, are they null/empty? Hence the relationship is defined with parent STI table and subclass inherits these relations. Additional type column in STI defines the subclass type.
Check here
You may need to dig further on Polymorphic classes instead of STI if both JobPost and Post have relationship with Company, else create two separate model, as they tend do have some unique relationships and column fields.
UPD
Based on updated ask
app/model/company.rb
class Company < ActiveRecord::Base
has_many :posts
delegate :jobposts, to: :posts
end
app/model/post.rb
class Post < ActiveRecord::Base
belongs_to :company
self.inheritance_column = :ptype
scope :job_posts, -> { where(ptype: 'JobPost') }
def self.ptype
%w(JobPost)
end
end
app/models/jobpost.rb
class JobPost < Post; end
Create a company
company = Company.create!(company_params)
Create some posts and add them to the company
company.posts << JobPost.new(jobpost_params)
To fetch jobpost by company relationship
company.job_posts
In case you are storing company_id in jsonb in any which column, just format your jobpost_params hash input accordingly and it should do the deed for you
OLD ASK
To find by primary key
Company.find(id)
In your case, id is self.metadata['company_id']
To find by other keys
Company.find_by(key: value)
Company.find_by_id is no more recommended
Please remove do and end after belongs_to in your model, instead in your controller you can write:
Jobpost.all.each do |x|
# your do
end
regarding foreign key, as rails is convention over configuration, it by default includes company_id reference to Jobpost which you can change in your Company.rb model
I got a trouble in my rails project to achieve foreign key concepts, I had gone through http://guides.rubyonrails.org/association_basics.html and few of tutorials and came to know that this can be achieved by association and I had tried using the same but yet to succeed.
Actually I am new to Rails and I continue running into the same path. Any guidance much appreciated...
As per my scenario , I am having two models User and Region
In User model there are three fields user_id, user_name, user_region_id (foreign key)
In Region model => region_id(primary key), region name
I want to display In user_id user_name region_name (using foreign key) on index page of of user
so how an achieve this ??Please help me out
your help will be greatly appreciated
thanks in advance for your help
Conventionally you should not prefix your column names with user_ and region_, because they are already present in the table named like that. Removing the prefix you can achieve your desired result using following code in your models:
class User < ActiveRecord::Base
belongs_to :region
end
class Region < ActiveRecord::Base
has_many :users
end
I think the easiest way to remember association in rails is that the model which has the foreign key should have the belongs_to declaration. So in your setup, since a Region has many users, it would make sense to have the foreign key in the users table.
Assuming that the foreign key in the users table is called region_id, you should declare the following association in the User model.
class User < ActiveRecord::Base
belongs_to :region
end
So this works because rails assumes that the foreign key is under a column called region_id. AND the primary key is under a column called id in the regions table.
On the side of the association, that's when you decide if it's a has_one or has_many. Since it has already been decided that it should be has_many, it's as easy as just declaring that association in the Region model
class Region < ActiveRecord::Base
has_many :users
end
If you want to keep using region_id as the primary key for the regions table, change the association in the user model to
class User < ActiveRecord::Base
belongs_to :region, primary_key: :region_id
end
AND make sure to declare it in the region model so that doing region.users will work
class Region < ActiveRecord::Base
self.primary_key = 'region_id'
end
I have a relationship between two models that is currently a many to many. I'd like to move it to a one to one relationship, because that's what all of the data has turned out to be.
Is there a seamless way to do this? Via a migration and model change combo? My models would be simple to just change as shown below:
Now
class App < ActiveRecord::Base
has_and_belongs_to_many :owners
end
class Owner < ActiveRecord::Base
has_and_belongs_to_many :apps
end
Changing to
class App < ActiveRecord::Base
belongs_to :owner
end
class Owner < ActiveRecord::Base
has_one :app
end
But how do I update the database to reflect these changes without losing any of the data I currently have?
Add an owner_id field to your apps table and then iterate through your apps_owners table and set the owner_id field in apps to the owner_id field in apps_owners for the apps record whose id is equal to app_id.
Assuming you're correct and there aren't multiple apps_owners entries for the same app_id following SQL should do it (not tested), although I'm not familiar with how to incorporate raw SQL into migrations:
update apps set owner_id=(select owner_id from apps_owners where apps.id = apps_owners.app_id)
You won't be able to do this simply. The main problem is that has_and_belongs_to_many relationships use a tertiary database table (apps_owners in your case) to hold the relationship information.
After you create the appropriate migration to add owner_id to your App model, you'll need to create a rake task that reads the app_owner table and re-creates the relationships.
Model I
class TimeLog < ActiveRecord::Base
has_one :custom_time_fields, :dependent => :destroy
end
Model II
class CustomTimeFields < ActiveRecord::Base
belongs_to :time_log
end
above design in terms of database will be
timelogs table + custom_time_field_id(foreign key)
custom_time_fields
So when i delete timelog entry its associated 'custom_time_field' will be auto deleted by rails
But i want database design like following
Table I:
time_logs
Table II
custom_time_fields (having time_log_id as foreign key)
Table I will have Zero or one association of Table II
How can i represent above database design in Rails models, so that when i delete time_log, associated custom_time_field entry is auto deleted.
You have to switch the has_one and belongs_to relations of your models to change the table containing the foreign key (the model with the relation belongs_to is the one holding the foreign key). Do not forget to adapt your migrations according to the change (to declare the time_log_id column).
I think the "zero or one" relation you're looking for is the has_one relation. This relation is not mandatory (unless you add a validation to it).
What is the difference between a belongs_to and a has_one?
Reading the Ruby on Rails guide hasn't helped me.
They essentially do the same thing, the only difference is what side of the relationship you are on. If a User has a Profile, then in the User class you'd have has_one :profile and in the Profile class you'd have belongs_to :user. To determine who "has" the other object, look at where the foreign key is. We can say that a User "has" a Profile because the profiles table has a user_id column. If there was a column called profile_id on the users table, however, we would say that a Profile has a User, and the belongs_to/has_one locations would be swapped.
here is a more detailed explanation.
It's about where the foreign key sits.
class Foo < AR:Base
end
If foo belongs_to :bar, then the foos table has a bar_id column
If foo has_one :bar, then the bars table has a foo_id column
On the conceptual level, if your class A has a has_one relationship with class B then class A is the parent of class B hence your class B will have a belongs_to relationship with class A since it is the child of class A.
Both express a 1-1 relationship. The difference is mostly where to place the foreign key, which goes on the table for the class declaring the belongs_to relationship.
class User < ActiveRecord::Base
# I reference an account.
belongs_to :account
end
class Account < ActiveRecord::Base
# One user references me.
has_one :user
end
The tables for these classes could look something like:
CREATE TABLE users (
id int(11) NOT NULL auto_increment,
account_id int(11) default NULL,
name varchar default NULL,
PRIMARY KEY (id)
)
CREATE TABLE accounts (
id int(11) NOT NULL auto_increment,
name varchar default NULL,
PRIMARY KEY (id)
)
has_one and belongs_to generally are same in a sense that they point to the other related model. belongs_to make sure that this model has the foreign_key defined.
has_one makes sure that the other model has_foreign key defined.
To be more specific, there are two sides of relationship, one is the Owner and other is Belongings. If only has_one is defined we can get its Belongings but cannot get the Owner from the belongings. To trace the Owner we need to define the belongs_to as well in the belonging model.
One additional thing that I want to add is, suppose we have the following models association.
class Author < ApplicationRecord
has_many :books
end
If we only write the above association, then we can get all books of a particular author with
#books = #author.books
but, for a particular book, we can't get the corresponding author with
#author = #book.author
To make the above code work we need to add an association to the Book model as well, like this
class Book < ApplicationRecord
belongs_to :author
end
This will add method 'author' to the Book model. For mode details see guides
has_one
This method should only be used if the other class contains the foreign key.
belongs_to
This method should only be used if the current class contains the foreign key.
From a simplicity standpoint, belongs_to is better than has_one because in has_one, you would have to add the following constraints to the model and table that has the foreign key to enforce the has_one relationship:
validates :foreign_key, presence: true, uniqueness: true
add a database unique index on the foreign key.