Rails Polymorphic Associations - ruby-on-rails

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.

Related

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

Like/Dislike style Voting Database in Rails

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

Rails: Many-to-many relationships with 3 Models

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!

Rails modeling for a user

When building a rails app that allows a User to login and create data, is it best to setup a belongs_to :user association on every single model? For example, let's say a user can create Favorites, Colors and Tags.
And let's say Favorites has_many :tags and Colors also has_many :tags. Is it still important for Tags to belong_to :user assuming the User is the only person who has authority to edit those tags?
And a similar question along the same lines: When updating data in FavoritesController, I've come to the conclusion that you perform CRUD operations by always doing something like current_user.favorites.find(param[:id].update_attributes(param[:favorite]) so that they can definitely only update models that belong to them. Right?
Update Wasn't too happy with any of the answers, as no one really answered my question but instead went after the for-example-only Tags model suggesting better ways to do that. I'm assuming I was right, and models should belong_to :user. I also discovered some great security tips that address my questions here: http://asciicasts.com/episodes/178-seven-security-tips
As you describe the tags it seems that they are more of an aspect, so you can implement them as a polymorphic association. But you should do it many-to-many, as tags can be reused among users and taggable objects. Let's call the join model Tagging, which will be the one that belongs to user if you want to remember who created the tagging.
class Tag < ActiveRecord::Base
has_many :taggings, :dependent => :destroy
has_many :colors, :through => :taggings, :source => :taggable, :source_type => "Color"
has_many :favorites, :through => :taggings, :source => :taggable, :source_type => "Favorite"
end
class Tagging < ActiveRecord::Base
belongs_to :user
belongs_to :taggable, :polymorphic => true
belongs_to :tag
end
class Color < ActiveRecord::Base
belongs_to :user
has_many :taggings, :as => :taggable
has_many :tags, :through => :taggings
end
class Favorite < ActiveRecord::Base
belongs_to :user
has_many :taggings, :as => :taggable
has_many :tags, :through => :taggings
end
class User < ActiveRecord::Base
has_many :favorites
has_many :colors
has_many :taggings
has_many :tags, :through => :taggings
end
As for the Favorite updating, I agree with you: you will mostly work within the scope of a user (most likely the currently logged in user).
It depends on your model. Both cases are valid but I'd discorage making a circular relationships like that. Having a hierarchy is more flexible. For example: User->Favorites->Tags (unless you want to tag users as well)
User.favorites.find(params[:id]).update_attributes(param[:favorite])
is what you mean I guess (syntax). Whoever calls the URL will perform that action. Dont rely on the fact that that URL is visible to one user only (owner of the favorite). You should have checks in place that the currently logged in user is the only one performing actions on the objects that belong to him.
The proposed mechanism sounds a bit too complex for me. I prefer the current_user way. Assume there is a current_user (following the authlogic way) in your authentication system, then simple add a user references (user_id) in every relevant table. Update the current_user for new or update record via a controller filter.
In the models, put relevant belongs_to :users accordingly, put enough has_many in users model if needed.
:has_many and :belongs_to in AR will explains the relationship between models, but not necessarily you have to use them in your models, the associaton between them will be already present in the tables as a foreign key.
But adding :has_many or :belongs_to to your models will give you extra methods to your model
ex:
class User < ActiveRecord::Base
has_many :favorites
#def favorites
# Favorite.find_all_by_user_id(self.id)
# end
end
If you mention has_many it will give a new method in your model called favorites, that method will be invisible (will be present in the AR).
Similarly for any association, if you are planning to use this kind of methods you should use associations in your models.

Rails Polymorphism 'backwards'

Say I'm writing a blog app with models for posts, pages and photos. I've got a category model, that may be linked to any of these models. So, a category may contain various kinds of items. Every item only has ONE category.
I could implement this using a generic tagging pattern with a join table, but I want to make sure every subject can have only category.
What would be the best way to implement this in Rails?
Okay, I think I've got it:
class Post < ActiveRecord::Base
has_one :categorization, :as => :categorizable
has_one :category, :through => :categorization
end
class Category < ActiveRecord::Base
has_many :categorizations, :dependent => :destroy
end
class Categorization < ActiveRecord::Base
belongs_to :category
belongs_to :categorizable, :polymorphic => true
end
Now various models can have a category, but each instance can have only one category… I guess.

Resources