【问题标题】:Rails - Issue with controllers for creating a user groupRails - 用于创建用户组的控制器问题
【发布时间】:2016-06-26 02:10:51
【问题描述】:

我目前有一个 Rails 应用程序,它允许用户创建组并允许其他用户加入组。组“创建者”是该组的所有者,任何加入 ON REQUEST 的都是成员。我希望用户只能创建一个组,但属于多个组(我认为我已经掌握了这种关系,但我有点不确定)。我需要一点帮助来理解我需要做什么才能在用户页面上显示组关联。我应该如何创建组“显示”页面以及如何在用户“显示”页面上显示组成员身份?我得到了 SO 的帮助,并按照 Railscast on self-referential association 来帮助指导我建立关系。

在此示例中,组称为 Cliqs,成员资格由 has_many :through 控制。我为用户模型使用了设计。

澄清我的问题:我是否正在捕捉我正在尝试建立的关系?我将如何允许用户查看他们所属的组?

顺便说一句,我不确定组创建者是否与组成员相关联。我如何在我的模型/控制器中表示它?

这是我的代码:

组模型:

class Cliq < ActiveRecord::Base
 belongs_to :owner, class_name: 'User'

 has_many :members, through: :cliq_memberships, source: :user
 has_many :cliq_memberships
end

会员模式:

class CliqMembership < ActiveRecord::Base
 belongs_to :cliq
 belongs_to :user
end

用户模型:

class User < ActiveRecord::Base
 has_one :owned_group, foreign_key: 'owner_id', class_name: 'Group'

 has_many :cliqs, through: :cliq_memberships
 has_many :cliq_memberships
.
.
.
end

组控制器:

    class CliqsController < ApplicationController

    def show
        @cliq = Cliq.find(params[:id])
    end

    def new
        @cliq = Cliq.new(params[:id])
    end

    def create
        @cliq = Cliq.create(cliq_params)
        if @cliq.save
            redirect_to current_user
        else
            redirect_to new_cliq_path
        end
    end

    def destroy
    end

    def cliq_params
        params.require(:cliq).permit(:name, :cliq_id)
    end
end

组成员控制:

class CliqMembershipsController < ApplicationController

    def create
        @cliq = cliq.find(params[:cliq_id])
        if @cliq_membership.save = current_user.cliq_memberships.build(:cliq_id => params[:cliq_id])
            flash[:notice] = "Joined #{@cliq.name}"
        else
            #Set up multiple error message handler for rejections/already a member
            flash[:notice] = "Not able to join Cliq."
        end
        redirect_to cliq_url
    end

    def destroy
        @cliq = Cliq.find(params[:id])
        @cliq_memberships = current_user.cliq_memberships.find(params[cliq_memberships: :cliq_id]).destroy
        redirect_to user_path(current_user)
    end
end

还有我的用户显示页面:

    <h1> <%= @user.username %> </h1>

<h2>Cliqs</h2>

<%= link_to "Create Cliq", new_cliq_path %>

<ul>
  <% for cliq_membership in @user.cliq_memberships %>
    <li>
      <%= cliq_membership.cliq.name %>
      (<%= link_to "Leave Cliq", cliq_membership, :method => :delete %>)
    </li>
  <% end %>
</ul>

<h3>Title:</h3>
<% @uploads.each do |upload| %>
    <div>
        <%= link_to upload.title, upload_url %>
    </div>
<% end %>

还有我的迁移:

点击:

class CreateCliqs < ActiveRecord::Migration
  def change
    create_table :cliqs do |t|

      t.string :name
      t.references :owner
      t.integer :cliq_id

      t.timestamps null: false
    end
  end
end

CliqMemberships:

    class CreateCliqMemberships < ActiveRecord::Migration
  def change
    create_table :cliq_memberships do |t|
        t.references :user
        t.references :cliq

      t.timestamps null: false
    end
  end
end

以下工作的完整解决方案。

【问题讨论】:

  • 所以用户和 Cliqs 之间有一个桥接表。对于我过去从事的项目,桥接表中有一个role 列,指示用户是管理员、用户还是组的所有者。这种方法在这里行得通吗?
  • 我将如何实现它?我仍然对 HMT 协会感到有些困惑。
  • HMT?你是说多对多吧?许多用户可以属于许多组,对吗?唯一的限制是用户只能拥有一个组,对吧?
  • 就是这样。

标签: mysql ruby-on-rails activerecord has-many-through


【解决方案1】:

尝试以下方法:

您修改后的模型。修复了以下问题:

User 模型中,对于has_one :owned_group,您将class_name 设置为Group 而不是Cliq

