I want to create a model similar to reddit where users can upvote or downvote a link or article. I am having trouble wrapping my head around this: how do I made my models so that a user can like vote up or down a link only once and be able to change their mine (switch to a downvote) but never be able to vote multiple times no matter how much time has passed/logging out does not matter
has_many :through
You'd create something like this:
#app/models/post.rb
Class Post < ActiveRecord::Base
has_many :votes do
def user(user)
find_by user_id: user.id
end
end
has_many :voters, through: votes, class_name: "User", foreign_key: "user_id"
end
#app/models/vote.rb
Class Vote < ActiveRecord::Base
belongs_to :post
belongs_to :user
end
#app/models/user.rb
Class User < ActiveRecord::Base
has_many :votes
has_many :posts, through: :votes
end
Standard has_many :through relationship - giving you the ability to use a join model to associate two or more other models together:
--
User
This would allow you to call the following:
#app/views/posts/show.html.erb
<% if #post.votes.user(current_user).present? %>
<% link_path = #votes.votes.user(current_user).value == "up" ? vote_down_path : vote_up_path %>
<%= link_to "Vote", link_path %>
<% else %>
# empty vote link
<% end %>
The tricky bit is to associate a single vote with a single user - hence why I included an ActiveRecord Association Extension for your votes association
You can use relationships to capture this...
An Article has many votes
A Vote belongs to a User
A Vote has one article
An article has many voters (users) through Votes but they must be unique. (validation rule)
Related
I have two models, one called User and another called Recruiter. What I would like to do is to be able to create a scope that searches users and returns the results so recruiters can see them. But I'm not sure how to go about setting up the association. I made a through association between users and recruiters and created a new join table called recruiter_users but I'm not sure if this is the correct approach.
1) What the best way to make the association between the 2 models
2) how exactly would I display the user results in the recruiters view?
class RecruiterUser < ApplicationRecord
# this is a join model between recruiters and users
belongs_to :recruiter
belongs_to :user
class User < ApplicationRecord
# creates association with recruiters model through the join table recruiter_users
has_many :recruiter_users
has_many :recruiters, through: :recruiter_users
class Recruiter < ApplicationRecord
# creates association with users model through the join table recruiter_users
has_many :recruiter_users
has_many :users, through: :recruiter_users
Again, without having more details about your application, if all you need to do is display the Users associated with a particular Recruiter in a view, it could be as simple as this:
<% #recruiter.users.each do |user| %>
<%= user.whatever_attribute %>
<% end %>
It sounds like you want your average run of the mill many to many association:
class User
has_many :recruitments
has_many :recruiters,
through: :recruitments
end
class Recruiter
has_many :recruitments
has_many :recruited_users,
through: :recruitments,
source: :user
end
class Recruitment
belongs_to :user
belongs_to :recruiter
end
You don't have to name your join models a + b. If there is a more descriptive name of what the relation is use it.
this would let you iterate through the users recruited by a recruiter by:
#recruiter = Recruiter.includes(:recruited_users).find(params[:id])
<% #recruiter.recruited_users.each do |user| %>
# ...
<% end %>
This question is related to that shows a has_many :through association. I have a similar association as shown in the answer. I will cite for reference.
$ rails g model FavoriteRecipe recipe_id:integer user_id:integer
# Join model connecting user and favorites
class FavoriteRecipe < ActiveRecord::Base
belongs_to :recipe
belongs_to :user
end
---
class User < ActiveRecord::Base
has_many :recipes
# Favorite recipes of user
has_many :favorite_recipes # just the 'relationships'
has_many :favorites, through: :favorite_recipes, source: :recipe # the actual recipes a user favorites
end
class Recipe < ActiveRecord::Base
belongs_to :user
# Favorited by users
has_many :favorite_recipes # just the 'relationships'
has_many :favorited_by, through: :favorite_recipes, source: :user # the actual users favoriting a recipe
end
I am curious to know if this solution would work with a many-to-many relationship with joining model e.g. FavoriteRecipe that has a boolean attribute favorite_set. When iterating through the Recipes I would like to find out if the current user has 'favorited' this/these Recipes. Something like current_user.recipe.favorite_set? but of course going through the FavoriteRecipe joining model. If true display 'FAVORITE SET' otherwise give an option to favorite this recipe. My implementation is a feed that displays all "Recipes" and shows "FAVORITE SET" or gives the option to "favorite" one or multiple Recipes.
Thanks in advance.
To solve this problem I created a helper module in app/helpers
module ReminderHelper
#Check if user has set reminder for a specific event
def event_reminder_set? user, event
EventReminder.where(user: user, event: event).any?
end
end
I included the module in app/controllers/application_controller.rb
include ReminderHelper
In my view I use it as follows
<% unless event_reminder_set?(current_or_guest_user, event) %>
REMIND ME
<% else %>
REMINDER SET
<% end %>
This should work with other models associations with a many-to-many relationship with joining model e.g. FavoriteRecipe
I've been grappling with a problem which is proving to be quite hard. I've got a User model, a Photo model and a comment model. Now the way my website works is that a User can have many comments on a particular photo. On the reverse side, a comment can only belong to a particular user on a particular photo.
I've read through the Active Record Associations docs and from what I've gathered is that we can't use a has_many :through associations since it appears to work for polymorphic associations between models. I'm wondering if one can use has_many :through association on one side and belongs_to association on the reverse side.
Any tips, pointers and advice? I'm just beginning in Ruby on Rails
Thanks.
Wouldn't this work?
class User
has_many :photos
has_many :comments
end
class Photo
belongs_to :user
has_many :comments
end
class Comment
belongs_to :user
belongs_to :photo
end
User has many photos and comments (the ones he uploaded/written), and each comment belongs to user (writer) and a photo which was commented on.
#app/models/user.rb
class User < ActiveRecord::Base
has_many :photos
has_many :comments, through: :photos #-> probably won't work but I'd try it first
end
#app/models/photo.rb
class Photo < ActiveRecord::Base
belongs_to :user
has_many :comments do
def current_user #-> photo.comments.current_user
where user_id: self.user_id
end
end
end
#app/models/comment.rb
class Comment < ActiveRecord::Base
belongs_to :photo
belongs_to :user
end
--
You could access the photo's comments as follows:
<% #user.photos.each do |photo| %>
<%= photo.comments.each do |comment| %>
<%= comment %>
<% end %>
<% end %>
If you wanted to show only the user's comments, you'd be able to use the current_user ActiveRecord Association Extension:
<% #user.photos.each do |photo| %>
<%= photo.comments.current_user.each do |comment| %>
<%= comment %> #-> comments where user_id will be the same as photo's user_id
<% end %>
<% end %>
You can do it like this:
User
has_many :comments
Photo
has_many :comments
belongs_to :user
Comment
belongs_to :user
belongs_to :photo
I'm still a new to Rails, so I'm finding this so frustrating. I'm trying to make a small application as part of a personal project that lets users create and join each others groups.
I've looked on StackOverflow and found two similar questions with answers that don't quite address my scenario (I've even tried using their code and still can't figure out how to go about this).
Those SO links are here:
How do I create a join action between a group and a user?
creating a join action between user and group correctly
What I'm trying to do is figure out the controller actions and erb code that lets users create and join groups. Here are two posts on SO that I've read repeatedly, I tried using their code at one point, but kept getting a
First argument in form cannot contain nil or be empty
for
<%= form_for(#membership) do |f| %>
That error came from when I was using the membership_controller code in the second SO post I listed. Here are the models I have so far, which I wrote with some help from SO as well.
user.rb (model)
class User < ActiveRecord::Base
has_many :memberships, :dependent => :destroy
has_many :groups, :through => :memberships
membership.rb (model)
class Membership < ActiveRecord::Base
attr_accessible :user_id, :group_id
belongs_to :user
belongs_to :group
group.rb (model)
class Group < ActiveRecord::Base
has_many :memberships, :dependent => :destroy
has_many :users, :through => :memberships
I honestly can't figure out how to connect these three models, I've seen examples, tried to think of it for myself for more than a few hours now.
If you can help at all, I will greatly appreciate it. I'm just pulling my hair out trying to get something that I know is simple to do in Rails to work properly.
I'll just detail what I would do:
#config/routes.rb
resources :groups, only: [:show], shallow: true do
resources :memberships, only: [:new] #-> domain.com/2/memberships/new
end
This will send you to the memberships controller, which you can look like this:
#app/controllers/memberships_controller.rb
Class MembershipsController < ApplicationController
def new
#group = Group.find params[:group_id]
#membership = Membership.new({group: group})
end
def create
#group = Group.find params[:group_id]
#membership = Membership.new(membership_params)
end
private
def membership_params
params.require(:membership).merge(group_id: params[:group_id], user_id: current_user.id)
end
end
This will allow you to create the following form:
#app/views/memberships/new.html.erb
<% if user_signed_in? %>
<% if current_user.groups.include?(#group) %>
You're already a member of this group!
<% else %>
<%= form_for [#group, #membership] do |f| %>
<%= f.submit "Join" %>
<% end %>
<% end %>
<% end %>
--
You'll have your models set up with as has_many :through association (allowing you to directly affect the join model):
#app/models/user.rb
Class User < ActiveRecord::Base
has_many :memberships
has_many :groups, through: :memberships
end
#app/models/membership.rb
Class Membership < ActiveRecord::Base
belongs_to :group
belongs_to :user
end
#app/models/group.rb
Class Group < ActiveRecord::Base
has_many :memberships
has_many :users, through: :memberships
end
This is not the most efficient, or probably "correct" way of doing this, but should give you something to work with
The error message implies that #membership is nil or []. You should check the following lines to see why that happened. Possibly #group does not exist
#group = Group.find_by_name(:group)
#membership = current_user.memberships.build(:group_id => #group.group_id)
What is the most efficient way for retrieving all unique venues with a specified set of features?
In the controller, I have:
#venues = Venue.all
#venues = #venues.features.where('feature.id == ' 1).distinct
Here's how my models are defined:
class Neighborhood < ActiveRecord::Base
has_many :venues
end
class Venue < ActiveRecord::Base
belongs_to :neighborhood
has_many :features
end
class FeatureType < ActiveRecord::Base
has_many :features
end
class Feature < ActiveRecord::Base
belongs_to :venue
belongs_to :feature_type
end
Just think about this using English. If a Venue has many Features and you ask "What is the Id of the Feature?" the response is going to be: "There are many Features, which one?"
The :has_many association gives you the following method: venure.features. That gives you all the of the "many" associated features. To get the Id of just one, you could do something like: venue.features.first.id.
Venue has_many features, so you must loop over the collection, vs a belongs_to where there is a single relationship between the models
<% venue.features.each do |feature| %>
<%= debug feature %>
<% end %>