Ownership conditional in a belongs_to association - ruby-on-rails

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

Related

Rails referral model with self referential association

I'm trying to create a Referral program on a Rails app and I struggle with the relationships.
My Referral model is pretty simple : godfather_id, godson_id, state
Both godfather and godson ids references an User, which can have many godsons but only one godfather.
class Referral < ApplicationRecord
belongs_to :user
belongs_to :godson, class_name: 'User'
end
The issue comes in my User model. I wan't to be able to do user.godsons to get an array of godsons Users and user.godfather to get the godfather User.
I tried a few things and I think those two where the closest to what I need to do (User model simplified for the example).
class User < ApplicationRecord
has_many :referrals
has_many :godson, -> { where(godfather_id: id) }, through: :referrals
has_one :godfather, -> { where(godson_id: id) }, through: :referrals
end
class User < ApplicationRecord
has_many :godson_relations, class_name: 'Referral', foreign_key: 'godson_id'
has_many :godsons, through: :godson_relations
has_one :godfather_relation, class_name: 'Referral', foreign_key: 'godfather_id'
has_one :godfather, through: :godfather_relations
end
I'm really unsure about how to materialize this relationship, any help will be appreciated 🙏
To make an actual self-referential assocation you would just add a column on the users table that points back to the same table:
class AddGodfatherToUsers < ActiveRecord::Migration[6.1]
def change
add_reference :users, :godfather, null: true,
foreign_key: { to_table: :users }
end end
class User
belongs_to :god_father,
class_name: 'User',
optional: true,
inverse_of: :god_children
has_many :god_children,
class_name: 'User',
foreign_key: :god_father_id
inverse_of: :god_father
end
If you must store Referalls as a separate table you were kind of on the right track but you got the foreign keys backwards:
class Referral < ApplicationRecord
# you better be explicit here or its going to get extremely confusing
belongs_to :godfather, class_name: 'User'
belongs_to :godson, class_name: 'User'
end
class User < ApplicationRecord
has_many :referrals_as_godfather,
class_name: 'Referral', foreign_key: 'godfather_id'
has_one :referral_as_godson,
class_name: 'Referral',
foreign_key: 'godfather_id'
has_many :godsons, through: :referrals_as_godfather
has_one :godfather, through: :referral_as_godson
end
It should be noted that has_one in no way guarentees that a user can have only one referral (and thus one godfather). It just adds a LIMIT 1 to the query. You would have to enforce that with a uniqueness constraint and validations.

Association between 3 models

