I’m creating a voting feature for our website in the style of YouTube “Likes” and “Dislikes” and Digg using Ruby on Rails 3. I have having trouble coming up with the right scheme.
I have three models, Users, Topics, and Votes. Each User will make one vote “Like” or “Dislike” for one Topic. Like these sites, Users can Vote on a Topic, but they can also create new Topics. I’d like to be able to not only view all of a User’s Votes, but also both the Topics they have created and the Topics they have voted on. I am trying to build this on our own and decide how best to setup the database to handle this process.
My first idea was to use :has_many and belongs_to exclusively like so…
class User < ActiveRecord::Base
has_many :votes
has_many :topics
class Topic < ActiveRecord::Base
has_many :votes
belongs_to :users
class Vote < ActiveRecord::Base
belongs_to :topics
belongs_to :users
boolean choice #tracks whether the User chooses Like or Dislike
But it became evident that this might not be the best way to do this. I began to think the best method would be to use a :has_many :through association like...
class User < ActiveRecord::Base
has_many :votes, :through => :topics
But I’m not sure. Any ideas on how best to set something like this up?
I think your initial setup is good but it can be further improved upon to better support what you want to accomplish. Perhaps like this:
class User < ActiveRecord::Base
has_many :votes
has_many :topics
#Lists all topics the user has voted on
has_many :voted_topics, :through => :votes, :source => :topic
#Lists all votes for the users topics
has_many :topic_votes, :through => :topics, :source => :votes
end
class Topic < ActiveRecord::Base
has_many :votes
belongs_to :user
end
class Vote < ActiveRecord::Base
belongs_to :topic
belongs_to :user
end
Related
I would like to achieve something as follows where PersonSubject has many topics, but the choices of these topics are limited to the the selection of topics through another model (ie: through the associated subject):
class Topic < ApplicationRecord
belongs_to :subject
end
class Subject < ApplicationRecord
has_many :topics
end
class PersonSubject < ApplicationRecord
belongs_to :person
belongs_to :subject
has_many :topics # where the choices are limited to the subject.skills
end
I would then like if any person_subject.subject.topics are deleted (or association removed), it would automatically update the person_subject.topics to no longer "point" to the Topic(s) that were deleted.
Is this possible?
You can use a lambda to put arbitrary filters on an association. See What is the equivalent of the has_many 'conditions' option in Rails 4?
has_many :topics, -> { where(skill: subject.skills) }
I don't know that this is exact code will work without seeing your schema (what is the data type of subject.skills, and how do you join this with topic?). But hopefully this gets you on the right track
edit
in response to your comment, I think
has_many :topics, through: :skills
would work
Total newbie to Rails and programming in general, so please forgive me if the answer to my question seems glaringly obvious. I've been doing a lot of reading including the ROR guides but can't seem to find a scenario specific to the following situation:
I have three models scaffolded under one User model, and am trying to link them like so:
class User < ActiveRecord::Base
has_many :malls, :dependent => :destroy
end
class Mall < ActiveRecord::Base
belongs_to :user
has_many :stores, :dependent => :destroy
has_many :cakes, :as => :cake_poly, :dependent => :destroy
end
class Store < ActiveRecord::Base
belongs_to :mall
has_many :cakes, :as => :cake_poly, :dependent => :destroy
end
class Cakes < ActiveRecord::Base
belongs_to :cake_poly, :polymorphic => true
end
Will this work? If it does, is there a better way to implement this? If not, how else can I implement the associations?
The idea is that each User may have many malls, each Mall may have many stores, and both malls and stores may have many cakes.
A key question for your design is the intent - what do you want to achieve?
You should not use the polymorphic relationship if you simply like to aggregate cakes available in the mall's shops for the mall model. You can use the following relationship to achieve such a design:
class Mall < ActiveRecord::Base
has_many :stores
has_many :cakes, :through => :stores
end
The polymorphic approach is a perfect choice if the two sets (cakes in the mall, cakes in the mall's stores) don't correlate.
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
So I've got three models in my app, a User, Review and a Movie model. A user can review many movies (one per movie), a Movie can have many reviews from many users. Their connection is a review.
Am I doing the following setup right?
class Movie < ActiveRecord::Base
has_many :reviews, :through => :users
end
class Review < ActiveRecord::Base
belongs_to :user
belongs_to :project
end
class User < ActiveRecord::Base
has_many :reviews, :through => :movies
end
I'm hoping I can do something like:
User.reviews (which would give me back the user's reviews and the corresponding id of the movie which the review relates to)
Thanks
I believe this is the approach you should be taking
class User < ActiveRecord::Base
has_many :reviews
has_many :movies, :through => :reviews
end
class Review < ActiveRecord::Base
belongs_to :user
belongs_to :movie
end
class Movie < ActiveRecord::Base
has_many :reviews
has_many :users, :through => :reviews
end
You would also want to validate uniqueness in the model Review and/or enforce uniqueness on the join-table to only allow a single user per movie. It's up to you if you want to take this UQ constraint into the schema as well (DHH says 'Yes', I say 'the slathering of dumb persistance' is a 'No no'...)
User.reviews will give you the 'join records', i.e. along the lines of <Review user_id=x, movie_id=y> Certainly, you'd have a lot more columns on the join table pertaining to the review, such as :summary, :content etc. It's easy enough to access the movie from a review, i.e. Movie.find_by_id(User.reviews.last.movie_id).title
I have a situation where I have Products, Suppliers, ShoppingLists and Valuations.
A shopping_list consist of many valuations each with a product, an specific supplier and a price.
My models are as follows:
class Product < ActiveRecord::Base
has_many :valuations
has_many :shopping_lists, :through => :valuations
end
class Supplier < ActiveRecord::Base
has_many :valuations
has_many :shopping_lists, :through => :valuations
end
class ShoppingList < ActiveRecord::Base
has_many :valuations
has_many :products, :through => :valuations
has_many :suppliers, :through => :valuations
end
class Valuation < ActiveRecord::Base
belongs_to :product
belongs_to :supplier
belongs_to :shopping_list
end
My routes.rb is:
map.resources :shopping_lists do |shopping_list|
shopping_list.resources :valuations
end
map.resources :product
map.resources :supplier
I wonder if this could be the best solution, anyway what I want is that the user can create as many lists as he wish, each with several valuations.
The first time a shopping list is created its also filled with one valuation at least. Then, the user can add/remove valuations to/from the shopping_list.
I would like a simple and elegant solution, without Ajax callbacks.
What is the best way to do this, from the controllers/views/routes perspectives?
Or should I completley change my schema ?
Thanks!
Just found two excelent resources from Ryan Bates:
http://asciicasts.com/episodes/196-nested-model-form-part-1
http://asciicasts.com/episodes/197-nested-model-form-part-2
Let's see if that do the job!
// UPDATE: Worked great!