向模型添加用户 ID 数组 - Rails 4

Posted

技术标签:

【中文标题】向模型添加用户 ID 数组 - Rails 4【英文标题】:Adding an array of user id's to a model - Rails 4 【发布时间】:2016-03-11 08:25:43 【问题描述】:

我正在尝试制作一个应用程序供学生发布项目。当他们创建帖子时,他们可以添加他们的队友。每个学生都有一个用户 ID,我希望创建项目的学生能够从与他们的队友相同的组织中选择其他 ID。模型关联是:

用户

has_and belongs_to_many :projects

项目

has_and belongs_to_many :users

我有一个项目模型,其中:

:user_id (integer)
:team_mates (integer)

在我的项目表单中,我希望学生(创建项目,从属于同一组织的学生列表中选择其他 ID)作为队友。我的第一个问题是队友属性是否应该是整数(因为可能有多个队友,这种情况下,这个属性可以保存一个数组吗?

我的下一个问题是 - 我不知道该怎么做。如果我在我的项目表单中添加一个选择行,以添加 user_ids,其中 user.organisation 等于当前用户的 id,那么创建项目的学生应该能够看到可能的选项列表。

然后在我的项目展示页面中,我想展示团队中的每个学生。

谁能帮助解决这个问题?我迷路了,不知道在哪里可以找到类似问题的示例。

更新

我发现这篇文章:http://collectiveidea.com/blog/archives/2015/07/30/bi-directional-and-self-referential-associations-in-rails/

虽然我很困惑。我不知道我是否应该与用户一起加入项目(通过我称为团队的加入表),或者我是否应该通过名为“团队”的加入表加入用户和用户。

如果我将用户加入项目,那么创建项目的用户可以选择其他用户作为项目团队成员对我来说是有意义的。然而,并不是说每个项目都有很多团队(这就是这个例子所展示的)。我不确定是否将 has_many 更改为 has_one,因为本文继续通过 join 解释 has_many。

如果我将用户加入用户,那么拥有许多项目的用户可能会为每个项目拥有不同的团队。所以这是不正确的。

以文章为例,我试过了:

创建团队模型:

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

团队.rb

belongs_to :project
belongs_to :team_mate, class_name: "Profile"

团队控制器(我稍后会解决这个问题 - 因为我还没有媒人部分,所以现在对其进行评论):

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

项目.rb

has_one :team
has_many :team_mates, through: :teams, dependent: :destroy

我对此进行了更改,以便项目拥有一个团队而不是多个团队。

我对此感到困惑,不知道如何启动和运行它。在我的项目表单中,我想让用户(创建项目的人)选择其他用户的个人资料,这些用户是队友。我现在迷路了。

我尝试制作团队助手:

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

在我的 profile.rb 中,我尝试创建一个范围来获取与项目创建者属于同一组织的个人资料(尽管我不确定这是否正确):

scope :in_same_organisation, -> (organisation_id)  where(organisation_id: organisation_id) 

然后在我的项目表单中,我尝试添加一个选择选项:

<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 的建议,我已经实施了建议的结构。我的项目表格有问题。我的完整设置是:

模型 组织

has_many :profiles

简介

  has_many :projects
  belongs_to :organisation
  has_many :teams, foreign_key: "team_mate_id"
  has_many :team_projects, through: :teams, source: :project

项目

belongs_to :profile
has_many :teams
has_many :team_mates, through: :teams

团队

belongs_to :project
belongs_to :team_mate, class_name: "Profile"

我的团队表有:

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

然后在我的项目表单中,我有:

<%= 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) %>

在我的个人资料模型中,我有:

def team_mate_select
    self.user.formal_name
end

我的结构是个人资料属于用户。在用户中,我有一个名为正式名称的方法,它为用户名添加了一个标题。

当我保存并尝试时,我收到一条错误消息:

