I have a User model and a Book model joined with a Like model.
class Book < ActiveRecord::Base
belongs_to :user
# the like associations
has_many :likes
has_many :liking_users, :through => :likes, :source => :user
class User < ActiveRecord::Base
has_many :books
# the like associations
has_many :likes
has_many :liked_books, :through => :likes, :source => :book
class Like < ActiveRecord::Base
belongs_to :user
belongs_to :book
I want to add an attribute to the Like model so while right now a User can Like a book to add it to their profile, I want the User to be able to write a recommendation.
I generated the attribute Recommendation:text to the Like (joining) model, but am unsure how to add this to views so a User can write a recommendation that will be tied to the Like (and thus that book and user).
I'm looking at this post - Rails has_many :through Find by Extra Attributes in Join Model - which describes something similar but does not explain how to implement this in the views.
Let me know if you can point me in the right direction. Thanks!
I think you should create a separate model for this Recommendation, dependent of the Like model:
class Book < ActiveRecord::Base
belongs_to :user
has_many :comments
has_many :commenters, :through => :comments, :source => :user
class User < ActiveRecord::Base
has_many :books
has_many :comments
has_many :commented_books, :through => :comments, :source => :book
class Comment < ActiveRecord::Base
belongs_to :user
belongs_to :book
Then, the logic to create a comment for a book:
# Books Controller
def show
#book = Book.find(params[:id])
#comment = #book.comments.build
end
# show view of the book
form_for #comment do |form|
form.hidden_field :user_id, value: current_user.id
form.hidden_field :book_id
form.text_area :content # the name of the attribute for the content of the comment
form.submit "Post comment!"
end
To list all the comments of a specific User:
# users controller (profile page)
def show
#user = User.find(params[:id])
end
# show view of Users
#user.comments.includes(:book).each do |comment|
"Comment on the book '#{comment.book.name}' :"
comment.content
end
I suppose that the like functionnality works with ajax ? (remote: :true or in pure javascript)
You can append a form with the response of your request with the id of the new like, and again handle the recommendation by an ajax request
Related
There has to be a better way to do this. My Favorite model belongs to User while Applicant belongs to both Gig and User. I am trying to efficiently determine whether a user has applied for Gig that was favorited (<% if #application.present? %>).
I tried chaining the collection by using something like #favorites.each.gig to no avail. While the below index action for Favorites seems to work, it's really verbose and inefficient. What is a more succinct way of doing this?
def index
#favorites = Favorite.where(:candidate_id => current_candidate)
#applications = Applicant.where(:candidate_id => current_candidate)
#favorites.each do |favorite|
#applications.each do |application|
if favorite.gig.id == application.id
#application = application
end
end
end
end
class User
has_many :applicants
has_many :gigs, :through => :applicants
has_many :favorites
end
class Favorite < ActiveRecord::Base
belongs_to :candidate
belongs_to :gig
end
class Applicant < ActiveRecord::Base
belongs_to :gig
belongs_to :candidate
end
class Candidate < ActiveRecord::Base
has_many :applicants
has_many :gigs, :through => :applicants
has_many :favorites
end
class Gig < ActiveRecord::Base
belongs_to :employer
has_many :applicants
has_many :favorites
has_many :users, :through => :applicants
end
For lack of a better answer, here's my idea:
--
User
Your user model should be structured as such (I just highlighted foreign keys, which I imagine you'd have anyway):
#app/models/user.rb
Class User < ActiveRecord::Base
has_many :applicants
has_many :gigs, :through => :applicants, foreign_key: "candidate_id"
has_many :favorites, foreign_key: "candidate_id"
end
This means you'll be able to call:
current_candidate.favorites
current_candidate.applicants
This will remove the need for your #applications and #favorites queries
--
Favorite
You basically want to return a boolean of whether applicant is part of the favorite model or not. In essence, for each favorite the candidate has made, you'll be able to check if it's got an application
I would do this by setting an instance method on your favorites method using an ActiveRecord Association Extension, like so:
#app/models/user.rb
Class User < ActiveRecord::Base
has_many :favorites do
def applied?
self.applicant.exists? proxy_association.owner.gig.id
end
end
end
This will allow you to call:
<%= for favorite in current_candidate.favorites do %>
<%= if favorite.applied? %>
<% end %>
This is untested & highly speculative. I hope it gives you some ideas, though!
I am trying to design model associations in rails
that consist of the following 3 types:
Commentator, Blogpost and Comment
--> it's "Commentator" and not "User" what means
that they are not the users who create blogposts...
instead they create comments only.
While the basic relationship between Commentator
and Comment is obvious:
class Commentator < ActiveRecord::Base
has_many :comments
class Comment < ActiveRecord::Base
belongs_to: comments
I am not sure how to relate "Blogpost" to this...
--> I would like go be able to ask for all the Blogposts
a Commentator has left as well as all the Commentators
of a specific Blogpost.
Since this is a many-to-many relationship I
would use:
class Commentator < ActiveRecord::Base
has_many :comments
has_many :blogposts, :through => :comments
class Blogpost < ActiveRecord::Base
"has_many :commentators, :through => :comments
When a commentator creates a blogpost, do I have to
write the commenentator_id and blogpost_id in comments
by myself into the corresponding fields of the comment table?
I think it would be better to have Blogposts as the
going through element since the relationship could be
automatically be build when a commentator creates a comment.
(apart from the fact that commentators cannot create comments
to Blogposts that do not exist...)
But then, Commentator to Comment would NOT be a many-to-many
relationship and I cannot use "has_many ... through" anymore.
What is a good way to relate this 3 types of models?
Solution to the stated problem
class Commentator < ActiveRecord::Base
has_many :comments
has_many :blogposts, :through => :comments
end
class Comment < ActiveRecord::Base
belongs_to :commentator
belongs_to :blogpost
end
class Blogpost < ActiveRecord::Base
has_many :comments
has_many :commentators, :through => :comments
belongs_to :user
class User
has_many :blogposts
end
To add a comment to an existing blog post (assuming we have a blog and commentator variables)
blog.comments.create(:commentator => commentator, :comment => "foo bar")
OR
commentator.comments.create(:blog => blog, :comment => "foo bar")
Note
Instead of using two models for users(i.e. User and Commenter), I would use one model and assign privileges
to distinguish between a commentor and a blog post writer.
class User
has_many :blogs
has_many :comments
has_many :commented_blogs, :through => :comments, :source => :blog
end
class Blog
has_many :comments
belongs_to :user
has_many :commenters, :through => :comments, :source => :user
end
class Comment
belongs_to :user
belongs_to :blog
end
Creating a blog entry:
if current_user.has_role?(:blog_writer)
current_user.blogs.create(params[:blog])
end
Adding a comment:
current_user.comments.create(:blog => blog, :content => "foor bar")
OR
blog.comments.create(:user => current_user, :content => "foor bar")
I have a model "Trip" and I want to be able to have only certain users in "Group" create "comments".
When I first created the trip model, I had it set so that only one user can edit it. Now, I want to be able to invite other users to edit it as well. The part that is tripping me up (pun intended) right now is that I have both trip belong_to :user (the user who created it) and trip has_many :users, :through => :group.
Questions:
Is this allowed per Rails convention?
Based on my model, group will have both user_id and trip_id. Is this the best way to approach this problem? That is, should there be a new record in the database for every user I invite to the group?
Thanks.
user.rb
class User < ActiveRecord::Base
has_many :trips, :through => :groups
has_many :trips, :dependent => :destroy
has_many :groups
has_many :comments, :dependent => :destroy
end
group.rb
class Group < ActiveRecord::Base
belongs_to :trip
belongs_to :user
end
trip.rb
class Trip < ActiveRecord::Base
belongs_to :user
belongs_to :traveldeal
has_many :groups
has_many :users, :through => :groups
end
comment.rb
class Comment < ActiveRecord::Base
belongs_to :user
belongs_to :commentable, :polymorphic => true
end
view (show.html.erb)
<% unless #user.trips.empty? %>
<% #user.trips.each do |trip| %>
<!-- Content here -->
<% end %>
<% end %>
Normal Rails convention would have been to call the Group model something like TripPerson to specify the two tables that it was joining, but these join tables are a natural byproduct of relational databases. You'll have to continue creating a new Group object for every person invited to the trip.
First I'm using Rails 3.1 from the 3-1-stable branch updated an hour ago.
I'm developing an application where I have 3 essential models User, Company and Job, Here's the relevant part of the models:
class User < ActiveRecord::Base
has_many :companies_users, class_name: "CompaniesUsers"
has_many :companies, :through => :companies_users, :source => :company
end
class Company < ActiveRecord::Base
has_many :companies_users, class_name: "CompaniesUsers"
has_many :employees, :through => :companies_users, :source => :user
has_many :jobs, :dependent => :destroy
end
class Job < ActiveRecord::Base
belongs_to :company, :counter_cache => true
end
class CompaniesUsers < ActiveRecord::Base
belongs_to :company
belongs_to :user
end
The code works just fine, but I have been wondering if it's possible to:
I want to link a job with an employer, so think of this scenario: A user John who's an employee at Example, he posted the job Rails Developer, so I want to access #job.employer and it should get me back the user John, in other words:
#user = User.find_by_name('john')
#job = Job.find(1)
#job.employer == #user #=> true
So I thought of two possible solutions
First solution
class Job
has_one :employer, :through => :employers
end
class User
has_many :jobs, :through => :employers
end
class Employer
belongs_to :job
belongs_to :user
end
Second solution
class Job
has_one :employer, :class_name => "User"
end
class User
belongs_to :job
end
Which route should I go? Is my code right ?
I have another question, how to get rid of the class_name => "CompaniesUsers" option passed to has_many, should the class be Singular or Plural ? Should I rename it to something like Employees ?
P.S: I posted the same question to Ruby on Rails: Talk
Unless I'm missing something, I'd suggest simply doing
class Job
belongs_to :employer, :class_name => "User"
end
class User
has_many :jobs
end
This would give you methods like
user = User.first
user.jobs.create(params)
user.jobs # array
job = user.jobs.first
job.employer == user # true
You'll need an employer_id integer field in your Jobs table for this to work.
Typically you want to name your pass through model:
company_user
Then you don't need this:
class_name: "CompaniesUsers"
Just make sure the name of your database table is:
company_users
What you have works for you, so that's great. I just find when I don't follow convention I
run in to trouble down the road.
In my form for member_profile, I would like to have role checkboxes that are visible for admins. I would like to used some nested form_for, but can't make it work, so I've resorted to manually creating the check_box_tags (see below), and then manually adding them to member_profile.member.
Note that the Member model is Devise, and I don't want to mix those fields in with my MemberProfile data, in case I change auth systems in the future.
class Member < ActiveRecord::Base
has_one :member_profile
has_many :member_roles
has_many :roles, :through => :member_roles
end
class MemberProfile < ActiveRecord::Base
belongs_to :member
has_many :member_roles, :through => :member
#has_many :roles, :through => :member_roles #can't make this work work
end
class Role < ActiveRecord::Base
has_many :member_roles
validates_presence_of :name
end
class MemberRole < ActiveRecord::Base
belongs_to :member
belongs_to :role
end
Form (haml)
= form_section do
- Role.all.each do |x|
=check_box_tag 'member[role_ids][]',
x.id,
begin #resource.member.role_ids.include?(x.id) rescue nil end
=x.name
member_profiles_controller.rb
def update
if #resource.update_attributes params[:member_profile]
#resource.member.role_ids = params[:member][:role_ids]
redirect_to(#resource, :notice => 'Member profile was successfully updated.')
else
render :action => "edit"
end
end
I've decided it only makes sense to do a nested has_many :through on Update, since the join model is what is being 'gone through' to get to the has_many :through model. Before the hmt is created, there is obviously no record in the join model.