Associates the same model twice in mongoid - ruby-on-rails

I have two class Intern::Question and Intern::Answer, and the standard association look like :
class Intern::Question
has_many :intern_answers, class_name: 'Intern::Answer'
end
class Intern::Answer
belongs_to :intern_question, class_name: 'Intern::Question'
end
And now I want to reference twice answer belongs_to question, answer can store parent question and next question, something like :
class Intern::Question
has_many :intern_answers, class_name: 'Intern::Answer'
has_many :node_for_answers, class_name: 'Intern::Answer'
end
class Intern::Answer
belongs_to :intern_question, foreign_key: :intern_question_id, class_name: 'Intern::Question'
belongs_to :next_question, foreign_key: :next_question_id, class_name: 'Intern::Question'
end
But I have try that and get this error :
Mongoid::Errors::AmbiguousRelationship

Found solution here, using inverse_of
class Intern::Question
has_many :intern_answers, class_name: 'Intern::Answer', inverse_of: :intern_question
has_many :node_for_answers, class_name: 'Intern::Answer', inverse_of: :next_question
end
class Intern::Answer
belongs_to :intern_question, foreign_key: :intern_question_id, class_name: 'Intern::Question', inverse_of: :intern_answers
belongs_to :next_question, foreign_key: :next_question_id, class_name: 'Intern::Question', inverse_of: :node_for_answers
end

Related

Converting a has_one association to has_many

Wondering about a relationship I have and not sure wheter this is due to cause some issues in the future.
I have the following relationships with Users and Leases.
class User < ApplicationRecord
has_one :lease, foreign_key: "tenant_id"
has_many :leases, foreign_key: "landlord_id"
end
and
class Lease < ApplicationRecord
belongs_to :tenant, class_name: "User"
belongs_to :landlord, class_name: "User"
end
and I'm trying to convert the relationship with the tenant and the lease to has_many, but I don't know how to approach this the right way.
I got this to work with
class User < ApplicationRecord
has_many :leases_as_landlord, class_name: "Lease", foreign_key: "tenant_id"
has_many :leases_as_tenant, class_name: "Lease", foreign_key: "landlord_id"
end
and
class Lease < ApplicationRecord
belongs_to :tenant, class_name: "User", inverse_of: :leases_as_tenant
belongs_to :landlord, class_name: "User", inverse_of: :leases_as_landlord
end
but I don't like calling User.leases_as_landlord and User.leases_as_tenant. What I would like to do is just call User.leases to return the leases in which the User is either the landlord or the tenant.
You can add instance method:
class User < ApplicationRecord
has_many :leases_as_landlord, class_name: "Lease", foreign_key: "tenant_id"
has_many :leases_as_tenant, class_name: "Lease", foreign_key: "landlord_id"
def leases
leases_as_landlord.or(leases_as_tenant)
end
end
It will also return ActiveRecord_AssociationRelation and you can chain other ActiveRecord method on it.
Also I would recommend to follow Rails Convention and name your has_many associations in the plural.
class User < ApplicationRecord
has_many :landlord_leases, class_name: 'Lease', foreign_key: :tenant_id
has_many :tenant_leases, class_name: 'Lease', foreign_key: :landlord_id
def leases
landlord_leases.or(tenant_leases)
end
end

Self-Referential Has Many Through with Custom Foreign Keys in Rails

I have a User model and a relationship table called ParentsChildren.
I'm trying to create two relationships on the User model so that User#children returns all of a users children and User#parents returns all of a users parents.
I've managed to get this working before, but I'm doing something wrong right this time, and I'm not sure what it is exactly.
class ParentsChildren < ApplicationRecord
self.table_name = 'parents_children'
belongs_to :parent_user, class_name: 'User'
belongs_to :child_user, class_name: 'User'
end
class User
has_many :parent_relationships, class_name: 'ParentsChildren', foreign_key: :parent_user_id
has_many :child_relationships, class_name: 'ParentsChildren', foreign_key: :child_user_id
has_many :children, through: :parent_relationships, class_name: 'User', source: :child_user
has_many :parents, through: :child_relationships, class_name: 'User', source: :parent_user
end
# => uninitialized constant ParentsChildren::ChildUser
Figured it out. The key was to drop 'User' as the class name for has_many :parents and has_many :users. It's inferred through the given sources.
class User
has_many :parent_relationships, foreign_key: :child_user_id,
class_name: 'ParentsChildren'
has_many :children, through: :parent_relationships,
source: :parent_user
has_many :child_relationships, foreign_key: :parent_user_id,
class_name: 'ParentsChildren'
has_many :parents, through: :child_relationships,
source: :child_user
end

Rails. Association of the same model with many parents

I stuck with this. I have a model Position and I need to set an association where a position can be a compounded position (has related positions) and an ingredient position (has parent(s) position) at the same time.
So I created a table related_positions with :copmound_id and :ingredient_id.
To be clear what I need as an output:
related_positions
compound_id | ingredient_id|
pos_1 | pos_2
pos_1 | pos_3
pos_1 | pos_4
pos_5 | pos_2
pos_5 | pos_6
pos_5 | pos_7
pos_1.ingredients = [pos_2, pos_3, pos_4]
pos_5.ingredients = [pos_2, pos_6, pos_7]
pos_2.compounds = [pos_1, pos_5]
It might be kind of self join but with multiple parents
UPDATE:
I found this How to model a many self-referential relationship with many parents?. Which is very close. But I still can't get it work
From the description shared association can be something mentioned below:
class Position
has_many :related_positions, class_name: "RelatedPosition", foreign_key: "ingredient_id", :source => :relate_position
has_many :compounds, class_name: "RelatedPosition", foreign_key: "compound_id", :source => :compound_position
end
class RelatedPosition
belongs_to :relate_position, class_name: "Position"
belongs_to :compound_position, class_name: "Position"
end
Looks like what you want to do is a self-reference
Something like this might work
class Position
has_many :children, class_name: 'Position', foreign_key: 'parent_id'
belongs_to :parent, class_name: 'Position'
end
Thanks to that post http://blog.hasmanythrough.com/2007/10/30/self-referential-has-many-through I've got what I needed.
So, if someone has a similar case here is my solution:
class Position < ApplicationRecord
has_many :parents, class_name: 'RelatedPosition', foreign_key: 'ingredient_id', dependent: :destroy
has_many :children, class_name: 'RelatedPosition', foreign_key: 'compound_id', dependent: :destroy
has_many :compounds, through: :parents
has_many :ingredients, through: :children
end
class RelatedPosition < ApplicationRecord
belongs_to :ingredient, class_name: 'Position'
belongs_to :compound, class_name: 'Position'
end

