Rails - Polymorphic association join table - ruby-on-rails

I am currently trying to set up a model structure that seems quite simple, but I haven't quite got it down.
I have a model payment that can belong to either a customer or a supplier (which can both have many payments).
My question is simply whether I need to manually create an interface table to allow this, or if declaring the polymorphic associations will do this for me?
e.g. I have:
class Payment < ActiveRecord::Base
belongs_to :payment_originator, :polymorphic => true
end
class Customer < ActiveRecord::Base
has_many :payments, :as => :payment_originator
end
class Supplier < ActiveRecord::Base
has_many :payments, :as => :payment_originator
end
Is this enough, or do I also need to use a generator to manually create the payment_originator model?
Thanks!

As far as the models go, this is good enough. You just need to migrate a :payment_originator_type and :payment_originator_id to the payments table. The associations you defined above will automatically fill these in for you.

Related

Rails Create "Collection" of posts

In my rails app, user's can create Designs.
Design.rb
belongs_to :user
User.rb
has_many :designs
I'm trying to create a new model Look so user's can create Looks. The way I envision this to work is when a user goes to /looks/new, they have a list of all the designs they have favorited (which I have that set up that variable already) in a table format with the right column being checkboxes where the user can go through and check a few of those Designs and click Create. All the Designs that have been checked would be part of that Look.
As I haven't done this sort of thing before, I need some help accomplishing this in all aspects MVC.
Look.rb
has_many :designs
Design.rb
belongs_to :looks # ??? Would the model be something different since technically when you create a design it doesn't belong to a look.
Looks Controller
def new
#designs = #user.favorites #This get's me all the designs that the particular user has favorited
#look = Look.new # ??? Again, as I haven't set this sort of relation up before, I'm unsure.
end
Please let me know any other code I can provide to help out. I may even be making this sound more complicated than it is.
This configuration should work for you Justin:
class User < ActiveRecord::Base
has_many :designs
has_many :looks, through: :designs
end
class Design < ActiveRecord::Base
belongs_to :user
has_many :designs_looks
has_many :looks, through: :designs_looks
end
class Look < ActiveRecord::Base
has_many :designs_looks
has_many :designs, through: :designs_looks
end
class DesignsLook < ActiveRecord::Base
belongs_to :design
belongs_to :look
validates :design_id, presence: true
validates :look_id, presence: true
end
I don't know what you want to do in the future but you might want to consider putting the user_id on the DesignsLook model, so you would not need a complex join query to retrieve all the Looks of a User. And also you implement shared Designs with all users
Your user has many designs. New looks can have many designs. And design can belong to MANY looks, users. Smells like has many ..., :through
http://guides.rubyonrails.org/association_basics.html#the-has-many-through-association
class User
has_many :designs, through: :design_possesion
end
class Look
has_many :designs, through: :look_designs
end
class Design
has_many :look_designs, :design_possesion
end
Of course you'll have to create corresponding tables.

Enforcing uniqueness of a model in has_many though

I have a User model, which has_many Dish through Recommendation. I would like to enforce uniqueness of Dish, as well as uniqueness of Recommendation.
How should I go about this in ActiveRecord?
In my dish.rb:
validate_uniqueness_of :dish_name
What I would like to have is: when an user recommends a dish, create a new dish if it does not exist, then create recommendation. If the dish already exists, then just create recommendation and point to existing dish.
Do I need to handle these situations manually (i.e., checking existence of dish in controller), or ActiveRecord has a way to handle it internally?
Update:
validate_uniqueness_of :dish_name only checks and return error message if the dish was created there. It probably won't create new recommendation that points to existing dish.
You could always .find_or_create_by_<attribute> to find the dish to begin with
As I see, more than one user can recommend the same dish.
Your models should look like:
Class User < ActiveRecord::Base
has_many :recommendations
has_many :dishes, :through => :recommendations
end
Class Dish < ActiveRecord::Base
has_many :recommendations
has_many :users, :through => :recommendations
end
So your Recommendations table in database should have two columns (beside it's id and timestamps) called user_id and dish_id . To validate that a user doesn't recommend
the same dish twice, do:
Class Recommendations < ActiveRecord::Base
belongs_to :dish
belongs_to :user
validates_uniqueness_of :dish_id, :scope => :user_id
end
And i didn't know about the .find_or_create_by method that Dan recommended, so definetly try to use something like that.
Hope i helped :)

