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
)。是否有一种惯用的方法来获取这些记录?如果没有,是否有一种干净的方式来获取这些记录?
感谢您的帮助。
答案 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
创建角色的方法可以是add a callback到Photo
。但正如您已经推测的那样,从模型中通过会话访问用户并不是一个好方法。
相反,您可以在实例化时将用户传递给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