How to create this `through`` association?

Organization and Link are associated through Node.
Organization:
has_many :nodes
has_many :links, through: :nodes, source: :where_first_links
Node:
belongs_to :organization
has_many :where_first_links, class_name: "Link",
foreign_key: "first_node_id"
has_many :where_second_links, class_name: "Link",
foreign_key: "second_node_id"
Link:
belongs_to :first_node, class_name: "Node"
belongs_to :second_node, class_name: "Node"
Question:: How can I associate Link back to Organization? I tried the line below but that does not seem to work (ArgumentError: Unknown key: :through.):
belongs_to :organization,
through: :first_node,
source: :where_first_links,
inverse_of: :links
belongs_to association not support through key
you should use has_one association
has_one :first_node_organization,
through: :first_node,
class_name: 'Organization',
source: :organization
Use has_one instead of belongs_to.
class Link < ActiveRecord::Base
belongs_to :first_node, class_name: "Node"
belongs_to :second_node, class_name: "Node"
has_one :organization, through: :first_node
end

Foreign Key class_name

So, this is my first time using foreign keys, and though I think they are working properly, I don't understand the class_name portion in the syntax. Is that the class_name that the table being referred to is in?
My code:
Game Model:
belongs_to :user, foreign_key: 'white_player_id', class_name: 'User'
belongs_to :user, foreign_key: 'black_player_id', class_name: 'User'
User Model:
has_many :games, foreign_key: 'white_player_id', class_name: 'Game'
has_many :games, foreign_key: 'black_player_id', class_name: 'Game'
I was looking at: http://ricostacruz.com/cheatsheets/rails-models.html and noitced that in their example they have the class name of both belongs_to and has_many pointing to the Folder class..
belongs_to :parent, :foreign_key => 'parent_id' class_name: 'Folder'
has_many :folders, :foreign_key => 'parent_id', class_name: 'Folder'
So, that leads me to believe that the class name is supposed to point to the class that contains the foreign_key?? A little insight would be much appreciated.
If you define multiple assocations with the same name your just overwriting the same assocation.
belongs_to :white_player, foreign_key: 'white_player_id', class_name: 'User'
belongs_to :black_player, foreign_key: 'black_player_id', class_name: 'User'
class_name is the class of the related object.
foreign_key refers to the table of the model class where you are defining the relationship when defining a belongs_to relationsship.
class Game < ActiveRecord::Base
belongs_to :white_player, foreign_key: 'white_player_id', class_name: 'User'
# foreign_key is game.white_player_id
end
So when we do game.white_player Active Record looks for:
User.find(game.white_player_id)
added:
In your second example foreign_key in has_many refers to the related table.
belongs_to :parent, :foreign_key => 'parent_id' class_name: 'Folder'
has_many :folders, :foreign_key => 'parent_id', class_name: 'Folder'
And you would not need to specify the foreign key and class name explicitly:
class Folder < ActiveRecord::Base
# ActiveRecord will infer that the class name is Folder
has_many :folders, foreign_key: 'parent_id'
# Rails will infer that the foreign_key is parent_id
belongs_to :parent, class_name: 'Folder'
end
As you can see ActiveRecord is one smart cookie and can infer class names and foreign keys.
Here is an easier to explain example of has_many and foreign_keys:
class User < ActiveRecord::Base
has_many :unread_messages, -> { where read: false },
foreign_key: 'recipient_id', # refers to messages.recipient_id
class_name: 'Message'
end
user.unread_messages will query the table message:
SELECT "messages".* FROM "messages" WHERE "messages"."recipient_id" # ...
class_name is for the class which is used in method you use, if ActiveRecord can not decide which class it is. In your example you dont need class_name because your method is user and class that will connect to is User, ActiveRecord can figure that out on its own.
You have another problem. You have two relations with same name user and user, that is not posible.
You could made it like this though:
Game Model:
belongs_to :white_player, foreign_key: 'white_player_id', class_name: 'User'
belongs_to :black_player, foreign_key: 'black_player_id', class_name: 'User'
UserModel:
has_many :white_games, class_name: 'Game'
has_many :black_games, class_name: 'Game'
You might want to create it with a new model, the simpliest way to do it :
Migration - 1/2 : console
rails g model Game white_player:references black_player:references
Migration - 2/2 : db/migrate/create_games.rb
In the migration file, delete the "foreign_key: true" entry, that would look like :
t.references :white_player, foreign_key: true
t.references :black_player, foreign_key: true
Run : rails db:migrate
Model files :
Game Model :
belongs_to :white_player, class_name: 'User'
belongs_to :black_player, class_name: 'User'
User Model :
has_many :white_player_games, class_name: 'Game', foreign_key: 'white_player_id'
has_many :black_player_games, class_name: 'Game', foreign_key: 'white_player_id'
Hope that helps.

Resources