undefined method `team_mate_id' for #<Project:0x007fa08ed3d8e0>

(突出项目表单的集合选择行)

我的项目/form.html.erb 有:

<%= 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 %>

【问题讨论】:

【参考方案1】:

引入Team 模型,在Usermodel 和team 模型之间具有has_and belongs_to_many 关系。然后,一个Team 代表一个User 对象的数组。

【讨论】:

不会是项目和团队之间吧?该模型的属性是什么?如何让它们在我的项目模型中发挥作用? 是的,另外projectteam 之间会有关系。如果每个project 恰好有一个team,那么您只需将team_id 列添加到project 模型,再加上belongs_to 关系。 userteam 之间的关系类似于您已经拥有的 userproject 之间的关系。啊哈,也许你的projects 在这方面表现得像teams?那么你根本不需要team 模型。 我一点也不明白。你的结论是团队模型不是解决方案吗【参考方案2】:

您正在寻找的是self-referential associations。您必须创建一个连接表来保存哪个用户是哪个用户的团队成员的记录;类似于以下内容:

+-----------+----------------+------------+-------------+
|  user_id  | team_member_id | created_at | updated_at  |
+-----------|----------------+------------+-------------+
|           |                |            |             |
+-----------+----------------+------------+-------------+

【讨论】:

所以你认为我需要一个用于团队成员的新表以及一个连接表,而不是项目模型上的一个属性(用于团队成员)? 是的,当然。因为一个用户可以有多个队友。 但是如何将它添加到项目中?不同的项目可以有不同的队友【参考方案3】:

您可以考虑“您正在创建什么资源”。添加新项目时,您正在创建项目资源。当项目的创建者将其他人添加到项目中时,您正在创建一个 ProjectMemberRelationship 资源。因此,我认为您只需使用这些模型即可获得所需的东西 - 组织、用户、项目、ProjectMemberRelationship(以及每个模型的表格)。

您将需要一个通过 user_id 字段引用用户的项目表。

使用

创建 ProjectMemberRelationship 模型
rails generate model ProjectMemberRelationship project_id:integer member_id:integer

组织模式:

has_many :users

用户模型:

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

您需要foreign_key: "member_id",因为默认情况下,列名不是 Rails 所期望的 user_id。你需要source: :project,因为列名不是collaborated_project_id,而是project_id。

项目模型:

belongs_to :user
has_many :project_member_relationships
has_many :members, through: :project_member_relationships

ProjectMember关系模型:

belongs_to :project
belongs_to :member, class_name: "User"

使用上述定义的模型,每次将成员添加到项目时,都会在 project_member_relationships 表中添加一个新行,其中存储项目的 project_id 和添加的用户的 member_id,而用户和项目表不受影响通过这个动作。

让我们把任务分成两站。

第 1 步:用户创建项目。

第 2 步:用户将协作者添加到项目中。

第 1 步相当简单明了。您创建一个项目,说出它的主题和创建它的用户的 user_id。

现在在项目的显示页面上,您可以有一个表单来添加新的 project_member_relationship。您只需在此处提交 2 个字段 - member_id(用于添加用户)和 project_id(等于项目#show 页面上的 params[:id])。对于 member_id 字段,您可以根据需要使用collection_select,其中您的集合是与创建者属于同一组织的用户。请注意,提交此表单一次只能创建一个项目成员。

我尚未测试代码,但如果您遇到问题,我很乐意为您提供帮助。

【讨论】:

嗨,维沙尔。非常感谢您的帮助。我明白你在这里的建议,我认为这是有道理的。 希望它对您有用并为您带来预期的结果。干杯! 抱歉 Vishal,评论计时器已用完。我看到您已经遵循了我最初链接到的关于双向关联的示例中的逻辑方法,其中被连接的两个对象通过连接链接模型和对象有很多这样的连接。我想知道这样设计关系是否有意义: - 一个项目拥有一个名为“团队”的模型实例 - 一个团队与许多配置文件有关系 - 每个项目只有一个团队,每个团队都有一个项目 所以这与上面的解决方案不同,其中每个项目都有许多 project_member_relationships 并且每个 project_member_relationships 是项目到配置文件的一对一映射。 我想知道以第一种方式处理它是否是某种编程/数据结构约定?如果是这样的话,我想如果对这些关系进行更复杂的查询,可能会有一些未来的好处。从技术上讲,您认为一种方式比另一种方式更可取吗?

以上是关于向模型添加用户 ID 数组 - Rails 4的主要内容,如果未能解决你的问题,请参考以下文章

Rails 控制台通过 id 数组查找用户

在 Rails 中向模型添加错误不起作用

Node.js Mongoose 如何保存新文档并向数组字段添加新值

向 Rails 模型添加额外字段以减少不必要的数据库查询

Rails:更新模型属性而不调用回调

Rails复杂模型关联,用户和团队之间的共享文档