has_many :through 之前声明has_many。否则它可能会起作用,但它是一种很好的做法并且易于阅读。

class User < ActiveRecord::Base
  has_one :owned_group, foreign_key: 'owner_id', class_name: 'Cliq'
  has_many :cliq_memberships
  has_many :cliqs, through: :cliq_memberships
end

class CliqMembership < ActiveRecord::Base
  belongs_to :cliq
  belongs_to :user
end

class Cliq < ActiveRecord::Base
  belongs_to :owner, class_name: 'User'
  has_many :cliq_memberships
  has_many :members, through: :cliq_memberships, source: :user
end

您修改后的控制器。修复了以下问题:

CliqsController 中,由于它与Cliq 相关,因此您在创建它时不会得到cliq_id。所以从cliq_params 中删除了cliq_id。您可以在其中添加其他 cliq 相关属性。

create 中,您忘记将current_user 指定为cliq 的所有者。这将在下一个注释中解决。

由于usercliq 的所有者,因此使用build_owned_group 构建cliq,它会自动将current_user 设置为所有者。

尽量不要在同一个语句中做多件事。就像将它分配给一个变量以及对新分配的变量进行一些操作一样。例如:在CliqMembershipsControllercreate 操作中,您分配了@cliq_membership 并在其上调用save。将这两个分为两个步骤。

CliqMembershipsControllerdestroy 中,无需加载@cliq,并且还修复了您查找@cliq_membership 的方式。

class CliqsController < ApplicationController

    def show
        @cliq = Cliq.find(params[:id])
    end

    def new
        @cliq = Cliq.new(params[:id])
    end

    def create
        @cliq = current_user.build_owned_group(cliq_params)

        if @cliq.save
            redirect_to current_user
        else
            redirect_to new_cliq_path
        end
    end

    private
    def cliq_params
        params.require(:cliq).permit(:name)
    end
end

class CliqMembershipsController < ApplicationController

    def create
        @cliq = Cliq.find(params[:cliq_id])
        @cliq_membership = current_user.cliq_memberships.build(cliq: @cliq)

        if @cliq_membership.save
        flash[:notice] = "Joined #{@cliq.name}"
        else
        #Set up multiple error message handler for rejections/already a member
        flash[:notice] = "Not able to join Cliq."
            redirect_to cliq_url
        end

    def destroy
        @cliq_membership = current_user.cliq_memberships.find(params[:id])

        if @cliq_membership.destroy
            redirect_to user_path(current_user)
        end
    end 
end

最后是你修改后的观点:

修复了一些问题。

尝试在集合上使用each 进行迭代。这更像是ruby 方式,而不是for 循环。

根据您的CliqMemberhipsController 代码,我假设您正在使用如下嵌套资源。所以修复了link_to 使用cliq_cliq_memberhip_path 而不是cliq_membership_path

<h1><%= @user.username %></h1>

<h2>Cliqs</h2>

<%= link_to "Create Cliq", new_cliq_path %>

<ul>
    <% @user.cliq_memberships.each do |cliq_membership| %>
        <li><%= cliq_membership.cliq.name %>(<%= link_to "Leave Cliq", cliq_cliq_membership_path([cliq, cliq_membership]), method: :delete %>)</li>
    <% end %>
</ul>

这假设您有一个包含以下内容的路由文件:

resources :cliqs do
    resources :cliq_memberships
end 

【讨论】:

  • 我非常喜欢这个!但我得到一个“未知属性:Cliq 的‘Owner_ID’”。
  • 这里是迁移: 对于 Cliq:def change create_table :cliqs do |t| t.string :name t.references :owner t.integer :cliq_id t.timestamps null: false
  • 对于 Cliq_Membership:def change create_table :cliq_memberships do |t| t.references :user t.references :cliq t.timestamps null: false
  • 这里发生了什么?
  • 请将所有迁移(格式化)添加到您的问题中。还提供堆栈跟踪的错误以查看问题发生的位置。
【解决方案2】:

按照我上面的评论,在我看来最好的办法是在桥表中实现某种role 属性。

The Rails docs say this:

如果您需要验证、回调或连接模型上的额外属性,您应该使用 has_many :through。

所以你可以在你的模型中尝试这个:

class Cliq < ActiveRecord::Base
  has_many :cliq_memberships
  has_many :members, through: :cliq_memberships

  def owner
    cliq_memberships.where(role: 'owner').user
  end
end

# this model is used to access attributes on the bridge table
class CliqMembership < ActiveRecord::Base
  belongs_to :cliq
  belongs_to :user

  attr_accessor :role
