I'm trying to make an app for students to post projects. When they create a post, they can add their team mates. Each student has a user id, and I want the student creating the project to be able to select other ids from the same organisation as their team mates. The model associations are:
user
has_and belongs_to_many :projects
project
has_and belongs_to_many :users
I have a project model, with:
:user_id (integer)
:team_mates (integer)
In my projects form, I want the student (creating the project, to select other ids (from a list of students who belong to the same organisation) as team mates. My first question is whether the team mates attribute should be an integer (since there might be more than one team mate, in which case, can this attribute hold an array?
My next problem is - I'm lost for how to go about this. If I add a select line to my project form, to add user_ids, where the user.organisation equals the current user's id, then the student creating the project should be able to see a list of possible options.
Then in my projects show page, I want to display each student in the team.
Can anyone help with how to approach this? I'm lost and stuck for where to find examples of similar problems.
UPDATE
I found this article: http://collectiveidea.com/blog/archives/2015/07/30/bi-directional-and-self-referential-associations-in-rails/
I'm confused though. I don't know whether I should join projects with users (through a join table I've called teams) or whether I should join users with users, through a join table called 'teams'.
If I join users with projects, it makes sense to me that the user who creates a project can choose other users to be project team mates. However, it isn't true to say that each project has many teams (which is what this example shows). I'm not sure about changing the has_many to a has_one, since the article goes on to explain about the has_many through join.
If i join users to users, then a user with many projects may have different teams for each project. So that wouldn't be correct.
Taking the article as an example, I tried:
create teams model:
class CreateTeams < ActiveRecord::Migration
def change
create_table :teams do |t|
t.references :project, index: true, foreign_key: true
t.references :team_mate, index: true
t.timestamps null: false
end
add_foreign_key :teams, :projects, column: :team_mate_id
add_index :teams, [:project_id, :team_mate_id], unique: true
end
end
Team.rb
belongs_to :project
belongs_to :team_mate, class_name: "Profile"
Teams controller (I will figure this out later - its commented for now since I don't have a matchmaker section yet):
class TeamsController < ApplicationController
before_action :resync_matches, only: :index
def index
# several orders of magnitude faster
@team_mates = current_user.team_mates
.page(params[:page])
end
private
def resync_matches
# only resync if we have to
if current_user.teams_outdated?
new_matches = MatchMaker.matches_for(current_user)
current_user.team_mates.replace(new_matches)
end
end
end
project.rb
has_one :team
has_many :team_mates, through: :teams, dependent: :destroy
I changed this so that projects have one team rather than many.
Im confused about this and not sure how to get this up and running. In my projects form, I want to offer users (who create projects) to pick profiles of other users who are team mates. I'm lost at this point.
I tried to make a Teams Helper:
module TeamsHelper
def team_mate_options
s = ''
Profile.in_same_organisation.each do |profile|
s << "<option value='#{profile.id}'>#{profile.user.full_name}</option>"
end
s.html_safe
end
end
In my profile.rb, I tried to make a scope to get the profiles who belong to the same organisation as the project creator (although I'm not sure this is correct):
scope :in_same_organisation, -> (organisation_id) { where(organisation_id: organisation_id) }
Then in my projects form I tried to add a select option:
<div class="form-group">
<%= label_tag 'team_mates', 'Choose team mates' %>
<%= select_tag 'team_mates', team_mate_options, multiple: true, class: 'form-control chosen-it' %>
</div>
VISHAL'S SUGGESTION
Taking Vishal's suggestion, I have implemented the structure proposed. I'm having a problem with the projects form. My complete setup is:
Models Organisation
has_many :profiles
Profile
has_many :projects
belongs_to :organisation
has_many :teams, foreign_key: "team_mate_id"
has_many :team_projects, through: :teams, source: :project
Project
belongs_to :profile
has_many :teams
has_many :team_mates, through: :teams
Team
belongs_to :project
belongs_to :team_mate, class_name: "Profile"
My teams table has:
create_table "teams", force: :cascade do |t|
t.integer "project_id"
t.integer "team_mate_id"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
Then in my project form, I have:
<%= f.label :team_mates, :label => "Add a team member" %>
<%= f.collection_select(:team_mate_id, Profile.all, :id, :team_mate_select, {prompt: "Select the team member"}, {:required => true}) %>
In my profile model, I have:
def team_mate_select
self.user.formal_name
end
My structure is that profiles belong to users. In user, I have method called formal name which adds a title to the users name.
When I save this and try it, I get an error that says:
undefined method `team_mate_id' for #<Project:0x007fa08ed3d8e0>
(highlighting the collection select line of the project form)
My projects/form.html.erb has:
<%= simple_form_for(@project) do |f| %>
<%= f.error_notification %>
<div class="form-inputs">
<%= f.input :title, :label => "Title", autofocus: true %>
<%= f.input :description, :as => :text, :label => "Describe your project", :input_html => {:rows => 10} %>
<%= f.input :remark, :as => :text, :label => "Is there an interesting fact or statistic that's relevant to this research?", :input_html => {:rows => 5}, :placeholder => "In fact, ...(insert a fact which shows why this research might be interesting or relevant)" %>
<%= f.input :hero_image, :label => "Add an image" %>
<%= f.label :team_mates, :label => "Add a team member" %>
<%= f.collection_select(:team_id, Profile.all, :id, :team_mate_select, {prompt: "Select the team member"}, {:required => true}) %>
<div class="form-actions" style="margin-top:50px">
<%= f.button :submit, "Create", :class => 'formsubmit' %>
</div>
<% end %>
Introduce a Team
model, with has_and belongs_to_many
relations between a User
model and a team
model. Then, a Team
represents an array of User
objects.
What you are looking for is self-referential associations . You would have to create a join table that keeps the records of which user is team member of which user; something like following:
+-----------+----------------+------------+-------------+
| user_id | team_member_id | created_at | updated_at |
+-----------|----------------+------------+-------------+
| | | | |
+-----------+----------------+------------+-------------+
You can think on the lines of 'what resource you are creating'. When you add a new project, you are creating a Project resource. When the project's creator adds someone else to the project, you are creating a ProjectMemberRelationship resource. So, I think you can get what you need with only these models - Organisation, User, Project, ProjectMemberRelationship (and a table for each of them).
You will need a projects table that has a reference to users through user_id field.
Create ProjectMemberRelationship model with
rails generate model ProjectMemberRelationship project_id:integer member_id:integer
Organisation Model:
has_many :users
User model:
belongs_to :organization
has_many :projects
has_many :project_member_relationships, foreign_key: "member_id"
has_many: collaborated_projects, through: :project_member_relationships, source: :project
You need foreign_key: "member_id"
as the column name is not user_id as expected by Rails by default. And you need source: :project
because the column name is not collaborated_project_id but project_id.
Project model:
belongs_to :user
has_many :project_member_relationships
has_many :members, through: :project_member_relationships
ProjectMemberRelationship model:
belongs_to :project
belongs_to :member, class_name: "User"
With models defined as above, every time a member is added to a project, a new row is added to project_member_relationships table that stores the project_id for the project and member_id of the user that was added, while users & projects tables are unaffected by this action.
Let's divide the task in 2 stops.
Step 1: A user creates a project.
Step 2: The user adds collaborators to the project.
Step 1 is fairly easy and straightforward. You create a project with, say its topic and user_id of the user who created it.
Now on the project's show page, you can have a form to add a new project_member_relationship. You only need 2 fields to submit here - member_id (for the user to be added) & project_id (which equals params[:id] on projects#show page). For member_id field, you can use collection_select where your collection is the users that belong to the same organisation as the creator, as required. Mind you, one submission of this form creates only one project member at a time.
I haven't tested the code yet but I'll be happy to help if you run into issues.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.