I would like a user be able to create a course(so it should belong to one user) and also be able to join another course that it haven't created by him.What is the proper associations between the course and the user ? I want to make the following model associations:
Class User < ActiveRecord::Base
has_many :courses
has_many :comments ,through: :courses
end
Class Course < ActiveRecord::Base
has_and_belongs_to_many :users #here i am not sure
has_many :comments
end
Class Comment < ActiveRecord::Base
belongs_to :courses
end
I think what you should be able to do something like:
Class User < ActiveRecord::Base
has_many :courses
has_many :course_users
has_many :subscribed_courses, through: :course_users, source: :course # I think you should be able to do foreign_key: :course_id, class_name: 'Course'
has_many :comments ,through: :courses
end
Class Course < ActiveRecord::Base
belongs_to :user
has_many :course_users
has_many :participants, through: :course_users, source: :user # I think you should be able to do foreign_key: :user_id, class_name: 'User'
has_many :comments
end
Class Comment < ActiveRecord::Base
belongs_to :courses
end
#course_users is a join table for courses and users
class CourseUser < ActiveRecord::Base
# inside here you could have several other connections e.g grade of a user in a course within this join model
belongs_to :user
belongs_to :course
end
If I'm understanding what you're saying - you need to have a third model - you can call it enrollment
For Course you would use belongs_to :user if each course is created as a user.
Your Enrollment model with have two HABTAM
Class Enrollment < ActiveRecord::Base
has_and_belongs_to_many :users
has_and_belongs_to_many :courses
end
(An Aside, if a course is going to be offered more than once, you'll have to add an additional model for each instance of the course and the enrollment will belong to that model, and not courses)

Active Record association has_many with multiple foreign keys

I'm trying to set up a referral model. A referral contains a user who is referred, a user who does the referring, and a doctor
class User < ActiveRecord::Base
has_many :referrals
belongs_to :profile, polymorphic: true
end
class Referral < ActiveRecord::Base
belongs_to :user
belongs_to :referrer, :class_name => "User"
belongs_to :doctor, :class_name => "User"
end
I'm able to create the generic has_many :referrals to see doctors that have been referred to the user however I'd also like to see the doctors that you've referred to others (using the referrer column).
I've tried has_many :doctors_referred, primary_key: "referrer_id" and has_many :doctors_referred, through: :referrals ,source: "referrer" with no luck. How can I see which doctors a user has referred?
# mostly irrelevant
class PatientProfile < ActiveRecord::Base
has_one :user, as: :profile
end
class DoctorProfile < ActiveRecord::Base
has_one :user, as: :profile
end
My first attempt would be this:
class User < ActiveRecord::Base
has_many :referrals_as_referrer, source: :referred
has_many :referrals_as_referree, source: :user
has_many :doctors_as_referrer, through: :referrals_as_referrer
has_many :doctors_as_referree, through: :referrals_as_referree
end
The issue here is that saying has_many :referrals isn't enough because you can have a referral where you were the referrer or one where you were the referree. With this you can type user.referrals_as_referrer which will give a list of referrals where user is the referrer. You can also write user.doctors_as_referrer which will go through the previous association and retrieve a list of users that represent doctors which were referred by a referral in the user.referrals_as_referrer list.
I haven't tested this and I might be wrong, but let's iterate once you try it out.

has_many (different objects) as: one type of object

I need a way of referrencing 2 different objects as 1.
I have a Message object with needs to keep track of Recipients. the problem is that Recipients could be a User or a Contact.
should the models be: ?
class Message < ActiveRecord::Base
has_many :users, as: :recipients
has_many :contacts, as: :recipients
end
class User < ActiveRecord::Base
belongs_to :recipient, polymorphic: true
end
class User < ActiveRecord::Base
belongs_to :recipient, polymorphic: true
end
because, I feel like polymorphic relationships are built to go the opposite way.
also, this way doesn't allow me to reference #message.recipients which is what I need.
I hope this makes sense
Thank you
What you have done is completely incorrect. I think you need many-to-many association. My association whould be that:
class Message < ActiveRecord::Base
has_many :recipient_links
has_many :users, through: :recipient_links, source: :recipient, source_type: 'User'
has_many :contacts, through: :recipient_links, source: :recipient, source_type: 'Contact'
end
class Contact < ActiveRecord::Base
has_many :recipient_links, as: :recipient
has_many :messages, through: :recipient_links
end
class User < ActiveRecord::Base
has_many :recipient_links, as: :recipient
has_many :messages, through: :recipient_links
end
# fields: message_id, recipient_id, recipient_type
class RecipientLink < ActiveRecord::Base
belongs_to :recipient, polymorphic: true
belongs_to :message
end
... I can't add comments yet so i give here solution for: When use answer from up to receive all #message.recipients every type in only 2 request:
RecipientLink.includes(:recipient).where(message_id: #message.id).collect(&:recipient)

has_many :through and has_many relationship between same 2 models

A list has one owner (a user). A list also has a number of panelists (also users). I have tried defining the relationships between the three models: User, List, and Panelist. But I'm getting nowhere.
user.rb
class User < ActiveRecord::Base
has_many :lists
has_many :panelMemberships, :through => :panelists, :source => :lists
end
list.rb
class List < ActiveRecord::Base
belongs_to :user
has_many :panelMembers, :through => :panelists, :source => :user
end
panelist.rb
class Panelist < ActiveRecord::Base
belongs_to :list
belongs_to :user
end
I've tried all different combinations but nothing seems to work. Thanks in advance for any help you can provide.
The model also has to have a has_many relationship for whatever the through model is, so wherever you have has_many :x, through: :y, you also need to say has_many :y. You also shouldn't have a panelist model separate from your user model if panelists are users (unless you're doing STI, which you're not). From what I understand, you're trying to do something like this:
class User < ActiveRecord::Base
has_many :owned_lists, class_name: "List", foreign_key: :owner_id # this is for the owner/list relationship
has_and_belongs_to_many :lists # for the normal panelist / list relationship
end
class List < ActiveRecord::Base
belongs_to :owner, class_name: "User"
has_and_belongs_to_many :users
end
Then you'll need to make a migration for a users_lists (with user id and list id) table which will be your join table but won't need its own model. But if you really want to keep the through relationship (good for if you do other stuff with the join model), then you'd do:
class User < ActiveRecord::Base
has_many :owned_lists, class_name: "List", foreign_key: :owner_id # this is for the owner/list relationship
has_many :panel_memberships
has_many :lists, through: :panel_memberships
end
class List < ActiveRecord::Base
belongs_to :owner, class_name: "User"
has_many :panel_memberships
has_many :users, through: :panel_memberships
end
class PanelMembership < ActiveRecord::Base
belongs_to :user
belongs_to :list

Resources