i'm trying to create a polymorphic relationship between votes can be submitted by users and apply to articles. my code
class Vote < ActiveRecord::Base
attr_accessible :value, :voteable_id, :voteable_type
belongs_to :voteable, :polymorphic => true
end
class User < ActiveRecord::Base
has_many :votes, :as => :voteable
end
class Article < ActiveRecord::Base
has_many :votes, :as => :voteable
end
<Vote id: 1, value: 1, created_at: "2012-07-27 03:13:14", updated_at: "2012-07-27 03:13:14", voteable_id: nil, voteable_type: nil>
From looking at the rails documentation via http://guides.rubyonrails.org/association_basics.html#polymorphic-associations
I feel that my code is set correctly but i'm having a bit of trouble triggering it correctly, ie, how do I actually create a vote object with a properly define relationship to either an article or user?
Is votable_type is string?
Next example should work correctly..
#user.votes.new :value => 1
#user.save
.
I was able to make this work, I was setting the voteable_type attribute incorrectly.
Related
Accordingly to devise documentation, related records to current_user is always available, despite what I've read previously. For instance: current_user.comments current_user.profile_images
What bugs me really is this:
Post.rb
class Post < ApplicationRecord
belongs_to :user
has_many :postsettings, inverse_of: :post
accepts_nested_attributes_for :postsettings
User.rb
class User < ApplicationRecord
has_many :posts
has_many :postsettings
Postsettings.rb
class Postsetting < ApplicationRecord
belongs_to :post, required: false
belongs_to :user, required: false
In Posts controller I have this:
#post_delete = current_user.postsettings.includes(:postsettings).where.not
(postsettings: {user_id: current_user.id, delete_post: false})
Which works, but is not producing the desired outcome because I need to query ALL Posts where current_user.POSTSETTINGS.delete_post is true or false.
So I've been at this for a couple of days now, and I managed to come up with this:
#post_delete = Post.includes(current_user.postsettings).where(
postsettings: {user_id: current_user.id, delete_post: false})
This produces an error message which I have not seen before.
ActiveRecord::ConfigurationError in Posts#index
#<Postindex id: 284, read: nil,
created_at: "2017-04-15 11:38:02",
updated_at: "2017-04-15 11:38:02", post_id: 96, user_id: 1,
delete_post: false>
Which indicates as far as I can see that the query finds all that it needs to find. But it won't actually function. Please help me.... I'm dying over here.
Post.joins(:postsettings)
.where(postsettings: { user: current_user } )
.where(postsettings: { delete_post: [true, false] })
.joins creates a INNER JOIN which means that only rows from posts with matches in postsettings will be returned.
The answer given by max is good, for convenience though, you can set your user model as follows. Add this line to your user model.
has_many :posts_with_settings, through: :postsettings, source: :post
Now you should be able to call current_user.posts_with_settings to give you all the posts with postsettings set for current_user. From there you can filter as you wish, e.g,
current_user.posts_with_settings.where(postsettings: {delete_post: true})
For more info on :through option see here.
A Manager has many contacts via polymorphic association
class Manager
has_many :contacts, as: :contactable
end
class Contact
belongs_to :contactable, polymorphic: true
end
The relation works fine but now a contact can be associated to many managers.
So, added a new model Contactable, a joins table 'contactables' and moved contactable_id and contactable_type fields from contacts table to contactables table.
class Contactable
belongs_to :contact
belongs_to :contactable, polymorphic: true
end
Now confused about the Manager and Contact relation that how it would be defined in models correctly to make it working. Tried the following but it doesn't work:
class Manager
has_many :contacts, through: :contactables, source: :contactable, source_type: "Contact"
end
So I checked this interesting topic and will tell what I know.
When you create objects as usual in has_many :through:
class Contact
has_many :contactables
has_many :managers, :through => :contactables
end
class Manager
has_many :contactables
has_many :contacts, :through => :contactables
end
class Client
has_many :contactables
has_many :contacts, :through => :contactables
end
class Contactable
belongs_to :contact
belongs_to :manager
belongs_to :client
end
You get to use foreign keys fro each referenced object. Polymorphic looks like a great solution. So:
class Contactable
belongs_to :contact
belongs_to :polymorphic_model, polymorphic: true
end
class Contact
has_many :contactables
has_many :managers, :through => :contactables, :source => :polymorphic_model, :source_type => 'Manager'
end
class Manager
has_many :contactables, :as => :polymorphic_model
has_many :contacts, :through => :contactables
end
Setting the :as option indicates that this is a polymorphic
association
:source => :polymorphic_model is used to tell Rails to get the related object from the subclass. :source means the same as :class_name. Without this option Rails would try to get associated Manager from the Contactables table, while it should be reached via virtual Polymorphic_model.
By adding belongs_to :polymorphic_model to Contactable you enable Contact (witch already sits there, because of belongs_to :contact) to be associated with a Manager or Client, because thats what Polymorphic association does - references two or more parent tables. And because Contact have_many Contactables, the same Contact object can be associated with many managers or clients. So after you understand it, it looks really simple - Joined model belongs to Contact and Joined model also holds references to Manager and Client through Polymorphic association. So in order for Contact to have many managers, you create another Contactable object that belongs to the same Contact, but different Manager. Doesn't look super efficient, but personally me, not knowing a better way..
Here is a tested proof:
Manager.create!(name: "Bravo")
=> #<Manager id: 1, created_at: "2017-04-12 12:17:41", updated_at: "2017-04-12 12:17:41", name: "Bravo">
Manager.create!(name: "Johnny")
=> #<Manager id: 2, created_at: "2017-04-12 12:18:24", updated_at: "2017-04-12 12:18:24", name: "Johnny">
Contact.create!(number:"123")
=> #<Contact id: 1, created_at: "2017-04-12 12:18:59", updated_at: "2017-04-12 12:18:59", number: 123>
c = Contactable.new
c.contact = Contact.first
c.unit = Manager.first
c
=> #<Contactable id: nil, unit_type: "Manager", created_at: nil, updated_at: nil, unit_id: 1, contact_id: 1>
Now to set another Manager to the same contact, we create a new Contactable:
cc = Contactable.new
cc.contact = Contact.first
cc.unit = Manager.last
cc
=> #<Contactable id: nil, unit_type: "Manager", created_at: nil, updated_at: nil, unit_id: 4, contact_id: 1>
And to get all associated:
Contact.first.managers
Contactable's database:
contact_id
unit_id
unit_type
And one interesting quote by #Bill Karwin:
The Polymorphic Associations design breaks rules of relational
database design. I don't recommend using it.
But he wrote this long time ago. Probably irrelevant now.
Why can you not have a foreign key in a polymorphic association?
I am making a toy application to learn Rails 4 (without just cloning a tutorial).
Users sign up (I'm using the Devise gem to take care of user authentication), and a BTC pub/prv keypair is generated, and an address is computed and displayed to the user (in a flash message), so they can top off their account. Other Users sign up, and anyone can search for anyone and a dropdown is dynamically populated with every single user, but filters down names as a User types the name of their friend/associate, whoever they want to send Bitcoin to. I am only using testnet for this idea at the moment, no real BTC (don't worry!).
Anyways, here is my idea for modeling this application:
class User < ActiveRecord::Base
has_many :account
end
class Tx < ActiveRecord::Base
has_one :receiver, class => "account"
belongs_to :user, through :account
end
class Account < ActiveRecord::Base
belongs_to :user
has_many :tx
end
The reason why I don't like the above is because in my mind it seems that a Tx (short for transaction since transaction is a reserved word in Rails) actually belongs to two users, but my readings seem to indicate that I can't have something like this:
class User < ActiveRecord::Base
has_many :tx
end
class Tx < ActiveRecord::Base
has_one :receiver, class => "user"
has_one :sender, class => "user
end
Which of these implementations is better? I appreciate any insight into this model.
I'd go with the second method. I went with "transfers" instead of "tx", for readability - but you can name it as you please.
class User < ActiveRecord::Base
has_many :transfers
has_many :received_transfers, :class_name => "Transfer", :foreign_key => "receiver_id"
end
class Transfer < ActiveRecord::Base
belongs_to :user # Sender
belongs_to :receiver, :class => "User"
end
Testing it:
>> Transfer.create(:user_id => 1, :receiver_id => 2, :amount => 4.00)
=> #<Transfer id: 1, user_id: 1, receiver_id: 2, amount: #<BigDecimal:7fb3bd9ba668,'0.4E1',9(36)>, created_at: "2014-09-03 04:35:47", updated_at: "2014-09-03 04:35:47">
>> User.first.transfers
=> #<ActiveRecord::Associations::CollectionProxy [#<Transfer id: 1, user_id: 1, receiver_id: 2, amount: #<BigDecimal:7fb3c10682f0,'0.4E1',9(18)>, created_at: "2014-09-03 04:35:47", updated_at: "2014-09-03 04:35:47">]>
>> User.last.received_transfers
=> #<ActiveRecord::Associations::CollectionProxy [#<Transfer id: 1, user_id: 1, receiver_id: 2, amount: #<BigDecimal:7fb3bdabace8,'0.4E1',9(18)>, created_at: "2014-09-03 04:35:47", updated_at: "2014-09-03 04:35:47">]>
Happy coding!
I've came into a problem while working with AR and polymorphic, here's the description,
class Base < ActiveRecord::Base; end
class Subscription < Base
set_table_name :subscriptions
has_many :posts, :as => :subscriptable
end
class Post < ActiveRecord::Base
belongs_to :subscriptable, :polymorphic => true
end
in the console,
>> s = Subscription.create(:name => 'test')
>> s.posts.create(:name => 'foo', :body => 'bar')
and it created a Post like:
#<Post id: 1, name: "foo", body: "bar", subscriptable_type: "Base", subscriptable_id: 1, created_at: "2010-05-10 12:30:10", updated_at: "2010-05-10 12:30:10">
the subscriptable_type is Base but Subscription, anybody can give me a hand on this?
If the class Base is an abstract model, you have to specify that in the model definition:
class Base < ActiveRecord::Base
self.abstract_class = true
end
Does your subscriptions table have a 'type' column? I'm guessing that Rails thinks that Base/Subscription are STI models. So when a row is retrieved from the subscriptions table and no 'type' column is present, it just defaults to the parent class of Base. Just a guess...
I'm trying to define multiple polymorphic relations (has_many_polymorphs plugin) from a single parent to same children.
Note has many viewers
Note has many editors
Viewers could be either Users or Groups
Editors could be either Users or Groups
Permission is the association model with note_id, viewer_id, viewer_type, editor_id, editor_type fields
Everything works out as expect as long as I have only one has_many_polymorphs relation defined in Note model
class User < ActiveRecord::Base; end
class Group < ActiveRecord::Base; end
class Note < ActiveRecord::Base
has_many_polymorphs :viewers, :through => :permissions, :from => [:users, :groups]
end
class Permission < ActiveRecord::Base
belongs_to :note
belongs_to :viewer, :polymorphic => true
end
Note.first.viewers << User.first # => [#<User id: 1, ....>]
Note.first.viewers << Group.first # => [#<User id: 1, ....>, #<Group ...>]
Note.first.viewers.first # => #<User ....>
Note.first.viewers.second # => #<Group ....>
Now, problems start to appear when I add the second relation
class Note < ActiveRecord::Base
has_many_polymorphs :viewers, :through => :permissions, :from => [:users, :groups]
has_many_polymorphs :editors, :through => :permissions, :from => [:users, :groups]
end
class Permission < ActiveRecord::Base
belongs_to :note
belongs_to :viewer, :polymorphic => true
belongs_to :editor, :polymorphic => true
end
Note.first.viewers << User.first # => [#<User id: ....>]
# >>>>>>>>
Note.first.editors << User.first
NoMethodError: You have a nil object when you didn't expect it!
The error occurred while evaluating nil.constantize
... vendor/plugins/has_many_polymorphs/lib/has_many_polymorphs/base.rb:18:in `instantiate'
I've tried refining the definition of has_many_polymorphs but it didn't work. Not even with an STI model for ViewPermission < Permission, and EditPermission < Permission.
Any thoughts / workarounds / issue pointers are appreciated.
Rails 2.3.0
Dont you need to add
has_many :permissions
to your Note.
FYI. I used has_many_polymorphs once but then dropped it, it wasn't working as expected.
Can you post the schema that you are using for Permission? My guess is the root of the problem lies there, you need to have multiple type, id pairs in the schema since you have two different belongs_to in the definition.
Edit:
I see you have posted the question on github as well. Not sure if you tried using the Double sided polymorphism. You probably have... like I said, I was not impressed by this plugin, as it brought in some instability when I used it.
== Double-sided polymorphism
Double-sided relationships are defined on the join model:
class Devouring < ActiveRecord::Base
belongs_to :guest, :polymorphic => true
belongs_to :eaten, :polymorphic => true
acts_as_double_polymorphic_join(
:guests =>[:dogs, :cats],
:eatens => [:cats, :birds]
)
end
Now, dogs and cats can eat birds and cats. Birds can't eat anything (they aren't <tt>guests</tt>) and dogs can't be
eaten by anything (since they aren't <tt>eatens</tt>). The keys stand for what the models are, not what they do.
#Tamer
I was getting the same error. The problem was that has_many_polymorphs creates the record in the join table using mass association and was failing. I added attr_accessible :note_id, :editor_id, and :editor_type to my Permission class and it worked afterwards. (Note: I substituted your model names for mine.)
I haven't looked too much into it, but I'd be curious if there's a way to alter this behavior. I'm fairly new to this framework and letting anything sensitive (like an Order-Payment association) be mass-assigned seems like asking to shoot myself in the foot. Let me know if this fixed your problem, and if you figure anything else out.
Best,
Steve