Rails has_many:through,null belongs_to,multiple belongs_to和belongs_to的删除?

时间:2012-12-05 06:20:59

标签: ruby-on-rails activerecord cancan

我想知道我是否在正确的轨道上,并且轨道是否允许这类事情。

UserRole中有一个Network。即“吉姆”是“学校”的“历史老师”。

Role同时具有位置(权力)和名称(标签)。 “Jim”是“历史老师”(标签),但拥有会员或管理员,主管或其他任何人(权力)的权力。

根据Role UserEvents可以查看Network中的所有User,无论他/她是否创建了它们。即如果“吉姆”是“校长”(管理员),“吉姆”可以看到“南希”的“休息计划”,但如果“吉姆”是“历史老师”(会员)则不能看到。“

EventRole内创建Network作为Event。即“吉姆”在“学校”创建“课程计划”作为“历史教师”。

Network永远与特定Role相关联,目前与Event相关联。

如果User替换User中的其他Role,新User可以访问Event,我希望Event保持不变}。即“汤姆”将“吉姆”替换为“历史教师”,并且可以修改“课程计划”,因为他是“历史教师”。 “吉姆”无法再访问“课程计划”。

但如果User没有附加Role,我还希望Role能够继续存在。即“汤姆”被解雇并且没有当前替换,管理员仍然可以看到“课程计划”。

最后,如果Event被删除,Network仍然存在,与Role User连接,而不是User

模型如下,我使用CanCan进行授权,这些是我的问题:

  1. 某个角色可以缺少User,还是需要创建一些通用的“无”Event或“所有人”RoleEvent可以Role错过Network吗? (可以belongs_to为null吗?)
  2. UserRoleEvents连接起来是好还是坏?有更好的方法吗?
  3. 如果Network根据他/她Role可以看到更多活动,那么他们在Networkclass User < ActiveRecord::Base has_many :roles has_many :networks, :through => :roles has_many :events, :through => :network # I would use CanCan to determine the authorization of # what network events they can see based on their role? end 中有多少class Network < ActiveRecord::Base has_many :roles has_many :users, :through => :roles has_many :events # it shouldn't have this through roles right? # because a role can be deleted end ?我正在考虑class Role < ActiveRecord::Base belongs_to :user #CAN THIS BE NULL? belongs_to :network end ,而Ability.rb会限制它。
  4. User.rb

    class Event < ActiveRecord::Base
      belongs_to :role      #Can this be null?
    
      belongs to :network
      # Does it belong to the network through the role,
      # or can it belong on its own, in case the role is deleted?
    
      # belongs_to :user, :through => :roles
      # Is this necessary if I am using CanCan
      # to determine if a User can reach the event?
    
    end
    

    Network.rb

    if user  
      user.roles.each do |role|
        can :manage, Event, :role_id => role.id
        if role.position == "admin" || role.position == "manager"
          can :manage, Event, :network_id => role.network_id
        elseif role.position == "supervisor"
          can :read, Event, :network_id => role.network_id
        end
      end
    end
    

    Role.rb

    {{1}}

    Event.rb

    {{1}}

    Ability.rb

    {{1}}

2 个答案:

答案 0 :(得分:3)

您的关联很好,您可以拥有空belongs_to个字段。默认情况下,Rails不会在数据库上创建外键约束。就CanCan设置而言,我会做类似的事情:

class Ability
  include CanCan::Ability

  def initialize(user)
    #Adding can rules do not override prior rules, but instead are logically or'ed.
    #See: https://github.com/ryanb/cancan/wiki/Ability-Precedence

    #Anything that you can pass to a hash of conditions in Active Record will work here.
    #The only exception is working with model ids. 
    #You can't pass in the model objects directly, you must pass in the ids.
    #can :manage, Project, :group => { :id => user.group_ids }
    #See https://github.com/ryanb/cancan/wiki/Defining-Abilities

    can :read, Event, network: {id: user.supervises_network_ids} 
    can :manage, Event, role: {id: user.role_ids}
    can :manage, Event, network: {id: user.manages_network_ids + user.admins_network_ids}
  end
end

你在哪里:

class User < ActiveRecord::Base
  #...
  def manages_network_ids
    @manages_network_ids ||= roles.manager.collect &:network_id
  end

  def admins_network_ids
    @admins_network_ids ||= roles.admin.collect &:network_id
  end

  def supervises_network_ids
    @supervises_network_ids ||= roles.supervisor.collect &:network_id
  end
  #...
end

关于角色你有:

class Role < ActiveRecord::Base
  #...
  scope :manager, where(position: "manager")
  scope :admin, where(position: "admin")
  scope :supervisor, where(position: "supervisor")
  #...
end

这允许您的事件为空role_id,因为您将允许网络上的任何管理员(或管理员)管理受network_id约束的所有事件。此外,如果您将角色重新分配给新用户,则旧用户将无法再管理该事件,而新用户将无法管理该事件。

答案 1 :(得分:0)

要回答您的第一个问题,belongs_to技术上可以nil。您必须进行验证(validates_presence_of)以避免这种情况。

我不知道CanCan 但是,从概念层面来说,我不会为了能够访问数据而写出这么多关联。 Rails协会不仅仅是一个数据阅读器,我不确定你是否在这里需要它。

例如,用户#事件可以替换为:

def events
  roles.includes(:events).map(&:events).flatten
end

或者

def events
  Event.where(:role_id => roles.map(&:id))
end

或者

def events
  Event.joins(:role).where(:role => {:user_id => id})
end

如果多次调用此方法,可以添加一些memoization。 事实上,如果你只召唤一次,你几乎不需要一种方法。

关于将事件与角色和网络相关联,我会小心,因为您可能存在一致性问题(您必须确保event.role.network == event.network)。另一种方法是为每个网络提供一个无法销毁的默认角色。但它可能并不简单。