How to do has_many and has_one association in same model? - ruby-on-rails

I need to do two associations in the same model. Where:
Team has_many User
Now, I want that Team has_one Leader
This "Leader" will be a User
Im trying to use has_one throught but I think that association isn't work.
Leader.rb
class Leader < ActiveRecord::Base
belongs_to :user
belongs_to :team
Team.rb
class Team < ActiveRecord::Base
has_one :user, through: :leader
end
User.rb
class User < ActiveRecord::Base
belongs_to :team
has_one :captain
end
and the get following error around line 27:
NoMethodError in TeamsController#create
26 def create
**27 #team = current_user.teams.create(team_params)**
28 #team.save
29 respond_with(#team)
30 current_user.update(team_id: #team.id)

In this case I think you need 2 model are enough
1). User model
class User < ActiveRecord::Base
belongs_to :team
end
2). Team model
class Team < ActiveRecord::Base
has_many :users
belongs_to :leader, class_name: 'User', foreign_key: :leader_id
end

How about setting a boolean flag in users table called leader. And then your association can become:
class Team < ActiveRecord::Base
has_many :users
has_one :leader, class_name: 'User', -> { where leader: true }
end

Team has_many User Now, I want that Team has_one Leader
This "Leader" will be a User
Use inheritance (also called sub-classing), Leader is a User.
class User < ActiveRecord::Base
belongs_to :team
end
class Leader < User
end
class Team < ActiveRecord::Base
has_many :users
has_one :leader
end
Your users table is also important. Ensure that users has t.belongs_to :team and t.string :type in its create_table method. Note that a Leader is a User and does not need a separate table, however you do need to allow ActiveRecord to record its type so it can return the correct Model later.
References:
inheritance specifically you need 'single table inheritance'
belongs_to scroll down for has_one and has_many, the three relationships used here.

current_user.teams.create(team_params)
Teams is for a has_many association, you want current_user.create_team(team_params)

You have has_one association between user and team. Try this:
current_user.create_team(team_params)
Also, you should add proper back association from team to leader.
class Team < ActiveRecord::Base
belongs_to :leader
has_one :user, through: :leader
end

Related

Rails combine multiple polymorphic has_many associations

