Mongoid::Errors::AmbiguousRelationship: - ruby-on-rails

talk_groups table
field :parent_topic_id, type: Integer, default: -1
has_many :topics, :dependent => :destroy
belongs_to :parent_topic, :class_name => 'Topic', :foreign_key => :parent_topic_id
topics table
belongs_to :talk_group
The relation above works well on sqlite/mysql, but doesn't work on mongoid
because when a model can't have many and belongs_to with another same model
parent_topic.talk_group will appear Mongoid::Errors::AmbiguousRelationship: error

the problem is caused not because "when a model can't have many and belongs_to with another same model".
it's because when mongoid looks at the topic model, it can't know if the talk_group field is referring to the topics or parent_topic relation in the talk_group model.
mongoid provided an option to avoid this "AmbiguousRelationship" it's the inverse_of option...
so to solve this issue you should alter the topic model to become
belongs_to :talk_group, inverse_of: :parent_topic
and the talk_group model should become
has_many :topics, dependent: :destroy
belongs_to :parent_topic, class_name: 'Topic'
# you can add inverse_of option on this side as well although it's not mandatory
belongs_to :parent_topic, class_name: 'Topic', inverse_of: :talk_group
also refere to this question for more info.

Related

I have a questions about using :source when building associations in rails

I have a problem when setting up associations in my rails project and wonder if anyone can help me with that??
I have three models: user, comment and event. And there are two kinds of users: organization and volunteer. I have problem when I tried to make event.volunteers and volunteer.joined_events work...
Here are how the models set up:
class Comment < ApplicationRecord
    belongs_to :organization, class_name: "User"
    belongs_to :volunteer, class_name: "User"
    belongs_to :event
end
class User < ApplicationRecord
    has_many :organized_events, foreign_key: "organization_id", class_name: "Event"
    has_many :joined_events, through: :being_commented_comments, :source => :event
    has_many :commented_comments, foreign_key: "organization_id", class_name: "Comment"
    has_many :being_commented_comments, foreign_key: "volunteer_id", class_name: "Comment"
end
class Event < ApplicationRecord
    belongs_to :organization, class_name: "User"
    has_many :volunteers, through: :comments, source: "Volunteer"
    has_many :comments
end
And I keep getting errors like:
ActiveRecord::HasManyThroughSourceAssociationNotFoundError (Could not find the source association(s) "Event" in model Comment. Try 'has_many :joined_events, :through => :being_commented_comments, :source => <name>'. Is it one of organization, volunteer, or event?)
or
ActiveRecord::HasManyThroughOrderError (Cannot have a has_many :through association 'User#joined_events' which goes through 'User#being_commented_comments' before the through association is defined.)
and I think the problem happens because I am not familiar enough with :source...Any suggestions would be super appreciated! Thanks!
When using has_many through: you must declare the association you are going through before the indirect association:
class User < ApplicationRecord
has_many :organized_events, foreign_key: "organization_id", class_name: "Event"
has_many :being_commented_comments, foreign_key: "volunteer_id", class_name: "Comment"
has_many :joined_events, through: :being_commented_comments, source: :event
has_many :commented_comments, foreign_key: "organization_id", class_name: "Comment"
end

Rails many to many relationship confusion

So im working on a rails app for users to create events (and attend other created events). You can read about the assignment here (for the Odin Project): https://www.theodinproject.com/courses/ruby-on-rails/lessons/associations
Anyways I thought I had understood many to many relationships in rails, but the way i've seen other people write the models is confusing to me.
To me it seems like it should be something like:
class User < ApplicationRecord
has_many :attendances
has_many :events, through: :attendances
end
class Attendance < ApplicationRecord
belongs_to :user
belongs_to :event
end
class Event < ApplicationRecord
has_many :users
has_many :users, through: :attendances
end
This makes sense to me because a User can create many events, and an event can have many users attending. (Although attendances is probably the wrong word, maybe invites or something).
But i've seen some weird examples (You can see others source code below on the project) and it seems like they are adding much more to the models and also renaming the source/foreign_key/class_name.
Am I missing something? This still allows a user to "own" an event right? Maybe im mis-understanding how many-to-many works. But this fits at least in my mind of how it should be.
For reference some other models I was seeing was similar to this:
class Event < ActiveRecord::Base
belongs_to :creator, :class_name => "User"
has_many :event_attendees, :foreign_key => :attended_event_id
has_many :attendees, :through => :event_attendees
end
class EventAttendee < ActiveRecord::Base
belongs_to :attendee, :class_name => "User"
belongs_to :attended_event, :class_name => "Event"
end
class User < ActiveRecord::Base
has_many :created_events, :foreign_key => :creator_id, :class_name => "Event"
has_many :event_attendees, :foreign_key => :attendee_id
has_many :attended_events, :through => :event_attendees, :foreign_key => :attendee_id'
end
Basically similar things to the above. Im not really sure what this is doing? Or why all the extra is necessary.
In your example everything according to conventions. Maybe except many-to-many table naming.
attendances table has 'user_id' and 'event_id' fields. But in case it could conflict with other fields, or not descriptive enough you could use different keys.
belongs_to :creator, :class_name => "User"
belongs_to :creator by default would look for Creator model, so it is needed to specify class name explicitly, like in the provided example.
has_many :event_attendees, :foreign_key => :attended_event_id
By default foreign key would be event_id, so here it is specified explicitly too.
has_many :created_events, :foreign_key => :creator_id, :class_name => "Event"
By default, rails would look for user_id foreign key and CreatedEvent model. And these attributes specified explicitly.
You just need to understand what attributes rails provides by default, to change if it is required.
ActiveRecord associations default to a class and foreign key with the same name as the association. The code here is specifically specifying these because they are not the default.

