I have the following:
belongs_to :type, :class_name => :activity_type
belongs_to :activity_type # needed for has_one :through?
has_one :category, :through => :activity_type, :class_name => :activity_category
Is there a way to do this "has_one through" relationship using "type" instead of "activity_type"?
Edit: This was not working, and I failed to see thanks to the magic word "type".
What I have now is this:
belongs_to :company
belongs_to :type, :class_name => 'ActivityType', :foreign_key => 'activity_type_id'
has_one :category, :through => :type, :class_name => 'ActivityCategory', :foreign_key => 'activity_category_id'
But it fails with
no such column: activity_types.category_id
which is correct, since the expected column would be "activity_types.activity_category_id". How can I fix that?
You're using associations in a wrong way. First of all when you use through: then class_name, foreign_key will be omitted. So this is why it's expecting category_id. Also I think that you shouldn't overload your associations with rewriting defaults options since when there's a lot of associations on a model it starts to look like a mess and hard to understand. So you probably should just write it as belongs_to :activity_type and add whatever name suits you best like alias_method :type, :activity_type.
So in short, this is what I'm proposing you:
belongs_to :company
belongs_to :activity_type
has_one :activity_category, :through => :activity_type
alias_method :type, :activity_type
alias_method :category, :activity_category
Related
I'm building a simple ticket system in an application using MongoDB. At one point, I was able to create tickets, but now I am not. The User model is as follows:
class User
include Mongoid::Document
include Mongoid::Timestamps::Updated
has_many :initiated_tickets, :class_name => 'Ticket', :inverse_of => :initiator
has_many :assigned_tickets, :class_name => 'Ticket', :inverse_of => :assignee
The Ticket model is as follows:
class Ticket
include Mongoid::Document
include Mongoid::Timestamps::Updated
field :name
field :initiator_email
field :assignee_email
field :comment
belongs_to :alert
has_one :initiator, :class_name => 'User', :inverse_of => :initiated_tickets
belongs_to :assignee, :class_name => 'User', :inverse_of => :assigned_tickets
When I attempt to create a ticket, I get an error from Mongoid stating:
Mongoid::Errors::InverseNotFound:
Problem:
When adding a(n) User to Ticket#initiator, Mongoid could not determine the inverse foreign key to set. The attempted key was 'initiated_tickets_id'.
I'm not sure what's going wrong here. It looks like the inverse_of is set up correctly for both. Any idea why this isn't working, when it previously was? Thanks!
You only need inverse_of defined on the belongs_to side. Whereas, the has_many side should have the foreign_key defined. I have the exact same relation working as follows:
class User
has_many :initiated_tickets, foreign_key: "initiator_id", class_name: "Ticket"
has_many :assigned_tickets, foreign_key: "assignee_id", class_name: "Ticket"
class Ticket
field :initiator_id, :type => String
field :assignee_id, :type => String
belongs_to :initiator, inverse_of: "initiated_tickets" class_name: "User"
belongs_to :assignee, inverse_of: "assigned_tickets" class_name: "User"
EDIT
Rewrote my answer because I was mistaken originally.
Basically, I want to accomplish something like this:
Class Node < ActiveRecord::Base
has_and_belongs_to_many :parents, :class_name=>'Node'
has_and_belongs_to_many :children, :class_name=>'Node'
end
but it isn't working, and I'm not entirely sure the proper way to do this. I'm going to try explicitly defining a join table next and have both use it: If that's the solution, would the column be called "children_id" or "child_id"?
Class Node < ActiveRecord::Base
has_and_belongs_to_many :parents, :class_name=>'Node', :join_table => "parents_children", :foreign_key => :child_id, :association_foreign_key => :parent_id
has_and_belongs_to_many :children, :class_name=>'Node', :join_table => "parents_children", :foreign_key => :parent_id, :association_foreign_key => :child_id
end
Note that you can rename the join table and foreign keys as long as you set the appropriate foreign key names here.
This is doable, but I strongly recommend using has_many :through instead:
class Node < ActiveRecord::Base
has_many :parent_node_links,
:class_name => 'NodeLink',
:foreign_key => :child_id
has_many :parents,
:through => :parent_node_links,
:source => :parent
has_many :child_node_links,
:class_name => 'NodeLink',
:foreign_key => :parent_id
has_many :children,
:through => :child_node_links,
:source => :child
end
class NodeLink < ActiveRecord::Base
belongs_to :parent,
:class_name => "Node"
belongs_to :child,
:class_name => "Node"
end
Having a first-class join model makes it much easier to manage the relationships and gives you the freedom to add relevant meta-data at a later point in time.
Ok I'm really feeling I'm missing the rails way on this one.
Following my last question rails parameters in form_for, I can correctly update the message contents but am struggling with updating the recipients
My Draft model
class Draft < ActiveRecord::Base
belongs_to :message
belongs_to :draft_recipient, :class_name => "User"
delegate :created_at, :subject, :user, :body, :draft_recipients, :to => :message
...
My Message Model
class Message < ActiveRecord::Base
belongs_to :user
has_many :recipients, :through => :message_copies
has_many :draft_recipients, :through => :drafts
has_many :message_copies
has_many :drafts, :class_name => "Draft", :foreign_key => :message_id
attr_accessor :to #array of people to send to
attr_accessible :subject, :body, :to, :recipients, :author, :user
...
In my controller I want to do something like
new_draft_recipients = params[:draft][:draft_recipients].split(",")
#draft.update_attributes(:draft_recipients => new_draft_recipients)
which obviously doesn't work. When I try update each record comparing old (from the database )and new recipients (passed through the form), the algorithm gets ridiculously complicated. I feel what is missing is proper associations, but I don't manage to understand which. I know this is really simple. Thanks for your help
So I have a somewhat confusing relationship here, between a Note, Group, and User. And I ended up with has_many twice in my model. But I'm currently focused on the Note & Group relationship.
Background: A Group can have a note. A User can also have a note. Which is why my Note is polymorphic. However, I also created a join model called a Tag, so that a Note can belong to multiple groups. In my code though, I ended up with multiple 'has_many :notes'. See all of my code below. What would be the proper way to do something like this?
Thanks in advance!
note.rb
belongs_to :notable, :polymorphic => true
has_many :tags
has_many :groups, :through => :tags
user.rb
has_many :notes, :as => :notable
group.rb
has_many :notes, :as => :notable
has_many :tags
has_many :notes, :through => :tags
tag.rb
belongs_to :note
belongs_to :group
You just need to give it a different name.
class Group
has_many :notes, :as => :notable
has_many :tags
has_many :tagged_notes, :class_name => 'Note', :through => :tags
end
If you only want a single note for the :as => :notable part (this wasn't very clear in your question), you could just do this:
class Group
has_one :note, :as => :notable
has_many :tags
has_many :notes, :through => :tags
end
The names just have to be different. Although with note vs. notes it might not be very clear what the distinction is in other parts of your code.
I have multiple models with created_by and modified_by columns. This is what I have for a Deal Model.
class Deal
has_one :user , :foreign_key => 'created_by'
has_one :user , :foreign_key => 'modified_by'
end
class User
belongs_to :created_by , :class_name => 'Deal' , :foreign_key => 'created_by'
belongs_to :modified_by , :class_name => 'Deal' , :foreign_key => 'modified_by'
end
When I create the deal, looks like it is saving correctly. But in the show view when I try to get #deal.created_by.email I get an "undefined method email" error. Can some tell me how to get this working please?
Also since I have multiple models with these two columns, there can be many belongs_to in User model. Is there an elegant solution for this case?
First thing you have to add is the specification of accessible attributes.
In User you would have to add:
attr_accessible :email, :created_by, :modified_by
In Deal:
attr_accessible :created_by, :modified_by
But you should also change the direction of your relation. The foreign_key is always on the belongs_to side.
This is what worked for me:
class Deal < ActiveRecord::Base
belongs_to :created_by, :class_name => "User", :foreign_key => "created_by"
belongs_to :modified_by, :class_name => "User", :foreign_key =>"modified_by"
attr_accessible :created_by, :modified_by, :name
end
class User < ActiveRecord::Base
has_many :created_deals, :class_name => "Deal", :foreign_key => "created_by"
has_many :modified_deals, :class_name => "Deal", :foreign_key => "modified_by"
attr_accessible :created_deals, :modified_deals, :name
end
If you have more models, which look similiar you could probably use polymorphic associations: http://guides.rubyonrails.org/association_basics.html#polymorphic-associations
First of all, from my experience it is generally a bad idea to have associations using the foreign key as name. Especially when writing fixtures it seems rails will get confused between setting the actual value "created_by" or the model in the created_by association. In my models I generally use these associations for the cases you describe:
belongs_to :creator, :class_name => "User", :foreign_key => 'created_by'
belongs_to :modifier, :class_name => "User", :foreign_key => 'modified_by'
You can use association names like 'creating_user' instead if you prefer. If you really want created_by as association name you should have created_by_id or something similar as foreign key, just as long as its not equal to the association name.
Then I am a bit confused by your pasted code. Your choice "Deal has_one User" and "User belongs_to Deal" means that the users table will have the columns created_by and modified_by (foreign keys) containing Deal Ids, basically meaning that users get created by a single deal? However it seems like deals should get created by users and not the other way round. Your example of deal.created_by.email can not work at all with your associations, since deal would not have an association called "created_by", only "user", of which you have two associations with the same name in a single model which can not work at all in the first place.
Fixing your associations similar to what Patrick suggested:
class Deal < ActiveRecord::Base
belongs_to :creator, :class_name => "User", :foreign_key => "created_by"
belongs_to :modifier, :class_name => "User", :foreign_key =>"modified_by"
end
class User < ActiveRecord::Base
has_many :created_deals, :class_name => "Deal", :foreign_key => "created_by"
has_many :modified_deals, :class_name => "Deal", :foreign_key => "modified_by"
end