更新嵌套表单时,如何使用新记录执行某些操作?

时间:2015-05-27 00:30:12

标签: ruby-on-rails ruby nested-forms rolify

class Photo < ActiveRecord::Base 
  has_many :item_photos
  has_many :items, through: :item_photos
end 

class Item < ActiveRecord::Base 
  has_many :item_photos
  has_many :photos, through: :item_photos
  accepts_nested_attributes_for :photos
end 

class ItemPhotos < ActiveRecord::Base
  belongs_to :photo
  belongs_to :item
end 

当我编辑或创建项目时,我还会上传或删除照片。但是,多个用户可以查看项目。这些用户应该只能查看自己的照片。

项目#1有三张照片。艾米可以访问一个。巴里可以使用两个。 Barry加载/items/1并对其进行编辑。他删除了一张Photo,忽略了另一张,并添加了两张新照片。

class ItemsController < ApplicationController
  def update
    if @item.update(item_params)
      # Give Barry access to the Photo he just made. 
      @item.only_the_photos_barry_just_made.each do |p|
        current_user.add_role :viewer, p
      end 
    end 
  end 
end 

我不想使用访问会话信息的方法污染models/photo.rb(例如current_user)。是否有一种惯用的方法来获取这些记录?如果没有,是否有一种干净的方式来获取这些记录?

感谢您的帮助。

1 个答案:

答案 0 :(得分:2)

一个简单的解决方案是添加:照片的创建者关系。

rails g migration AddCreatorToPhotos creator:references

class Photo < ActiveRecord::Base
  belongs_to :creator, class: User
  # ...
end

然后您只需在Abilty课程中添加规则:

class Ability
  include CanCan::Ability
  # Define abilities for the passed in user here.
  # @see https://github.com/bryanrite/cancancan/wiki/Defining-Abilities
  def initialize(user = nil)
    user ||= User.new # guest user (not logged in)
    # ...
    can :read, Photo do |photo|
      photo.creator.id == user.id
    end
  end
end

然后,您可以通过以下方式获取当前用户可以阅读的照片:

@photos = @item.photos.accessible_by(current_ability)

编辑:

如果您想通过角色进行授权,则只需更改授权规则中的条件:

class Ability
  include CanCan::Ability
  # Define abilities for the passed in user here.
  # @see https://github.com/bryanrite/cancancan/wiki/Defining-Abilities
  def initialize(user = nil)
    user ||= User.new # guest user (not logged in)
    # ...
    can :read, Photo do |photo|
      user.has_role? :viewer, photo
    end
  end
end

编辑2:

创建角色的方法可以是add a callbackPhoto。但正如您已经推测的那样,从模型中通过会话访问用户并不是一个好方法。

相反,您可以在实例化时将用户传递给Photo。您可以设置belongs_to :creator, class: User关系或创建虚拟属性

class Photo < ActiveRecord::Base
  attr_accessor: :creator_id
end

然后,您可以通过隐藏字段传递用户(请记住将其列入白名单!):

# GET /items/new
def new 
  @item = Item.new
  @item.photos.build(creator: current_user) # or creator_id: current_user.id
end

<%= fields_for(:photos) do %>
  <%= f.hidden_field :creator_id %>
<% end %>

那么,我们如何创建回调?

class Photo < ActiveRecord::Base
  attr_accessor: :creator_id

  after_commit :add_viewer_role_to_creator!, on: :create

  def add_viewer_role_to_creator!
    creator.add_role(:viewer, self)
    true # We must return true or we break the callback chain
  end
end

但有一个问题:

我们不希望恶意用户在更新时将其ID分配给现有照片。

我们可以通过设置一些自定义参数白名单来完成此操作:

def item_params
  whitelist = params.require(:item).permit(:foo, photos_attributes: [:a,:b, :creator_id]
  current_user_photos = whitelist[:photos_attributes].select do |attrs|
   attrs[:id].nil? && attrs[:creator_id] == current_user.id
  end
  whitelist.merge(photos_attributes: current_user_photos)
end
相关问题