has_one through association with condition

I have 3 relevant tables/models. I would like to retrieve the user which has organized a party, given a Party record, a join table with a boolean organized column, and a users table.
My best attempt so far (this one makes the most sense to me after much fiddling). I've omitted the irrelevant columns.
class Party
# party_id
has_many :parties_users
has_many :users, through: :parties_users, source: :user
has_one :organizer,
-> { where organizer: true },
through: :parties_users,
source: :user
class PartiesUser
# party_id
# user_id
# organized:bool
belongs_to :party
belongs_to :user
class User
# user_id
has_many : parties_users
The above setup raises the following error, which I honestly don't fully understand:
ActiveRecord::HasOneThroughCantAssociateThroughCollection (Cannot have a has_one :through association 'Party#organizer' where the :through association 'Party#parties_users' is a collection. Specify a has_one or belongs_to association in the :through option instead.)
I know I can do this via an instance method, but given the frequency types of use, my app would massively benefit from having this as an association.
As the error message says, you can't have a has_one through a has_many.
You could (instead) do it this way if the organizer flag is on the join record...
has_many :parties_users
has_many :users, through: :parties_users, source: :user
has_one :main_party_user, -> {where organizer: true}, class_name: 'PartiesUser'
has_one :organizer, through: :main_party_user, class_name: 'User'

model relation going both ways with foreign key

I have two models reservations and reviews I can go reservation.review and get a correct output in cases. However, when i go review.reservation im not getting the same respect i get the relation error StatementInvalid.
Reviews model:
belongs_to :reservation, :foreign_key => :reservation_id, class_name: 'Reservation'
belongs_to :reviser
has_one :reviser
has_one :reservation
belongs_to :user
Reservation model:
has_one :review, :dependent => :destroy
A few things to note about your reviews model. You have this line:
belongs_to :reservation, :foreign_key => :reservation_id, class_name: 'Reservation'
but since you're naming the foreign key reservation_id, which is what rails names the foreign key by default, you can simply get rid of that part and say:
belongs_to :reservation
Secondly, I'm not totally sure why you have has_one :reservation since you already have belongs_to :reservation. I would probably delete the has_one :reservation line unless you're absolutely sure it should be there.
So your new reviews model would look like this:
belongs_to :reservation
belongs_to :reviser
has_one :reviser
belongs_to :user
I would double check in your schema.rb file to ensure that you have the field reservation_id in your reviews table. That field must be present for the association to work properly.

Default sort by associated field with reverse_relationship

A miniature may have many contents.
class Miniature < ActiveRecord::Base
has_many :contents, foreign_key: "setmini_id", dependent: :destroy
has_many :minisets, :through => :contents, source: :miniset
has_many :reverse_contents, foreign_key: "miniset_id", class_name: "Content", dependent: :destroy
has_many :setminis, :through => :reverse_contents, source: :set mini
On a miniature's show view I currently list it's contents. What I want to do is add a default sort scope to my Contents model so that it sorts by name.
class Content < ActiveRecord::Base
default_scope { order('name ASC') }
belongs_to :miniset, class_name: "Miniature"
belongs_to :setmini, class_name: "Miniature"
My attempt here fails and complains "No such column name:".
With a normal has_many_through relationship this would work but I'm guessing because I'm using the join table in two directions and declaring class_name: "Miniature" this doesn't work here.
Is there a way I can get this list to default sort by name?
I've tried "content.name" and "setmini.name" to no avail.
You'll need the name of the database table in your order clause (not the model or association name). If you're following Rails' conventions for naming, this would be:
order('contents.name ASC')
After looking at some other answers to similar questions I eventually got it working with the following:
default_scope joins(:setmini).order('miniatures.name ASC')

Resources