I have a requirement to have a small system where I have a number of "clubs", and members of this club. A user can be a member of more than one club.
I have the models defined:
club:
has_many :club_members
has_many :users , through: club_members
user:
has_many :club_members
has_many :clubs , through: club_members
club_member
belongs_to :club
belongs_to :user
I have managed to come up with a crude hack (selection list, and storing the member ids as a comma-separated list in the club, but that is so wrong it's making my eyes bleed just looking at the code ;)
What I am trying to achieve is to go to a form, add / remove members, and then when the form is posted , manipulate the models above to permanently save the data accordingly.
So, I actually have 2 questions:
1) Are the models above right ?
2) teaching a man to fish, are there any examples or projects that I can browse / examine that show the relationship management of adding / removing members of a club, both UI and backend ?
many thanks
I would suggest a look a these videos on railscasts regarding complex forms:
http://railscasts.com/episodes?utf8=%E2%9C%93&search=complex+forms
and a look at the accepts_nested_attributes method:
http://api.rubyonrails.org/classes/ActiveRecord/NestedAttributes/ClassMethods.html
Related
I'm trying to figure out the best way to model a many-to-one relationship in rails where there are multiple scopes to the relationship.
An example would be a restaurant has-many photos. I want to be able to call
restaurant.lounge_photos
and receive only the lounge photos,
but also be able to call
restaurant.food_photos
and receive just the food photos.
The two methods I can think of are:
to use multiple joins table, and a has_many to has_one relationship for each type of photo.
to add a 'type' attribute to the photo model and write a scoping method.
Both of these seem a bit clunky to me.
Is there a better solution here?
I think you have to go has_many and Single Table Inheritance(STI), as follow.
Make association with restaurant and photo
class Restaurant < ActiveRecord::Base
has_many :photos
end
class Photo < ActiveRecord::Base
belongs_to :restaurant
end
Then you have to use STI in Photo model. The reason is that you have almost all fields are common for lounge_photos and food_photos.
OR
Using scope directly you can differentiate it and achieve your goal.
For more details of use STI you can refer this link.
This is one way, using a type column
has_many :food_photos,
class_name: 'Photo',
foreign_key: :restaurant_id,
-> { where(type: 'food') }
has_many :lounge_photos,
class_name: 'Photo',
foreign_key: :restaurant_id,
-> { where(type: 'lounge') }
I've been working on this issue for a few days now, and the other Stack Overflow posts I've looked at are either really old, or not quite what I need.
Problem:
There are three models involved:
Trip - represents a trip and its associated information, NOTE: does not include any information about crew members.
Milestone - represents an action taken on a trip (for the purposes of this problem, when a crew member embarks or disembarks).
CrewMember - represents a single crew member on the trip, each crew member has an embark and disembark Milestone which may be shared with other crew members if they embark or disembark at the same place. CrewMembers are linked to Milestones by both their embark_milestone_id and disembark_milestone_id.
Now I'm working on a form where a user can edit all the information from a trip, this includes which CrewMembers are present on the trip, what their roles are, and where they get embark/disembark. The trip is being edited, so the whole page is wrapped in a form_for builder.
The segment of the page that edits CrewMembers is wrapped in a fields_for builder. The Trips model is configured to accept nested attributes for the CrewMember model. The number of crew members is variable, and each needs to have their information edited separately (so I can't use select_tags as far as I know). This works fine to display the crew members in the view, but does not work when I actually try to update, because CrewMembers are associated with Milestones through both embark_milestone_id and disembark_milestone_id instead of just milestone_id.
Question:
So with all that in mind, is there a way to 'teach' rails to recognize CrewMembers through Milestones from a trip form by either embark_milestone_id, disembark_milestone_id or preferably both?
My impression is that this is not the 'Rails' way of doing something like this, but I wanted to post the idea to the community before moving to a messier work-around.
Any advice is greatly appreciated. Thank you for your time.
Model Code as Requested:
class CrewMember < ApplicationRecord
belongs_to :guide
belongs_to :milestone
end
class Trip < ApplicationRecord
belongs_to :launch_slot, required: true
has_many :milestones
has_many :places, through: :milestones
has_many :crew_members, through: :milestones, foreign_key: embark_milestone_id
accepts_nested_attributes_for :milestones
accepts_nested_attributes_for :crew_members
end
class Milestone < ApplicationRecord
belongs_to :trip
belongs_to :place
has_many :crew_members
end
It sounds like you want two belongs_to relationships from CrewMember to Milestone. You’ll need to give these relationships different names, and then override the default class name using the class_name option (see docs):
class CrewMember < ApplicationRecord
belongs_to :guide
belongs_to :embark_milestone, class_name: :Milestone
belongs_to :disembark_milestone, class_name: :Milestone
end
Then from the Milestone class you’ll need to define two has_many relationships back to the CrewMember class. This time you’ll also need to specify the foreign_key, as that can’t be inferred either:
class Milestone < ApplicationRecord
belongs_to :trip
has_many :embarking_crew_members, class_name: :CrewMember,
foreign_key: :embark_milestone_id
has_many :disembarking_crew_members, class_name: :CrewMember,
foreign_key: :disembark_milestone_id
end
Your next question then is how to get a collection of allCrewMembers from the Trip model. You suggested that you'd want to return either crew members connected via the embark_milestone_id OR the disembark_milestone_id. There’s no straightforward way of doing this – you'd have to write your own method which both the embarking_crew_members and disembarking_crew_members relationships, joins them together, and then removes any duplicates. However, is it really possible to have a CrewMember who disembarks but didn't embark? ;-)
I'm struggling with the best way to design/query my db on a new Rails app. This is what I have in place right now:
documents:
title
has_many :document_sections
document_sections:
belongs_to :document
habtm :resources
resources_document_sections:
belongs_to :resource
belongs_to :document_section
resources:
text
So it's easy to say document_section.resources. But document.resources is giving me trouble
The only way I've found to do it so far is to collect the document section ids, and then run a second query:
d = Document.last
s_ids = d.document_section_ids
Resource.joins(:document_sections)
.where(document_sections: { id: s_ids })
.uniq
So this starts bad, and gets worse as the queries get more complicated. It's becoming quite a headache every time I have to touch this relationship.
I'm wondering if there is a different pattern that I could follow in laying out these tables, such that querying against them is not such a headache? Or is there a better querying strategy that I'm missing?
Your document has no relationships. You need to add something to both models in a relationship, you can't just add it to document_sections and expect documents to have any kind of relationship to anything.
You need to add a has_many ... through: to your Document:
class Document < ActiveRecord::Base
has_many :document_sections
has_many :relationships, through: :document_sections
end
I made a relationship with the three models using has_many :through:
class Curriculum class < ActiveRecord::Base
has_many :interests
has_many :vacancies,: through => :interests
end
class Vacancy class < ActiveRecord::Base
has_many :interests
has_many :resumes,: through => :interests
end
class Interest < ActiveRecord:: Base
belongs_to :vacancy
belongs_to :curriculum
end
And to create curriculum and vacancy, I create them by administrative, i need to know how can i create the interest to the id of the vacancy, and how it will be logged on the system I have to get the id of it and make the relationship in creating a new bank interest. I wonder how I can program it to do so, and I wonder how the controller will get the create action, and what better way to do this.
First, try to read the whole "Guide to Rails on Associations", especially the part about has_many :through. Then check your schema if your db is migrated and contains for the table interests the necessary foreign keys to curriculums and vacancies called curriculum_id and vacancy_id.
If that is all in place, the following code will create the relationship between two objects:
#curr = Curriculum.find(1)
#vac = Vacancy.find(1)
#curr.interests << #vac
#curr.save
The last two lines creates an interest between #curr and #vac and store that on the database. So you should not use IDs and handle them directly, but work with objects instead.
The second part now is to provide a UI to allow the definition (and removal) of interests between curricula and vacancies. The base flow here is:
You have one curriculum in focus.
You have a link to add / remove curricula.
The view that opens shows a list of possible vacancies, where every vacancy has a checkbox.
By selecting (or deselecting) the check boxes, the IDs of the vacancies will be held in the params of the request sent to the controller.
See the (older) podcast Railscast #52 how to do that in a similar context. Or see the example for has_many :through with checkboxes.
An alternative way would be to use JQuery autocomplete, and add so interests one-by-one. See the nice podcast Railscast #258 which uses JQuery Tokeninput for that.
I think this is what your looking for:
HABTM Checkboxes
That's the best way to use an Has and Belongs to many association.
I am new to Rails and finished Michael Hartl's "Ruby on Rails 3 Tutorial". Although the book teaches me a lot, I find this puzzle I don't understand.
To preview the puzzle, that is, I don't understand, inside User model,
has_many :following, :through=>:relationship, :source=>:followed
how this piece of code link "user.following" to an array of User instances.
And below is the whole puzzle.
First of all, I have the Relationship model, which records followed_id and follower_id infos. Inside Relationship model, the association is simple as
class Relationship < ActiveRecord::Base
attr_accessible :followed_id
belongs_to :follower, :class_name => "User"
belongs_to :followed, :class_name => "User"
end
Then, inside the User model, a user will assume the role of follower, and collect all its following rows in relationships table through relationships association.
class User < ActiveRecord::Base
.
.
.
has_many :relationships, :foreign_key => "follower_id", :dependent => :destroy
.
Until now, I got it.
But confusion came at the next line, where through user.following it can assemble all that user's following(User instances). Like so,
has_many :following, :through=>:relationships, :source=>:followed
I understand that :source=>:followed will overwrite the default, and let find all followed_ids associated with that user.
But, how can Rails recognize followed_id to link to User object? The label name doesn't match users, nor is there :class_name specified. I just don't get how Rails do this underlying work, or I missed out some hints.
Thank you! :)
But, how can Rails recognize followed_id to link to User object? The
label name doesn't match users, nor is there :class_name specified. I
just don't get how Rails do this underlying work, or I missed out some
hints.
Rails recognize that is an user object because it is set in Relationship's belongs_to. What Rails does here is to follow the relationship class through the foreign key "follower_id" and returning every User that has a relationship with the current user as followed. Of course Rails do that in a single SQL statement like this:
SELECT `users`.* FROM `users` INNER JOIN `relationships` ON `relationships`.followed_id = `users`.id WHERE ((`relationships`.follower_id = <the current user id> ))
has_many :following, :through=>:relationships, :source=>:followed
This explains to Rails that following is the inverse relationship of following and that users has many following and followed through his relationships.
The way Rails knows that followed_id is linked to User is that it is defined in your Relationship model.
Hope you've understood ! Good luck :)