Mongoid could not determine the inverse foreign key to set - ruby-on-rails

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.

Related

Has_many through with multiple relationships between the entities

I have these model definitions:
class Property < ActiveRecord::Base
belongs_to :user
has_many :users_favourites_properties, class_name: 'UsersFavouritesProperties', dependent: :destroy, :foreign_key => :property_id
has_many :favourites, :class_name => "User", through: :users_favourites_properties
end
class User < ActiveRecord::Base
has_many :properties
has_many :users_favourites_properties, class_name: 'UsersFavouritesProperties', dependent: :destroy, :foreign_key => :user_id
has_many :favourites, class_name: "Property", through: :users_favourites_properties
end
class UsersFavouritesProperties < ActiveRecord::Base
self.table_name = "favourites"
belongs_to :favourites, class_name: 'User', :foreign_key => :user_id, :primary_key => :id
belongs_to :favourites, class_name: 'Property', :foreign_key => :property_id, :primary_key => :id, counter_cache: :users_count
end
When I execute:
current_user.favourites << property
The UsersFavouritesProperties object created has the same user_id and property_id because the user_id is beign setted with the property_id.
Do you know what is happening?
You seem to misunderstand what the association name is for.
Each association method call (like has_many) adds certain methods to a model that allow for easy lookup for associated objects.
The name of association means (in scope of a model) "what is it for me". A symbol you specify first is an association name, and it is used (among other things) as a name of a method that you will use to access that association.
By defining an association named favourites once, you've defined the methods for UsersFavouritesProperties. The second time you define an association with the same name, some of the first one is redefined and crazy things can happen.
So, following that "what is it for me" principle, you can figure that a User associated with a certain UsersFavouritesProperty is UsersFavouritesProperty's user, and the resulting association, that you have:
belongs_to :favourites, class_name: 'User', :foreign_key => :user_id, :primary_key => :id
should be more like simple:
belongs_to :user
The rest is not even needed, as it follows the Rails' convention: class name and foreign key can be derived from association name in standard ways (capitalize for class name, add _id for foreign key), primary key is default.
I strongly recommend you read the Rails Style Guide and let ActiveRecord guess the most you can provide.
These lines
belongs_to :favourites, ...
belongs_to :favourites, ...
Should be
belongs_to :user, ...
belongs_to :property, ...
You can't use two keys for belongs_to

Facebook like requests model. Many-to-many with the same model

I have a Users model:
class User < ActiveRecord::Base
has_many :sent_requests, :foreign_key => :sender_id, :class_name => "Request"
has_many :recieved_requests, :foreign_key => :recipient_id, :class_name => "Request"
and a requests model:
class Request < ActiveRecord::Base
belongs_to :sender, class_name: "User", :source => "sender_id"
belongs_to :recipient, class_name: "User", :source => "recipient_id"
Im struggling to understand the difference of :foreign_key, :through, :source, :class_name, etc.
This seemed to be slightly different from most tutorial examples because a User can belong to a relationship on both sides (although not at the same time).
Any advice for how to get this working. And more importantly how to think about the relationship?
fyi:
my Request model just has sender_id, recipient_id, and accepted:boolean.
Here's what I'm using:
In user's model:
has_many :sent_requests,
:class_name => "Request",
:foreign_key => "sender_id"
has_many :received_requests,
:class_name => "Request",
:foreign_key => "receiver_id"
In request's model:
belongs_to :sender,
:class_name => "User"
belongs_to :receiver,
:class_name => "User"
Note that we user :class_name to specify the name of the class when rails cannot infer the name of the class. If you say:
has_many :requests
then you don't need to include the class name. Rails will automatically know that it is Request. But if you want to rename :request to something like :sent_request, you need to specify the class.
Also note that the foreign key is by default guessed to be the name of the association with an “_id” suffix. So a class that defines a belongs_to :person association will use “person_id” as the default :foreign_key. You need to specify the foreign_key if you are using a name that does not satisfy rails naming convention.

How to specify a foreign key?

i.e. Post model has a column poster_id and for posters I'm actually using User model. Obviously this is a User has_many Posts relationship, but how to I specify that poster_id is actually an id to User model?
Use foreign_key option:
has_many :posts, :foreign_key => :poster_id
For Post model it will be
belongs_to :user, :foreign_key => :poster_id
or
belongs_to :poster, :class_name => 'User'

Rails inverse_of confusion

I have these two models: Team and User.
Each user can have team and each team has many users. But I want to call team.users with team.members. I managed to do this with class_name, but I also need to have access to Team by User. In other words user.team should return me the team, in which the User is. Here is what I came up with...
My models are as follows:
class Team < ActiveRecord::Base
has_many :members, foreign_key: 'id', class_name: 'User', :inverse_of => :team
end
class User < ActiveRecord::Base
belongs_to :team, :inverse_of => :user
end
(I tried with :inverse_of => :members too, inside the User model.)
You got the :inverse_of right, at least when you use :inverse_of => :members. The problem here is the foreign key. foreign_key specifies the name of the key-column in the other table, so in your case in the user-table. If you specify just id, the id of the user would be used. Just leave it to the default by not specifying an foreign key, then its team_id.
class Team < ActiveRecord::Base
has_many :members, :class_name => 'User', :inverse_of => :team
end
class User < ActiveRecord::Base
belongs_to :team, :inverse_of => :members
end
And try to not mix hash-syntax from ruby 1.8 (:key => 'value') with the new syntax from ruby 1.9 (key: 'value').

Rails multiple has_one associations

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

Resources