如何只更新rails中current_user的嵌套表单属性 6?
How to update the nested form attributes of only the current_user in rails 6?
我的 rails 6 应用程序中有一个 'User'、'Task' 和 'Comment' 模型。用户模型是使用用于身份验证的设计定义的。用户 has_many 评论和 has_many 任务和任务 has_many 评论。
我有一个 'Task' 的嵌套形式,其中 accepts_nested_attributes_for 'Comment'。像这样:
<%= 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',那么 he/she 应该只能使用此表单添加和编辑他的评论,并且只能编辑任务名称'user2' 不能编辑或删除其他人的评论,只能编辑或删除他的评论。我们将如何为嵌套表单执行此操作。
我们可以用一些正常的形式来实现这个功能,比如:
<% if @task.user.id == current_user.id %>
<%= f.text_field(:name) %>
<% end %>
当当前模型的 user_id 与当前登录用户的 ID 匹配时,上面的代码只会显示 text_field 但我不知道如何在 f.fields_for 中执行此操作,因为我们没有像@task 这样的嵌套形式的变量。
可以通过object
属性得到form builder instance包裹的对象:
<div class="nested-fields">
<%= f.text_area(:body) %>
<% if f.object.user == current_user %>
<%= link_to_remove_association('Remove comment', f) %>
<% end %>
</div>
但是我真的不鼓励您重新发明 authorization wheel. The Pundit or CanCanCan 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>
我的 rails 6 应用程序中有一个 'User'、'Task' 和 'Comment' 模型。用户模型是使用用于身份验证的设计定义的。用户 has_many 评论和 has_many 任务和任务 has_many 评论。 我有一个 'Task' 的嵌套形式,其中 accepts_nested_attributes_for 'Comment'。像这样:
<%= 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',那么 he/she 应该只能使用此表单添加和编辑他的评论,并且只能编辑任务名称'user2' 不能编辑或删除其他人的评论,只能编辑或删除他的评论。我们将如何为嵌套表单执行此操作。
我们可以用一些正常的形式来实现这个功能,比如:
<% if @task.user.id == current_user.id %>
<%= f.text_field(:name) %>
<% end %>
当当前模型的 user_id 与当前登录用户的 ID 匹配时,上面的代码只会显示 text_field 但我不知道如何在 f.fields_for 中执行此操作,因为我们没有像@task 这样的嵌套形式的变量。
可以通过object
属性得到form builder instance包裹的对象:
<div class="nested-fields">
<%= f.text_area(:body) %>
<% if f.object.user == current_user %>
<%= link_to_remove_association('Remove comment', f) %>
<% end %>
</div>
但是我真的不鼓励您重新发明 authorization wheel. The Pundit or CanCanCan 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>