Rails object both belongs_to and has_one - ruby-on-rails

I'm having a trouble with grasping a proper relationship between two Rails/ActiveRecord classes.
I have a User, which can both create a Slip and simultaneously be an addressee of another slip. Each user can create any number of slips but only one user as the addressee of a given slip.
From the db perspective I have two integer (key) columns for 'user_id' (author) and 'addressee' in the slips table and no reference on the users table.
This is my current approach which is not working at all:
class User < ApplicationRecord
has_many :slips
belongs_to :slips, :foreign_key => 'addressee'
end
class Slip < ApplicationRecord
belongs_to :user
has_one :addressee, :through => :user
end
Please direct me to the proper way of binding these objects.
Thanks and have a nice day!

It should look something like this (the second one could just be called 'slip' but I've called it 'addressor_slip' to avoid confusion):
class User < ApplicationRecord
has_many :slips
has_one :addressor_slip, :class_name=> 'Slip', :foreign_key => 'addressee'
end
class Slip < ApplicationRecord
belongs_to :user
belongs_to :addressee, :class_name=> 'User', :foreign_key => 'addressee'
end
You could also create an association from User to User through :addressor_slip

Look at many to many relationships here: http://api.rubyonrails.org/classes/ActiveRecord/Associations/ClassMethods.html
You're probably looking for has_and_belongs_to_many.

Related

How to associate through a join table that reference the same model?

I have a model called House and I want to be able to associate houses with each other to show recommendations.
So I would expect that given a house, I should be able to ask: house.recommended_houses. A house could be recommended for more than one house.
I was thinking on having a table that would store this association (I don't know the name yet), so it would have the following columns:
recommended_house_id
recommended_for_house_id
I am failing to understand how would I hook this up with my House model. What would the associations look like, and also what name should I be using for that join model?
This should get you started:
class House < ApplicationRecord
has_and_belongs_to_many :recommendations,
class_name: "House",
foreign_key: "recommended_by_id",
association_foreign_key: "recommendation_id"
end
What you're describing is called a self-referential association.
You can set up a join table (recommendations) and the associated model:
class Recommendation < ActiveRecord::Base
belongs_to :house
belongs_to :recommended_house, :class_name => 'House'
end
and then use has_many, :through relationships within the House model to set up the relationships you're looking for.
class House < ActiveRecord::Base
has_many :recommendations
has_many :recommended_houses, :through => :recommendations
has_many :inverse_recommendations, :class_name => "Recommendation", :foreign_key => "recommended_house_id"
has_many :recommended_by_houses, :through => :inverse_recommendations, :source => :house
end
Now you can use both house.recommended_houses and house.recommended_by_houses.

How to implement ancestry for this specific situation

We have a Real-Estate app in which the User can be a Renter or Landlord(Owner). Renters can search for specific houses listed by the Owners. Renters can also add other persons(friends or acquaintances who are staying with that specific renter). In the app we treat them as Coapplicants.
Models
# user.rb
class User < ActiveRecord::Base
has_one :renter
has_one :owner
end
# renter.rb
class Renter < ActiveRecord::Base
belongs_to :user
has_many :coapplicants
end
# coapplicant.rb
class Coapplicant < ActiveRecord::Base
belongs_to :renter
end
Now as to increase the number of users for the app,we implemented a mailing system which sends an Welcome Mail(when the Renter adds a Coapplicant) to signup as a User.And that Coapplicant can choose to be Renter and can add many Coapplicants too. And the process goes on again resulting in increasing the users.
It's like a tree structure and now I want to set-up a perfect database relations(associations) to track the users flowing in and through which renter/coapplicant they are coming.
Now the Current Model structure(not yet developed) looks like this
# user.rb
class User < ActiveRecord::Base
has_one :renter
has_one :owner
end
# renter.rb
class Renter < ActiveRecord::Base
belongs_to :user
has_many :coapplicants
has_many :coapp_renters,
:through => :coapplicants
has_many :inverse_coapplicants,
:class_name => "Coapplicant",
:foreign_key => "coapp_renter_id"
has_many :inverse_coapp_renters,
:through => :inverse_coapplicants,
:source => :renter
end
# coapplicant.rb
class Coapplicant < ActiveRecord::Base
belongs_to :renter
belongs_to :coapp_renter,
:class_name => "Renter"
end
I guess i messed up things a bit. Which database-relationships(associations) would be the best for my current situation.
Can someone throw some light on this please.I'm thinking about using the ancestry gem but how to implement to my current situation.
I've found that sometimes even a small change in perspective when designing your associations can make them flow a lot more naturally.
You have focused exclusively on person entities; User.find(1).renter for example isn't very intuitive, since both models depict essentially the same person.
Instead of trying to model what people are, I would try to model what they have. In this case, instead of a User having a Renter, let them have many Rentals:
class User < ActiveRecord::Base
has_many :rentals,
foreign_key: 'renter_id'
end
class Rental
belongs_to :renter,
class_name: 'User'
belongs_to :property
end
I assume here that you have a model Property that stands for what is being rented - leave that out if it doesn't exist.
It's the same thing for owners. A User becomes an owner just by having Ownerships:
class User < ActiveRecord::Base
has_many :ownerships,
foreign_key: 'owner_id'
end
class Ownership
belongs_to :owner,
class_name: 'User'
belongs_to :property
end
A co-application is slightly different in that it belongs to a Rental:
class CoApplication
belongs_to :co_applicant,
class_name: 'User'
belongs_to :rental
end
class Rental
has_many :co_applications
end
class User < ActiveRecord::Base
has_many :co_applications,
foreign_key: 'co_applicant_id'
has_many :received_co_applications,
through: :rentals,
source: :co_applications
end
Now your Users can be owners, renters, co-applicants - all at the same time. And these associations allow you to capture everything that happened - who signed on whom through what is just a matter of chronological order.
From here on it's a matter of nesting your has_many :through associations to get whatever you want.
Want to know the properties a landlord owns?
has_many :owned_properties,
through: :ownerships,
source: :property
The rentals to her properties?
has_many :leases,
through: :owned_properties,
source: :rentals
The people who rented her properties?
has_many :renters,
through: :leases,
source: :renter
Same thing with co-applications. Want to know who co-applied with a user?
has_many :co_applicants,
through: :received_co_applications,
source: :co_applicant
I would refactor your code and make the co-applicants just a renter that is a child of another renter
in your renter model you have to add a "parent_id" to know whom the co-applicants belongs to.
now in your model you can do something like
#renter.rb
class Renter < ActiveRecord::Base
belongs_to :user
has_many :children, :class_name => "Renter"
belongs_to :parent, :class_name => "Renter"
end
# Example calls
Renter.first.children
Renter.first.parent
I hope this helps
Create a table called Relationship (or something) with two foreign_ids concerning what you want the User and Renter to be able to do with one another (example to be able to "Follow" one another => Follower_id and Following_id). Define methods in your Models related to those ids and then call those methods in your views to display the relations.

has_many :through relationships explained

I'm new to Rails and have some doubts about the kind of relationship do I need to use. Here is the case.
I have two models Offer and User, a user could belong to to many offers and offers can have many user. Also the users create the offers.
I think I have to use a has_many :through ralationship. For example I've created another model "Applicant". Applicant belongs_to user and belongs_to offer. But how is the relationship from the user and offer model? For example:
User Model
has_many :offer, :through => :applicant
Offer Model
has_many :user, :through => :applicant
My doubt is because I already have this two relationship
User Model
has_many :offers, :dependent => :destroy
Offer Model
belongs_to :user
After solve this, I guest I have to save the record in the applicant model from the applicanst_controller, right?
Thanks in advance
What you have described is a many-to-many relationship using a join table. You're actually pretty close but you just need to remove the has_many :offers, :dependent => :destroy from your user model and the blongs_to :user in your offer model. It should look something like this:
class User < ActiveRecord::Base
has_many :offers, :through => :applicants
end
class Applicant < ActiveRecord::Base
belongs_to :users
belongs_to :offers
end
class Offer < ActiveRecord::Base
has_many :users, :through => :applicants
end
You don't have to worry about the dependent destroy part as associations are automatically removed as the corresponding objects are removed. With a many to many association it doesn't really matter how you go about building the relationship. Either of the following will work:
#user.offers << #offer
#offers.users << #user
If you don't need to store any information specific to your applicant join table (e.g., time stamps, descriptions) you might instead want to look at a has_and_belongs_to_many relationship. Check out choosing between has_many_through and has_and_belongs_to_many for reference.
Edit
Heres the code for a HABTM relationship:
class User < ActiveRecord::Base
has_and_belongs_to_many :offers
end
class Offer < ActiveRecord::Base
has_and_belongs_to_many :users
end

Proper Rails Association to use

I am trying to create an association between two tables. A student table and a computer table.
A computer can only ever be assigned to one student (at any one time) but a student can be assigned to multiple computers.
This is what I currently have in mind. Setting up a has-many through relationship and modifying it a bit.
class Student < ActiveRecord::Base
has_many :assignemnts
has_many :computers, :through => :assignments
end
class Computer < ActiveRecord::Base
has_one :assignment
has_one :student, :through => :assignments
end
class Assignment < ActiveRecord::Base
belongs_to :student
belongs_to :computer
end
Does this seem like the best way to handle this problem? Or something better sound out quickly to the experts here. Thanks!
You need first to decide if a simple one-to many relationship is enough for you.
If yes, it gets a lot easier, because you can get rid of the Assignment-class and table.
Your database-table "computers" then needs a student_id column, with a non-unique index
Your models should look like this:
class Computer < ActiveRecord::Base
belongs_to :student
end
class Student < ActiveRecord::Base
has_many :computers, :dependent => :nullify
end
"dependent nullify" because you don't want to delete a computer when a student is deleted, but instead mark it as free.
Each of your computers can only be assigned to a single student, but you can reassign it to a different student, for example in the next year.
Actually your approach is fine, as one offered by #alexkv. It is more discussion, than question.
Another thing if you want to use mapping table for some other purposes, like storing additional fields - then your approach is the best thing. In has_many :through table for the join model has a primary key and can contain attributes just like any other model.
From api.rubyonrails.org:
Choosing which way to build a many-to-many relationship is not always
simple. If you need to work with the relationship model as its own
entity, use has_many :through. Use has_and_belongs_to_many when
working with legacy schemas or when you never work directly with the
relationship itself.
I can advise you read this, to understand what approach better to choose in your situation:
http://api.rubyonrails.org/classes/ActiveRecord/Associations/ClassMethods.html
http://blog.hasmanythrough.com/2006/4/20/many-to-many-dance-off
You can also use has_and_belongs_to_many method. In your case it will be:
class Student < ActiveRecord::Base
has_many :assignemnts
has_and_belongs_to_many :computers, :join_table => 'assignments',
end
class Computer < ActiveRecord::Base
has_one :assignment
has_and_belongs_to_many :student, :join_table => 'assignments',
end
or you can rename assignments table to computers_students and remove join_table
class Student < ActiveRecord::Base
has_many :assignemnts
has_and_belongs_to_many :computers
end
class Computer < ActiveRecord::Base
has_one :assignment
has_and_belongs_to_many :student
end

Rails model advice

I need some suggests for the model relationship that I'm going to develop:
I want to create a research table that keep track of all the reasearches performed inside the platform.
In the platform I'm developing, an user can search for other users.
In order to do it, I thought to create 3 fields in the research table: Performer (user_id that submit the research) Research_string (The string that the performer submit) and Results (that is one or more user).
Of course the relationship among user and research tables will be many to many, but note that the research table has 2 different column that involves the user_id (performer and results) so how can I specify to rails ? I thought something like that :
class User < ActiveRecord::Base
has_many :performed_research, :through => :research_table
class Research < ActiveRecord::Base
has_many :users
But how can I specify that the has_many users in the Research tables implies two different relations depending by the column ?
Tnx
EDITED: my solution
Your solution is not correct, because the user has only the research performed and not the research where he is resulted. I made another solution, that it's not the best of clear but it works, I would like to have your judge:
class Research < ActiveRecord::Base
belongs_to :searcher, :class_name => 'User', :foreign_key=> 'submitter_id'
has_many :found_users, :through=>:user_researches,:source=>:user
#It is necessary to let the association work without class_name it would look for
userResearch class
has_many :user_researches, :class_name =>'User_research'
end
class User_research < ActiveRecord::Base
belongs_to :user
belongs_to :research
end
class User < ActiveRecord::Base
# It returns a list of research performed by the user
has_many :researches, :foreign_key => 'submitter_id'
# It is necessary to let the searcher relationship works
has_many :user_researches, :class_name =>'User_research'
#==> Searcher will return an Array of research where the user's skill has been researched
has_many :follower_researches, :through => :user_researches, :source=>:research
end
I say that it;s not the best because the follower_research of the user model, show an array of research when he has been results ... and not an Array of submitter that searching him, so to obtain them, I have to scroll the array of research and then take the searcher field ... Are you able to perform an improvement (hopefully less complex than this)
Do you mean something like this?
class User < ActiveRecord::Base
has_many :research_entries
class ResearchEntry < ActiveRecord::Base
has_one :performer, :class_name => 'User'
has_and_belongs_to_many :resulting_users, :class_name => 'User'
Update: I changed this to use the has_and_belongs_to_many relationship which will allow you to use a join table to connect many users into "resulting_users" field of the ResearchEntry
Something like this:
class User < ActiveRecord::Base
has_many :submitted_researches, :class_name => 'Research', :foreign_key => 'submitter_id'
has_manu :researches, :through => :performed_researches
class Research < ActiveRecord::Base
belongs_to :submitter, :class_name => 'User'
has_many :researchers, :through => :performed_researches, :class_name => 'User'
class PerformedResearch < ActiveRecord::Base
belongs_to :user
belongs_to :research
See: http://guides.rubyonrails.org/association_basics.html#the-has_many-through-association
Update: I think I misread something. What is results? and how is it related to users?
Update 2: Still not sure I understand but here's another stab
I originally thought you were doing something related to academic research and the Research model was for research papers with the performer being scientists that did the research.
From re-reading your question I'm now thinking you have a rails application with some users and a search feature that lets a user search for other users and for each search you are trying to keep track of which user did the search and which users they found.
Let me know if any of these assumptions are wrong.
Based on that:
class User < ActiveRecord::Base
has_many :searches #all the searches this user performed
class Search < ActiveRecord::Base
belongs_to :searcher, :class_name => 'User' # the user who performed the search
has_many :found_users, :though => :user_search_results, :foreign_key => 'user_id' # using foreign_key because I want to change the name to 'found_users'
#alternatively if you don't want to use foreign_key use the line bellow instead
#has_many :users, :thorough => :user_search_results
class UserSearchResult < ActiveRecord::Base
belongs_to :user
belongs_to :search

Resources