Rails - 创建用户组的控制器问题
Rails - Issue with controllers for creating a user group
我目前有一个 Rails 应用程序,它允许用户创建群组并允许其他用户加入群组。组 "creator" 是组的所有者,任何根据请求加入的都是成员。我希望用户只能创建一个组,但属于多个组(我认为我已经捕捉到了这种关系,但我有点不确定)。我需要一些帮助来了解我需要做什么才能在用户页面上显示组关联。我应该如何创建群组 "show" 页面以及如何在用户 "show" 页面上显示群组成员资格?我得到了 SO 的帮助,并遵循了 Railscast on self-referential association 来帮助指导我建立关系。
在此示例中,组称为 Cliq,成员资格由 has_many :through 控制。我为用户模型使用了 Devise。
澄清我的问题:我是否捕捉到我试图建立的关系?我将如何允许用户查看他们所属的组?
顺便说一句,我不确定群组创建者是否被关联为群组成员。我如何在 model/controller?
中表示它
这是我的代码:
组模型:
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 %>
我的迁移:
Cliq:
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
Cliq 会员:
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
以下有效的完整解决方案。
按照我上面的评论,在我看来最好的办法是在桥 table.
中实现某种 role
属性
You should use has_many :through if you need validations, callbacks, or extra attributes on the join model.
所以你可以在你的模型中尝试这个:
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
所以桥 table 存储 role
,它将是一个枚举或一个字符串,代表 'member'、'owner',也许还有 'admin' 或其他东西。
一些示例用法:
# 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 解释得很好。
尝试以下操作:
您修改后的模型。修复了以下问题:
在 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
的所有者。这将在下一个注释中解决。
由于 user
是 cliq
的所有者,因此使用 build_owned_group
构建了 cliq
,这会自动将 current_user
设置为所有者。
尽量不要在同一条语句中做多件事。就像将它分配给一个变量以及对新分配的变量进行一些操作一样。例如:在 CliqMembershipsController
的 create
操作中,您正在分配 @cliq_membership
并在其上调用 save
。将这两者分成两步。
在 CliqMembershipsController
的 destroy
中,无需加载 @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
所以我从上面问题中的代码开始,然后深入研究我的答案(通过许多额外的试验)。这可能会在将来帮助某人,所以这是有效的。 (从两个答案中获取建议):
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
非常感谢您对此线程提供的所有不可思议的帮助!
我目前有一个 Rails 应用程序,它允许用户创建群组并允许其他用户加入群组。组 "creator" 是组的所有者,任何根据请求加入的都是成员。我希望用户只能创建一个组,但属于多个组(我认为我已经捕捉到了这种关系,但我有点不确定)。我需要一些帮助来了解我需要做什么才能在用户页面上显示组关联。我应该如何创建群组 "show" 页面以及如何在用户 "show" 页面上显示群组成员资格?我得到了 SO 的帮助,并遵循了 Railscast on self-referential association 来帮助指导我建立关系。
在此示例中,组称为 Cliq,成员资格由 has_many :through 控制。我为用户模型使用了 Devise。
澄清我的问题:我是否捕捉到我试图建立的关系?我将如何允许用户查看他们所属的组?
顺便说一句,我不确定群组创建者是否被关联为群组成员。我如何在 model/controller?
中表示它这是我的代码:
组模型:
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 %>
我的迁移:
Cliq:
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
Cliq 会员:
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
以下有效的完整解决方案。
按照我上面的评论,在我看来最好的办法是在桥 table.
中实现某种role
属性
You should use has_many :through if you need validations, callbacks, or extra attributes on the join model.
所以你可以在你的模型中尝试这个:
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
所以桥 table 存储 role
,它将是一个枚举或一个字符串,代表 'member'、'owner',也许还有 'admin' 或其他东西。
一些示例用法:
# 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 解释得很好。
尝试以下操作:
您修改后的模型。修复了以下问题:
在 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
的所有者。这将在下一个注释中解决。
由于 user
是 cliq
的所有者,因此使用 build_owned_group
构建了 cliq
,这会自动将 current_user
设置为所有者。
尽量不要在同一条语句中做多件事。就像将它分配给一个变量以及对新分配的变量进行一些操作一样。例如:在 CliqMembershipsController
的 create
操作中,您正在分配 @cliq_membership
并在其上调用 save
。将这两者分成两步。
在 CliqMembershipsController
的 destroy
中,无需加载 @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
所以我从上面问题中的代码开始,然后深入研究我的答案(通过许多额外的试验)。这可能会在将来帮助某人,所以这是有效的。 (从两个答案中获取建议):
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
非常感谢您对此线程提供的所有不可思议的帮助!