end

class User < ActiveRecord::Base
  has_many :cliq_memberships
  has_many :cliqs, through: :cliq_memberships

  # something like this would make it easy to grab the owned cliq
  def ownedCliq
    cliq_memberships.where(role: 'owner').cliq
  end
end

所以桥接表存储role,它可以是一个枚举或字符串,代表“成员”、“所有者”,也可能是“管理员”或其他内容。

一些示例用法:

# say I have a user
u = User.find(1)
# and I want the cliq that he/she owns
owned_cliq = u.ownedCliq

# maybe I have a group:
g = Cliq.find(1)
# and I want the user that owns it:
my_owner = g.owner
# now let's get all the members of the cliq (including the 'owner')
my_members = g.members

更多示例用法:

# inside the controller...

# say I have a user:
u = User.find(1)

# this user is trying to create a cliq
# pretend we fill it in with its data here...
c = Cliq.new
c.save!

# we'll need to hook the two together:
cm = CliqMembership.new(role: 'owner', user_id: u.id, cliq_id: c.id)
cm.save!
# or we might try something like this:
#cm = CliqMembership.find_or_create_by #...

另外,我发现this SO answer 很好地解释了一些事情。

【讨论】:

  • 我喜欢它的外观,因为它可以很好地分隔事物。我将如何设置创建用户是所有者?我将如何让其他用户请求加入并在他们自己的页面上显示“群组归属”关系?
  • 我理解您处理它的方式。目前,经过一些测试,我了解到,使用我的代码,创建用户只能创建一个组(这很好)。当我将“”放入“用户/显示”页面时,所有显示的是“CliqMemberships”。看来,使用我的代码,正在建立正确的关联,但是在呈现我想要的东西时存在一些问题。似乎“创建用户”没有作为成员持续存在。我该如何做到这一点?
  • 实际上用户可以创建多个组。验证会解决这个问题吗?
【解决方案3】:

所以我从上面问题中的代码开始,然后向内工作(通过许多额外的试验)。这可能对将来的某人有所帮助,所以这就是有效的。 (从两个答案中获取建议):

class Cliq < ActiveRecord::Base
 belongs_to :owner, class_name: 'User'

 has_many :cliq_memberships
 has_many :members, through: :cliq_memberships, source: :user
end

class CliqMembership < ActiveRecord::Base
 belongs_to :cliq
 belongs_to :user
end

class User < ActiveRecord::Base
 has_one :owned_cliq, foreign_key: 'owner_id', class_name: 'Cliq'

 has_many :cliq_memberships
 has_many :cliqs, through: :cliq_memberships
 .
 .
 .
end

class CliqsController < ApplicationController

    def show
        @cliq = Cliq.find(params[:id])
    end

    def new
        @cliq = Cliq.new(params[:id])
    end

    def create
        @cliq = current_user.build_owned_cliq(cliq_params)
        @cliq.members << current_user

        if @cliq.save
            redirect_to current_user
        else
            redirect_to new_cliq_path
        end
    end

    def destroy
    end


    def cliq_params
        params.require(:cliq).permit(:name, :cliq_id)
    end
end

class UsersController < ApplicationController

  def show 
      #find way to use username instead of id (vanity url?)
      @user = User.find(params[:id])
      @uploads = Upload.all
      @cliq_memberships = CliqMembership.all
      @cliqs = Cliq.all
  end

end

 class CliqMembershipsController < ApplicationController

    def show
    end

    def create
        @cliq = Cliq.find(params[:cliq_id])

        @cliq_membership = current_user.cliq_memberships.build(cliq: @cliq)

        if @cliq_membership.save
            flash[:notice] = "Joined #{@cliq.name}"
        else
            #Set up multiple error message handler for rejections/already a member
        flash[:notice] = "Not able to join Cliq."
        end
        redirect_to cliq_url
    end

    def destroy
        @cliq_membership = current_user.cliq_membership.find(params[:id])

        if @cliq_membership.destroy
        redirect_to user_path(current_user)
    end
end

class CreateCliqs < ActiveRecord::Migration
  def change
    create_table :cliqs do |t|

      t.string :name
      t.references :owner

      t.timestamps null: false
    end
  end
end

class CreateCliqMemberships < ActiveRecord::Migration
  def change
  create_table :cliq_memberships do |t|
      t.references :user
      t.references :cliq

      t.timestamps null: false
    end
  end
end

非常感谢在此线程上提供的所有令人难以置信的帮助!

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-07-12
    • 2012-07-21
    • 2023-04-08
    相关资源
    最近更新 更多