首页 > 解决方案 > 如何在rails 6中仅更新current_user的嵌套表单属性?

问题描述

我的 rails 6 应用程序中有一个“用户”、“任务”和“评论”模型。用户模型是使用用于身份验证的设计来定义的。用户 has_many 评论和 has_many 任务和任务 has_many 评论。我有一个“任务”的嵌套表单,它接受_嵌套属性_用于“评论”。像这样的东西:

<%= form_with model: @task do |f| %>
  <%= f.text_field(:name) %>
  <br><br>
  
  <%= form.fields_for :comments do |c| %>
    <%= render('comment_fields', f: c) %>
  <% end %>
  <%= link_to_add_association('Add comment', f, :comments) %>
  <%= f.submit %>
<% end %>

'_comment_fields.html.erb' 文件如下所示:

<div class="nested-fields">
  <%= f.text_area(:body) %>
  <%= link_to_remove_association('Remove comment', f) %>
</div>

以上两种形式只是原始代码的最小版本,我在这里使用它们仅供参考。现在,假设一个名为“user1”的用户使用数据库中的任务表单添加了一个名为“task1”的任务,并且还使用嵌套表单添加了一些评论。我想要的是,如果其他名为“user2”的用户尝试编辑任务“task1”,那么他/她应该能够使用此表单仅添加和编辑他的评论,并且只能编辑任务名称,而“user2”不应该能够编辑或删除其他人的评论,但只有他的。我们将如何为嵌套表单执行此操作。

我们可以以某种正常的形式拥有此功能,例如:

<% if @task.user.id == current_user.id %>
  <%= f.text_field(:name) %>
<% end %>

上面的代码只会在当前模型的 user_id 与当前登录用户的 id 匹配时显示 text_field,但我不知道如何在 f.fields_for 中执行此操作,因为我们在嵌套形式中没有像 @task 这样的变量。

标签: ruby-on-railsdevisenested-forms

解决方案


您可以通过属性获取由表单构建器实例包装的对象object

<div class="nested-fields">
  <%= f.text_area(:body) %>
  <% if f.object.user == current_user %>
    <%= link_to_remove_association('Remove comment', f) %>
  <% end %>
</div>

但是,我不会真正鼓励您重新发明授权轮。PunditCanCanCan gem 是最流行的选择,可以避免在您的视图和控制器中传播和复制身份验证逻辑。

使用 Pundit,您可以:

class CommentPolicy < ApplicationPolicy
  def destroy?
    record.user == user
  end
end
<div class="nested-fields">
  <%= f.text_area(:body) %>
  <% if policy(f.object).destroy? %>
    <%= link_to_remove_association('Remove comment', f) %>
  <% end %>
</div>

CanCanCan 的等价物是:

class Ability
  include CanCan::Ability

  def initialize(user)
    can :destroy, Comment, user: user
  end
end
<div class="nested-fields">
  <%= f.text_area(:body) %>
  <% if can?(:destroy, f.object) %>
    <%= link_to_remove_association('Remove comment', f) %>
  <% end %>
</div>

推荐阅读