New to Ruby, Rails, and OOP in general. I've been through the "Ruby on Rails Tutorial: Learn Web Development with Rails" and about 80% through "Agile Web Development with Rails 4" but I can't visually figure this out. I was hoping someone can help me understand, maybe I'm wording it wrong.
Models: User, Team, Membership, Order, OrderLine
It's a pretty basic setup. (short hand, if you will...)
Team belongs_to :user (owner, not terribly relevant here)
Users has_many :teams through: :memberships
Team has_many :users through: :memberships
OrderLine belongs_to :team
Note an OrderLine is assigned to a team, not a whole Order.
I'm trying to display all OrderLines which are associated with the currently logged in user (current_user). While this doesn't actually work, I feel it's close to producing. It also feels super dirty.
def index
#memberships = current_user.memberships.ids
membership_list = #memberships.join(", ")
#OrderLines = OrderLine.where("team_id IN (?)", membership_list)
end
Cheers!
# Model
class User < ActiveRecord::Base
has_many :owned_team, :class_name => "Team"
has_many :teams
has_many :memberships
has_many :order_lines, through: :memberships
class Team < ActiveRecord::Base
belongs_to :faction
belongs_to :region
belongs_to :owner, :class_name => "User"
has_many :users, through: :memberships
has_many :order_lines
class Membership < ActiveRecord::Base
belongs_to :user
belongs_to :team
has_many :order_lines, through: :team
# Controller
#OrderLines = current_user.order_lines
Some times all you need is a break.
Related
I have 3 model User Project Bug. I want to create many to many relation with through. I create the relation in model i don't know it is correct or not, user have user type column which is enum type user type contain developer, manager , QA
user.user_type.manager belong to many project it has one to many relation
user.user_type.developer has many project and many project belong to developer. it has many to many realtion
project has many bugs and bugs belong to project
developer has many bugs and many bugs belong to developer
bug model
class Bug < ApplicationRecord
belongs_to :project
has_many :developers, -> { where user_type: :Developer }, class_name: 'User', through: :project, source: :bugs
end
project model
class Project < ApplicationRecord
has_many :bugs, dependent: :delete_all
has_many :developers, -> { where user_type: :Developer }, class_name: 'User', through: :users, source: :project
has_many :users //it belong to manager_id
end
user model
class User < ApplicationRecord
enum user_type: %i[Manager Developer QA]
has_many :projects
has_many :bugs
end
developer_bug model
class DevelopersBug < ApplicationRecord
has_many :bugs
has_many :users
end
project_developer model
class ProjectsDeveloper < ApplicationRecord
has_many :projects
has_many :users
end
This
has_many :developers, -> { where user_type: :Developer },
class_name: 'User',
through: :users,
source: :project
is not what you think it is. It means something on the line of:
I already have an association 'users'. The users have an association 'project'.
Please configure an association that makes both JOINs and gives me the list of projects associated to the associated users.
This association will be named "developers" and be of objects of class "User".
You can see how these instructions are inconsistent. This
has_many :projects, through: :users, source: :project
will define a list of associated projects, by jumping over users.
On the other side, this:
has_many :developers, -> { where user_type: :Developer }, class_name: 'User'
will define a direct has-many association with a subset of all the users.
Given your description, your data model seems wrong, maybe this will be better:
class User < ApplicationRecord
has_many :managed_projects, inverse_of: :manager, class_name: 'Project'
has_and_belongs_to_many :projects
has_many :bugs
end
class Project < ApplicationRecord
belongs_to :manager, class_name: 'User', inverse_of: :managed_projects
has_and_belongs_to_many :users
has_many :bugs
end
class Bug < ApplicationRecord
belongs_to :user
belongs_to :project
end
Your schema should include the three tables, and an additional many-to-many join table projects_users that holds foreign keys to both users and projects.
As rewritten has already pointed in his excellent answer out your data model is flawed. What you want instead is a join table which joins the users and projects:
class User < ApplicationRecord
has_many :project_roles
has_many :projects, through: :project_roles
end
class ProjectRole < ApplicationRecord
belongs_to :user
belongs_to :project
end
class Project < ApplicationRecord
has_many :users
has_many :projects, through: :project_roles
end
If you then want to give the user specific roles in a project you would add the enum to the join table and this is where it starts to get hairy so bear with me here:
class ProjectRole < ApplicationRecord
enum roles: [:manager, :developer, :qa]
belongs_to :user
belongs_to :project
end
class User < ApplicationRecord
has_many :project_roles
has_many :projects, through: :project_roles
has_many :project_roles_as_manager,
-> { manager }, # short for `where(role: :manager)`
class_name: 'ProjectRole'
has_many :projects_as_manager,
class_name: 'Project',
through: :project_roles_as_manager,
source: :project
has_many :project_roles_as_developer,
-> { developer },
class_name: 'ProjectRole'
has_many :projects_as_developer,
class_name: 'Project',
through: :project_roles_as_developer,
source: :project
# ...
end
This defines associations with a default scope and then joins through that association. You would then do the same thing on the other end of the assocation:
class Project < ApplicationRecord
has_many :users
has_many :projects, through: :project_roles
has_many :manager_project_roles,
-> { manager },
class_name: 'ProjectRole'
has_many :managers,
through: :manager_project_roles,
source: :user
# ...
end
Of course this is a lot of duplication which you can cut by looping over ProjectRoles.roles.keys and defining the assocations dynamically.
This is a very flexible way of modeling it which makes as few assumptions about the domain as possible. For example it allows multiple managers for a project and it allows users to have different roles in different projects.
If you want to model "bugs" as you would typically would with issues in a tracker you would create one table for the bug and a join table for the assignment:
class Bug < ApplicationRecord
belongs_to :project
has_many :bug_assignments
has_many :users, through: :bug_assignments
end
class BugAssignment < ApplicationRecord
has_one :project, through: :bug
belongs_to :bug
belongs_to :user
end
class User < ApplicationRecord
# ...
has_many :bug_assignments
has_many :bugs
end
So I have three models, User, Team, and Game. Currently constructed as such.
class Team < ApplicationRecord
has_and_belongs_to_many :users
has_many :home_games, class_name: 'Game', foreign_key: 'home_team_id'
has_many :away_games, class_name: 'Game', foreign_key: 'away_team_id'
has_many :wins, class_name: 'Game', foreign_key: 'winner_id'
belongs_to :owner, class_name: 'User'
end
class User < ApplicationRecord
has_and_belongs_to_many :teams
has_many :teams_owned, class_name: 'Team', foreign_key: 'owner_id'
has_many :games, through: :teams
end
class Game < ApplicationRecord
belongs_to :home_team, class_name: "Team"
belongs_to :away_team, class_name: "Team"
belongs_to :winner, class_name: "Team", optional: true
end
I want to add an association between users and games. So I can call User.games and Game.users.
I tried adding this:
#in user model
has_many :games, through: :teams
#in team model
has_many :games, ->(team) { where('home_team_id = ? or away_team_id = ?', team.id, team.id) }, class_name: 'Game'
As the api docs said to do. But, when I try to call this association, I get an error that "game.team_id does not exist". Since each game has a home_team_id and away_team_id, but no team_id.
Did I just implement this extremely poorly? Or am I missing something? Any help appreciated.
I would say this isn't a really good solution.
In ActiveRecord you can't actually define associations where the foreign key can potentially be in two different columns like this:
has_many :games, ->(team) { where('home_team_id = ? or away_team_id = ?', team.id, team.id) }, class_name: 'Game'
It definitely won't work as Rails will still join the assocation as JOIN games ON games.team_id = teams.id. Just adding a WHERE clause to the query won't fix that. Since ActiveRecord actually creates a variety of different queries there is no option to simply provide a different join.
A kludge to make this work would be to add an instance method:
class Game < ApplicationRecord
def users
User.joins(:teams)
.where(teams: { id: home_team.id })
.or(Team.where(id: away_team.id))
end
end
As its not an actual association you cant join through it or use an sort of eager loading to avoid n+1 queries.
If you actually want to create a single association that you can join through you would need to add a join table between games and teams.
class Team < ApplicationRecord
# ...
has_many :game_teams
has_many :games, through: :game_teams
end
# rails g model game_team team:belongs_to game:belongs_to score:integer
class GameTeam < ApplicationRecord
belongs_to :team
belongs_to :game
end
class Game < ApplicationRecord
has_many :game_teams
has_many :teams, through: :game_teams
has_many :users, through: :teams
end
This is a better idea since it gives you a logical place to record the score per team.
As an aside if the composition of teams can change and accurate record keeping is important you might actually need additional join tables as the lineup when a game is played may not actually match the current lineup.
I searched for quite a long time and couldnt find that problem.
user.erb
has_many :workouts
has_many :result_units
workout.erb
belongs_to :user
has_many :sets
set.erb
belongs_to :workout
has_one :result_unit
result_unit.erb
belongs_to :user
belongs_to :set
1 possible Solution is that ResultUnit dont belong to User. But the question is then how much performance it will cost to query User.workouts.all.sets.all.resultunits.all
How could i create a new ResultUnit for User and Set?
This is a case for using a has_many :through association.
http://guides.rubyonrails.org/association_basics.html#the-has-many-through-association
Running User.workouts.all.sets.all.resultunits.all will result in numerous queries being executed. A has_many :through however, will execute only a single query and allow the database to optimize the joins between tables.
class User < ActiveRecord::Base
has_many :workouts
has_many :sets, through: :workouts
has_many :result_units, through: :sets
end
Ok, I didn't understand your problem 100%.. but I'm gonna have a stab at it and feel free to downvote if it's not right.
class User
has_many :workouts
has_many :result_units
has_many :sets, through: :workouts
# User.first.workouts
# User.first.result_units
# User.first.sets
end
class Workout
belongs_to :user
has_many :sets
# Workout.first.user
# Workout.first.sets
end
class ResultUnit
belongs_to :user
belongs_to :set
# ResultUnit.first.user
# ResultUnit.first.set
end
class Sets
belongs_to :workout
has_one :result_unit
# Set.first.workout
# Set.first.result_unit
end
I am creating an app that tracks a users employments and where they are in the companies. I need some help trying to route the app, I has made scaffolds of user, company, and department.
user
company (user has_many :through => employments)
department
user.rb
class User < ActiveRecord::Base
#associations
has_many :employments
has_many :companies, :through => :employments
has_one :department, :through => :employments
end
employment.rb
class Employment < ActiveRecord::Base
belongs_to :user
belongs_to :company
belongs_to :department
has_many :employment_histories
end
employment_history.rb
class EmploymentHistory < ActiveRecord::Base
belongs_to :employment
end
company.rb
class Company < ActiveRecord::Base
has_many :employments
has_many :users, :through => :employments
has_many :departments
end
department.rb
class Department < ActiveRecord::Base
belongs_to :company
end
For routing, the simplest way to do it is to declare your resources as resources, and then you access them through the id of the object.
http://guides.rubyonrails.org/routing.html#resource-routing-the-rails-default
Then to find through associations, I suggest you check out this railscast: http://railscasts.com/episodes/3-find-through-association?view=asciicast
If routing and finding through associations doesn't come easily, you will probably want to refactor your hierarchy to make more sense metaphorically. For instance my approach would be:
User has_many :employments and give it default_scope order: 'employments.created_at DESC' in the User model to make sure the most recent employment is first.
Employment has_one :department
Department belongs_to :company
Company has_many :departments
Then you can access all of this data through associations (see link above). You only need to route the resources, and then know how to access them in the right controllers.
I've started learning Rails recently and have completed the sample app at http://ruby.railstutorial.org. I am trying to add user-created groups to the sample social network. I've gotten stuck at how exactly I should model the group. I know there should be a user_id of who owns the group and there should be strings of the name and description. Aside from that I have no idea what the rest of the group data model should look like. Thank you in advance.
If you want a user to be in multiple groups your relations should be something like this
class Group < ActiveRecord::Base
belongs_to :owner, :class_name => "User"
has_many :memberships
has_many :members, :through => :memberships
end
class Membership < ActiveRecord::Base
belongs_to :member, :class_name => "User"
belongs_to :group
end
class User < ActiveRecord::Base
has_many :memberships
has_many :groups, :through => :memberships
has_many :owned_groups, :class_name => "Group", :foreign_key => "owner_id"
end
What about something like this?
class Group < ActiveRecord::Base
has_many :users
end
class User < ActiveRecord::Base
belongs_to :group
end