.build method not creating association in the join table

I have three Models setup with the following associations
class User < ActiveRecord::Base
has_many :faculties
has_many :schools, :through => :faculties
end
class School < ActiveRecord::Base
has_many :faculties
has_many :users, :through => :faculties
end
class Faculty < ActiveRecord::Base
belongs_to :user
belongs_to :school
end
and in my controller i go to create a school and assign the user
class SchoolsController < ApplicationController
def create
#school = current_user.schools.build(params[:school])
...
end
end
When I login and submit the form the flash displays success, but the association doesn't build on the join table.
I tried it inside the apps console and it builds the association just fine.
I've been stuck on this for a couple days now and I just cannot figure out what I am missing. Thank in advance for any and all advice
The build method does not save the object. You need to explicitly call #school.save.
Two things: If the schools association is :through a has_many association, you will have to select which parent the School exists through.
So, for instance, if you were to nest School resources under users as in /users/:id/faculties/:id you could create a school via current_user.faculties.find(params[:faculty_id]).schools.build(params[:school]).save
Based on the example code, it looks like the fundamental problem is that the has_many xxx, :through syntax is being used without specifying the id of the faculties record. Remember two things: 1) ActiveRecord doesn't natively support composite primary keys, and 2) you must call #save on associated records created using #build. If you remember these, you should be fine.

rails, model naming question

I'm creating a model called Chats. And I want to assign users to a discussion. They are either a part of the Chats or they aren't...
So I create one model Chats.
What's the standard Rails naming convention for the other table?
ChatUsers?
While has_and_belongs_to_many is an ok option here, I recommend going with has_many :through instead.
In essence you will have an explicit join model, which you can call something like ChatSession.
class Chat < ActiveRecord::Base
has_many :chat_sessions
has_many :users, :through => :chat_sessions
end
class User < ActiveRecord::Base
has_many :chat_sessions
has_many :chats, :through => :chat_sessions
end
class ChatSession < ActiveRecord::Base
belongs_to :user
belongs_to :chat
end
Now you will need a table called chat_sessions with columns :user_id, and :chat_id in it. This is your join table.
Advantage
You get a model which is fully under your control, and isn't just a dumb join table managed by rails. So for example, if you want to track number of messages particular user left in particular chat, it could be a column in chat_sessions table. Presence of :through renders habtm unneeded in most cases. There is no complexity overhead either.
If it is a join table, it would be both table names joined by '_' and in alphabetical order of table names:
chats_users
This is called a has_and_belongs_to_many association in rails. You basically have two models that call has_and_belongs_to_many and create a linking table that uses the two models in the name (alphabetical and plural).
models:
class Chat < ActiveRecord::Base
has_and_belongs_to_many :users
end
class user < ActiveRecord::Base
has_and_belongs_to_many :chats
end
Then your tables would be
chats
users
chats_users

RoR: Should I use belongs_to, :polymorphic in this scenario?

I am working on a project where many ActiveRecord models can have a conversation associated with it. Users can discuss just about every aspect of the site. I have two ideas as to how this should be implemented.
1) Use a belongs_to in the asset, not the conversation - conversation will be totally unaware of its asset
class Product< ActiveRecord::Base
belongs_to :conversation
end
class PurchaseOrder < ActiveRecord::Base
belongs_to :conversation
end
2) Use a belongs_to, :polymorphic => true in the conversation
class Conversation < ActiveRecord::Base
belongs_to :asset, :polymorphic => true
end
class Product < ActiveRecord::Base
has_one :conversation, :as => :asset
end
class PurchaseOrder < ActiveRecord::Base
has_one :conversation, :as => :asset
end
Which is the correct way to model this relationship? If I were to state the relationship, I would say that "a product / purchase order may have one conversation".
I think it depends on what, if anything, one model in the relationship needs to know about the other. Seems to me, from your description that the second approach it more fitting in this case. Why?
The Product and PurchaseOrder models are self-contrained entities in the sense that they can exist apart from conversations about them. So you probably don't want foreign keys polluting these models for tacking on conversations. The relationship ought to be unobtrusive in that sense.
A Conversation has a logical dependency on the entity it's associated with so it has the asset_id (and asset_type) foreign keys and that's probably reasonable
This is a very common question and one which always has me stopping to think a bit too. It's not always obvious. There's a good article considering the issue here

Resources