Suppose an app where users can give gifts to other users. There are two relations between the gift and the user (one as sender and the other one as receiver).
The receiver part seems to not be working according to the following code, where a gift is created but not retrieved when calling the association:
require 'rubygems'
require 'mongoid'
Mongoid.load!("./config/mongoid.yml")
class User
include Mongoid::Document
has_many :gifts
has_many :gifts_sent, class_name: "Gift", as: :sender
end
class Gift
include Mongoid::Document
belongs_to :user, inverse_of: :gifts
belongs_to :sender, inverse_of: :gifts_sent, class_name: "User"
end
alice = User.create!
bob = User.create!
gift = Gift.create! sender: alice, user: bob
puts Gift.where(sender_id: alice.id).count # => 1 (nice)
puts alice.gifts_sent.count # => 0 (not so nice)
How should the association be defined to make the last line output 1?
You have to give a inverse_of to has_many gifts relation.
And to work the second relation named gifts_sent, You have to mention the foreign_key while defining relation.
class User
include Mongoid::Document
has_many :gifts, inverse_of: :user
has_many :gifts_sent, :foreign_key => :assign_whatever_field, class_name: "Gift", inverse_of: :sender,
end
And the gift Model will be
class Gift
include Mongoid::Document
belongs_to :user, inverse_of: :gifts
belongs_to :sender,:foreign_key => :assign_whatever_field, inverse_of: :gifts_sent, class_name: "User"
end
It is necessary to include the inverse relation in the User model as well:
class User
include Mongoid::Document
has_many :gifts, inverse_of: :user
has_many :gifts_sent, inverse_of: :sender, class_name: "Gift"
end
class Gift
include Mongoid::Document
belongs_to :user, inverse_of: :gifts
belongs_to :sender, inverse_of: :gifts_sent, class_name: "User"
end
Related
I have an issue with mongoid / rails relations, I know that there are a lot of topics with this kind of issue, but I don't find any that help me ..
I have these models :
class Project
include Mongoid::Document
belongs_to :owner, :class_name => 'User', inverse_of: :projects
has_many :members
end
class Member
include Mongoid::Document
belongs_to :project, inverse_of: :members
belongs_to :user
end
class User
include Mongoid::Document
has_many :projects, inverse_of: :user
end
When I try to record an user as a member, I have this error :
Mongoid::Errors::InverseNotFound (
message:
When adding a(n) User to Project#members, Mongoid could not determine the inverse foreign key to set. The attempted key was 'project_id'.
summary:
When adding a document to a relation, Mongoid attempts to link the newly added document to the base of the relation in memory, as well as set the foreign key to link them on the database side. In this case Mongoid could not determine what the inverse foreign key was.
resolution:
If an inverse is not required, like a belongs_to or has_and_belongs_to_many, ensure that :inverse_of => nil is set on the relation. If the inverse is needed, most likely the inverse cannot be figured out from the names of the relations and you will need to explicitly tell Mongoid on the relation what the inverse is.
Example:
class Lush
include Mongoid::Document
has_one :whiskey, class_name: "Drink", inverse_of: :alcoholic
end
class Drink
include Mongoid::Document
belongs_to :alcoholic, class_name: "Lush", inverse_of: :whiskey
end):
I don't understand why, I think something is wrong with the relations, and the inverses relation but I don't know how to fix this issue.
class Project
include Mongoid::Document
belongs_to :owner, :class_name => 'User', inverse_of: :projects
has_many :members
has_many :users
end
But I don't think your modeling actually will accomplish what you want. In a relational database you would use an indirect association with a join table:
class User < ActiveRecord::Base
has_many :memberships
has_many :projects, through: :memberships
end
class Membership < ActiveRecord::Base
belongs_to :user
belongs_to :project
end
class Project < ActiveRecord::Base
has_many :memberships
has_many :members, through: :memberships,
source: :user
end
But over in Mongoland there are no joins so we we need to use a different approach which is embedding:
class Project
# ...
embeds_many :users
end
class User
# ...
embedded_in :project
end
Or you can fake an indirect association if you need to be able to add data to the intermediate model:
class Project
# ...
embeds_many :memberships
def members
Patient.in(id: memberships.pluck(:user_id))
end
end
class Membership
# ...
field :approved, type: Boolean
belongs_to :user
embedded_in :project
end
class User
# ...
def projects
Project.where("members.user_id" => id).all
end
end
I have one user model and one viewed_contractor model. I am treating user model as customer and contractor. customer can view many contractors by visiting their respective profile.Contractor can be viewed by many customers. I have customer_id and contractor_id in my viewed_contractor. I want to handle this relation as has_many through. Is it possible thorough has_many through?
It is possible. First, you'd need to specify the class_name option for the belongs_to associations in your ViewedContractor model so that they both refer to your User class. Then you could specify the has_many through: relations in your User model.
Something like this should work:
# viewed_contractor.rb
class ViewedContractor < ActiveRecord::Base
belongs_to :contractor, class_name: 'User', foreign_key: :contractor_id
belongs_to :customer, class_name: 'User', foreign_key: :customer_id
end
# user.rb
class User < ActiveRecord::Base
has_many :viewed_contractors_as_contractor, class_name: 'ViewedContractor', foreign_key: :contractor_id
has_many :viewed_contractors_as_customer, class_name: 'ViewedContractor', foreign_key: :customer_id
has_many :visited_contractors, through: :viewed_contractors_as_customer, source: :contractor
has_many :visited_customers, through: :viewed_contractors_as_contractor, source: :customer
end
I have two models: Users and Posts. The way I have things setup, a post belongs to an owner (i.e. user) and also has many participants (i.e. users). In my User model I'd like to ensure that an owner never belongs to a post. I've done this in the front-end but found more code than need-be.
This led me to believe that using conditions would be an ideal solution. I've seen SQL conditions used in this manner but didn't know exactly what the best way to get this done for an ownership scenario. Suggestions?
class User < ActiveRecord::Base
has_many :posts
# belongs_to :posts, conditions: ...
end
class Post
has_many :participants, class_name: "User", foreign_key: "user_id"
belongs_to :owner, class_name: "User", foreign_key: "user_id"
end
To acheive this, I think you need a third model. If you set things up as follows it should work:
User model:
class User < ActiveRecord::Base
has_many :posts # This is the other side of your owner association
has_many :user_posts # This is your link table for participants
has_many :participations, through: :user_posts, source: :user # These are the posts the user is a participant in
end
Post model:
class Post < ActiveRecord::Base
has_many :user_posts, ->(p) { where.not(user_id: p.user_id) } # Here is your condition for the participants
has_many :participants, through: :user_posts, source: :user
belongs_to :owner, class_name: "User", foreign_key: "user_id"
end
UserPost model:
class UserPost < ActiveRecord::Base
belongs_to :user
belongs_to :post
end
As #Oxynum's answer makes clear, you should also think about putting a validation in the UserPost model to prevent the participant from being saved if he is also the owner:
validate :participant_cannot_be_owner
def participant_cannot_be_owner
if user == post.try(:owner)
errors.add(:user_id, "can't be the owner of the post")
end
end
First, there is probably an error in your associations, cause it seems like you need a join table for the participants relationship.
You should probably use a http://guides.rubyonrails.org/association_basics.html#the-has-many-through-association
has_many through association.
Something like this :
class User < ActiveRecord::Base
has_one :owned_post, class_name: "Post", foreign_key: :owner_id
has_many :participations
has_many :posts, through: :participations
end
class Participation < ActiveRecord::Base
belongs_to :post
belongs_to :participant, class_name: "User"
end
class Post < ActiveRecord::Base
belongs_to :owner, class_name: "User"
has_many :participants, through: :participations
end
When you have this model, you can use a validation on the participation model to prevent an owner to be a participant. By using a custom validation : http://guides.rubyonrails.org/active_record_validations.html#performing-custom-validations
class Participation < ActiveRecord::Base
belongs_to :post
belongs_to :participant, class_name: "User"
validate :participant_is_not_the_owner
def participant_is_not_the_owner
if participant == post.owner
errors.add(:participant, "can't be the owner")
end
end
end
I want to implement these models:
Doctor # maybe I should use STI Doctor < User
has_many :cases
Patient
has_many :cases
Case # each case has one pair of doctor and patient
belongs_to :doctor
belongs_to :patient
But I have User model right now with Devise authentication. What fields should have cases table and do I need to change something in associations to make that working?
Doctor and User should be able to sign in to the app.
So I think cases table should have something like patient_id and doctor_id?
Your Case needs a doctor_id and a patient_id. These could either reference Doctor and Patient as separate models:
class Doctor < ActiveRecord::Base
has_many :cases
end
class Patient < ActiveRecord::Base
has_many :cases
end
class Case < ActiveRecord::Base
belongs_to :doctor
belongs_to :patient
end
Or they could reference User in general (probably with some kind of role field):
class User < ActiveRecord::Base
has_many :doctor_cases, class_name: "Case", foreign_key: "doctor_id"
has_many :patient_cases, class_name: "Case", foreign_key: "patient_id"
end
class Case < ActiveRecord::Base
belongs_to :doctor, class_name: "User"
belongs_to :patient, class_name: "User"
end
Now a user could be a doctor in one case and a patient in another case :-)
I think class_name will work
class User < ActiveRecord::Base
has_many :doctors, :class_name => 'User', :foreign_key => 'doctor_id'
has_many :patients, :class_name => 'User', :foreign_key => 'patient_id'
end
class Case < ActiveRecord::Base
belongs_to :doctor, :class_name => 'User', :foreign_key => 'doctor_id'
belongs_to :patient, :class_name => 'User', :foreign_key => 'patient_id'
end
1º user John has many gifts
user.rb
class User
include Mongoid::Document
has_many :gifts, dependent: :destroy, :autosave => true
has_many :orders, dependent: :destroy, :autosave => true
end
gift.rb
class Gift
include Mongoid::Document
belongs_to :user
has_many :orders ,dependent: :destroy, :autosave => true
end
2º user Anthony buy a gift to John and make a new order
class Order
include Mongoid::Document
belongs_to :gift
belongs_to :user
end
Now the user Anthony wants to access all his sales made.
The challenge here is that a user may have two roles, buyer or a seller.
How should I develop relationships between models that Antonio can access his sales made?
Models structure
class User
include Mongoid::Document
has_many :gifts, dependent: :destroy, :autosave => true
has_many :orders, dependent: :destroy, :autosave => true
end
class Gift
include Mongoid::Document
belongs_to :user
belongs_to :gifted_to, :class_name => 'User'
has_one :order ,dependent: :destroy, :autosave => true
end
class Order
include Mongoid::Document
belongs_to :gift
# below associtation is just for quicker ref
# otherwise you can have access to it via gift also
belongs_to :user
end