I have a model called Organization that has many teams and has many collaborations. A Collaboration also has many teams. So the team model has a polymorphic association with Organization and Collaboration.
What I would like to do is organization.teams and have that reflect all teams from the organization and all teams from the collaborations a part of the organization.
Tables
*organizations*
id
user_id
title
...
*collaborations*
id
organization_id
title
...
*teams*
id
teamable_id
teamable_type
title
...
Models
class Organization < ApplicationRecord
has_many :orgaization_teams, as: :teamable, class_name: "Team"
has_many :collaboration_teams, through: :collaborations, source: :teams, class_name: "Team"
end
class Collaboration < ApplicationRecord
belongs_to :organization
has_many :teams, as: :teamable
end
class Team < ApplicationRecord
belongs_to :teamable, polymorphic: true
belongs_to :organization, -> { joins(:teams).where(teams: {teamable_type: 'Organization'}) }, foreign_key: 'teamable_id'
belongs_to :collaboration, -> { joins(:teams).where(teams: {teamable_type: 'Collaboration'}) }, foreign_key: 'teamable_id'
end
Attempts
Attempt #1
class Organization < ApplicationRecord
has_many :teams, -> { joins(:organization, :collaboration) }, source: :teamable
end
Result:
SystemStackError (stack level too deep)
Attempt #2
class Organization < ApplicationRecord
def teams
orgaization_teams.or(collaboration_teams)
end
end
Result:
ArgumentError (Relation passed to #or must be structurally compatible. Incompatible values: [:joins])
Potential Solution
I'm considering separating the polymorphic association on Team to organization_id and collaboration_id
So the new table would look like this:
*teams*
id
organization_id
collaboration_id
title
...
I would go with two seperate foreign keys instead:
class Organization < ApplicationRecord
has_many :teams
has_many :collaborations
has_many :collaboration_teams,
through: :collaborations,
source: :team
end
class Collaboration < ApplicationRecord
belongs_to :organization
has_many :teams
end
class Team < ApplicationRecord
belongs_to :team, optional: true
belongs_to :organization, optional: true
end
If you want to get teams that directly belong to an or org or a its collaborations you want:
Team.where(
organization_id: org.id
).or(
Team.where(
collaboration_id: org.collaborations
)
)
I don't think this can actually be written as an association as its too structurally complex.

How to create a group of users (roomates) within one product (property) in Rails

I have a question on a platform I'm developing in Ruby on Rails 5.2.
I have an Owner model which is the owner of properties/property. The owner will post a property so that users (in this case roomates) can share the same property/house/department, etc.
I have Owners and I have Users (both tables are created using devise):
Owner.rb:
class Owner < ApplicationRecord
has_many :properties
end
User.rb:
class User < ApplicationRecord
#Theres nothing here (yet)
end
This is where the magic happens. Property.rb:
class Property < ApplicationRecord
belongs_to :owner
has_many :amenities
has_many :services
accepts_nested_attributes_for :amenities
accepts_nested_attributes_for :services
mount_uploaders :pictures, PropertypictureUploader
validates :amenities, :services, presence: true
scope :latest, -> { order created_at: :desc }
end
How can multiple users share a property? I'm aware that it will have a many-to-many association but I'm a bit confused how to connect these relationships so when the owner posts a property it will display something like:
Property available for: 3 users
And then begin to limit users until it completes the amount of users available.
This sounds like your average many to many assocation:
class User < ApplicationRecord
has_many :tenancies, foreign_key: :tenant_id
has_many :properties, through: :tenancies
end
class Tenancy < ApplicationRecord
belongs_to :tenant, class_name: 'User'
belongs_to :property
end
class Property < ApplicationRecord
has_many :tenancies
has_many :tenants, through: :tenancies
def availablity
# or whatever attribute you have that defines the maximum number
max_tenants - tenancies.count
end
end
You can restrict the number of tenants with a custom validation.
You can use a join table, called users_properties. This table will have a property_id and user_id. You'll then have the following in your properties model:
has_many :users_properties
has_many :users, through: :users_properties
Read more about it here https://guides.rubyonrails.org/association_basics.html

Polymorphic has-many-through relationships in Rails

I am trying to set up a polymorphic has-many-through relationship with ActiveRecord. Here's the end goal:
Users can belong to many organizations and many teams
Organizations have many users and many teams
Teams have many users and belong to an organization
I am trying to use has-many-through instead of has-and-belongs-to-many, since I need to associate some information along with the relationships (like user role in the organization or team), so I made a join table Membership.
How would I implement this?
I would design the schema like this:
Organization has many Team
Team has many TeamMember
User has many TeamMember
TeamMember belongs to User and Team
The models will be:
organization.rb
class Organization < ActiveRecord::Base
has_many :teams
has_many :team_members, through: :teams
has_many :users, through: :team_members
end
team.rb
class Team < ActiveRecord::Base
belongs_to :organization # fk: organization_id
has_many :team_members
has_many :users, through: :team_members
end
user.rb
class User < ActiveRecord::Base
has_many :team_members
has_many :teams, through: :team_members
has_many :organizations, though: :teams
end
team_member.rb
class TeamMember < ActiveRecord::Base
belongs_to :team # fk: team_id
belongs_to :user # fk: user_id
attr_accessible :role # role in team
end
So, compare with your requirements:
Users can belong to many organizations and many teams
=> Okay
Organizations have many users and many teams
=> Okay
Teams have many users and belong to an organization
=> Okay
Btw, we don't use any polymorphic here, and TeamMember stands for Membership in your early idea!
For polymorphic association,
class User
has_many :memberships
end
class Team
belongs_to :organization
has_many :memberships, :as => :membershipable #you decide the name
end
class Organization
has_many :memberships, :as => :membershipable
has_many :teams
end
class Membership
belongs_to :user
belongs_to :membershipable, polymorphic: true
end
Note that User is indirectly associated to Team and Organization, and that every call has to go through Membership.
In my projects, I use a Relationship class (in a gem I've named ActsAsRelatingTo) as the join model. It looks something like this:
# == Schema Information
#
# Table name: acts_as_relating_to_relationships
#
# id :integer not null, primary key
# owner_id :integer
# owner_type :string
# in_relation_to_id :integer
# in_relation_to_type :string
# created_at :datetime not null
# updated_at :datetime not null
#
module ActsAsRelatingTo
class Relationship < ActiveRecord::Base
validates :owner_id, presence: true
validates :owner_type, presence: true
validates :in_relation_to_id, presence: true
validates :in_relation_to_type, presence: true
belongs_to :owner, polymorphic: true
belongs_to :in_relation_to, polymorphic: true
end
end
So, in your User model, you would say something like:
class User < ActiveRecord::Base
has_many :owned_relationships,
as: :owner,
class_name: "ActsAsRelatingTo::Relationship",
dependent: :destroy
has_many :organizations_i_relate_to,
through: :owned_relationships,
source: :in_relation_to,
source_type: "Organization"
...
end
I believe you may be able to leave the source_type argument off since the joined class (Organization) can be inferred from :organizations. Often, I'm joining models where the class name cannot be inferred from the relationship name, in which case I include the source_type argument.
With this, you can say user.organizations_i_relate_to. You can do the same set up for a relationship between any set of classes.
You could also say in your Organization class:
class Organization < ActiveRecord::Base
has_many :referencing_relationships,
as: :in_relation_to,
class_name: "ActsAsRelatingTo::Relationship",
dependent: :destroy
has_many :users_that_relate_to_me,
through: :referencing_relationships,
source: :owner,
source_type: "User"
So that you could say organization.users_that_relate_to_me.
I got tired of having to do all the set up, so in my gem I created an acts_as_relating_to method so I can do something like:
class User < ActiveRecord::Base
acts_as_relating_to :organizations, :teams
...
end
and
class Organization < ActiveRecord::Base
acts_as_relating_to :users, :organizations
...
end
and
class Team < ActiveRecord::Base
acts_as_relating_to :organizations, :users
...
end
and all the polymorphic associations and methods get set up for me "automatically".
Sorry for the long answer. Hope you find something useful in it.

Rails - has_one and belongs_to for association

I have these two tables:
accounts
- user_id
users
- account_id
Many users can belong to an account and an account can have exactly one owner with full permissions. If a user owns an account, the two should reference each other. I'm trying to figure out how to set up this association. Here's what I have:
class Account < AR::Base
has_many :users
has_one :owner, class_name: 'User', foreign_key: :user_id
This seems right to me, but the User class is definitely not:
class User < AR::Base
belongs_to :account
has_one :account
An object can't belong to and have one at the same time. How should I set up my User class?
Following should work I think:
class Account < AR::Base
has_many :users
belongs_to :owner, class_name: 'User', foreign_key: :user_id
class User < AR::Base
belongs_to :account
has_one :account, inverse_of: :owner

belongs_to has_one structure

I have an application which has the following characteristics
There are Clubs
Each Club has Teams
Each Team has Players
I have a users table. The user table basically contains the username and password for the club manager, team manager and the player to login to the system.
How should I structure the models and the tables?
I plan to create tables for Club, Team and Players. But I am not sure show to structure the relationship between them and the users table.
I could create user_id in each of the model, but the relationship would be Club belongs_to User which doesn't seems right. Moreover I would end up with a User model that has the following
has_one :club
has_one :team
has_one :player
Which is not right. A user will have only one of them at any given time.
Is there a better way to structure this?
Under Rails, has_one is really "has at most one". It's perfectly valid to have all three has_one decorators in User. If you want to ensure they only have precisely one, you could add a validation, for instance:
class User < ActiveRecord::Base
has_one :club
has_one :team
has_one :player
validate :has_only_one
private
def has_only_one
if [club, team, player].compact.length != 1
errors.add_to_base("Must have precisely one of club, team or player")
end
end
end
Since you have the ability to change the users table in the database, I think I would put club_id, team_id, player_id in users, and have the following:
class Club < ActiveRecord::Base
has_one :user
has_many :teams
has_many :players, :through => :teams
end
class Team < ActiveRecord::Base
has_one :user
belongs_to :club
has_many :players
end
class Player < ActiveRecord::Base
has_one :user
belongs_to :team
has_one :club, :through => :team
end
class User < ActiveRecord::Base
belongs_to :club
belongs_to :team
belongs_to :player
validate :belongs_to_only_one
def belongs_to_only_one
if [club, team, player].compact.length != 1
errors.add_to_base("Must belong to precisely one of club, team or player")
end
end
end
I'd even be tempted to rename User as Manager, or have has_one :manager, :class_name => "User" in the Club, Team and Player models, but your call.

Resources