Rails Associations - Do I need has many and also belongs to together - ruby-on-rails

If I have a class Category and a class Post, post belongs to a category then I make a t.belongs_to in Post migration, but do I have to necessarily declare a t.has_many in Category? If not, if I have to use just one, which is the best to choose?

Both belongs_to and has_many will just create an association method.
For example, use this:
class User
has_many :cars
end
if you want to be able to access cars from current user. Such as: current_user.cars.
Or use this:
class Car
belongs_to :user
end
if you want to be able to access user from the instance of car. For example: Car.lost_and_found.user.
In most cases, you will usually need both ways, so then use both.

No you don't need to. t.has_many is not available on migrations and also more importantly, doesn't do anything for the table. However, belongs_to actually translates to a foreign key in the table linking the relationship.
For all methods that exist for a create_table e.g. creating references which also an alias for belongs_to refer to this documentation:
http://apidock.com/rails/ActiveRecord/ConnectionAdapters/SchemaStatements/create_table#227-All-methods

Related

setting up a belongs_to relation when the foreign key is stored in metadata

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

Is it possible to have multiple has_many associations with one specific model in Rails?

I have two models with the following associations:
organization.rb
class Organization < ActiveRecord::Base
has_one :user, as: :identifiable
has_many :speakers
#has_many :cast_items
end
speaker.rb
class Speaker < ActiveRecord::Base
has_one :user, as: :identifiable
#has_many :cast_items
end
As you can see, I've commented out an association with the CastItem model.
I want a Speaker to add multiple CastItems. Also, an Organization must be able to add multiple CastItems. When an Organization adds a CastItem, it does not necessarily belongs to a Speaker who is associated with an Organization. In other words an organization must be able to add a CastItem to itself or to a Speaker who is associated with him.
Will it be completely valid to put the has_many :cast_items in both models, or are there more practical design options?
Yes, you can do that. Remember to add organization_id and speaker_id to your cast_items model.
You can check out this link, http://guides.rubyonrails.org/association_basics.html , some useful information regarding many to many and one to many associations.
Personally, in your case, I will use has_many :through
You can definitely do that. I can't think of any reason that would be bad and it's often necessary.
You may want to look up the 'delegate' method for when you're creating CastItems, and have them always created by Organizations.
Also, make sure that if you have a :speaker_id on your CastItem that it can accept nil or false.

Is the has_may relationship on rails necessary or the belong_to is enough?

I have a model with a var reference to another one.
User -> Profile
When I have generated the Profile model I used the references
feature so it has generated the corresponding migration
....
t.references :user
....
My question is do I have to add a relationship on the User model too?
has_one :Profile
Yes, you need both code in two models and the migration you mentioned.
class User < AR
has_one :profile
end
class Profile < AR
belongs_to :user
end
has_one and belongs_to are just methods which adds some more methods to your model. This means, you can have belongs_to defined on one model and no has_one on the other. The only problem is that you would be able to call profile.user, but no user.profile.
It is absolutely up to you which methods you want to be defined and which you don't need. If you never ever want anyone to call profile.user, but want user.profile just call has_one :profile. In general those method shares nothing except that their using same foreign key column.
It is however worth mentioning, that this is usually advised to declare reverse association - it is not needed for things to work though.

Mongoid, how to keep relations in sync for common parent model?

I have three models:
Agency
has_many :owners
has_many :properties
Owner
belongs_to :agency
has_many :properties
Property
belongs_to :owner
belongs_to :agency
The agency.properties relation should refer to all properties that all owners have, but when I create a property inside an owner, the agency.properties relation is not created. I want this relation to be automatically fulfilled, as well as deleted when the owner or the property is deleted.
How can I achieve this behavior with mongoid?
You could also write something like:
Agency
has_many :owners
Owner
belongs_to :agency
has_many :properties
Property
belongs_to :owner
And then add an instance method in your Agency model:
def properties
owners.map(&:properties).flatten.uniq
end
However, this approach will query your database to retrieve the owners and then will query your DB again once per each owner to retrieve each owner's properties.
Hope this could help.
EDIT
There is another solution which implies just 2 queries:
def properties
Property.where({:owner_id.in => owner_ids})
end
PROS:
It uses only two queries.
It returns a Mongoid Criteria (the previous solution returned an array). Thus you can chain scopes and so on ( i.e. my_agency.properties.sold #if you have defined a sold scope)
CONS:
This code seems less readable.
Additionally it's less maintainable. If you change the foreign key in the Owner-Property relation you should update this method (Property.where({:foreign_key...}) or you change the way an owner has many properties. The first option is still valid as long as each owner properties can be found with the instance method some_owner.properties.
As this is a pretty specific use case, there is no way to 'automatically' do this using mongoid 'out of the box'. What I would suggest you is to have some before_save hooks, that would guarantee the state of your relations. You also, depending of your use case, Property could be a denormalized(embedded) object inside Owner and Agency.

Listing the names of associated models

class Article < ActiveRecord::Base
has_many :comments
belongs_to :category
end
Is there a class method for Article with which I can retrieve a list of associations? I know by looking at the model's code that Article is associated to Comment and Category. But is there a method to get these associations programmatically?
You want ActiveRecord::Reflection::ClassMethods#reflect_on_all_associations
So it would be:
Article.reflect_on_all_associations
And you can pass in an optional parameter to narrow the search down, so:
Article.reflect_on_all_associations(:has_many)
Article.reflect_on_all_associations(:belongs_to)
Keep in mind that if you want the list of all the names of the models you can do something like:
Article.reflect_on_all_associations(:belongs_to).map(&:name)
This will return a list of all the model names that belong to Article.

Resources