用户的 Rails 模型结构

Posted

技术标签:

【中文标题】用户的 Rails 模型结构【英文标题】:Rails model structure for users 【发布时间】:2011-09-03 11:43:41 【问题描述】:

我是 Rails 新手,正在开发我的第二个 Rails 应用程序。

该应用将为用户分配不同的角色,但有些用户将拥有多个角色。

网站的每个用户都是艺术家。一些用户将担任版主的角色。

我将如何构建它?在我使用过的一些 php 应用程序中,只有一个用户,然后是 is_admin 等的数据库列。但是我查看了 rails 应用程序的源代码,并看到了用户和管理员等单独的模型,尽管我'不知道为什么。

那么,我是否应该有一个具有角色属性的用户模型,可以是主持人,然后在我的视图、路线等中将用户称为“艺术家”?

或者我应该有一个 User 模型,一个从它继承的 Moderator 模型,以及一个属于 User 的 Artist 模型?

我真的很困惑。

【问题讨论】:

【参考方案1】:

您可以寻找宝石 Devise 和 CanCan。这对组合真的很强大。这使得两个模型用户和角色。在角色中,您可以创建新角色,而无需为它们创建新模型。虽然它创建了模型能力,但您可以在此处定义角色的访问规则。

手册: http://www.tonyamoyal.com/2010/07/28/rails-authentication-with-devise-and-cancan-customizing-devise-controllers/

在这里您可以找到 Devise 和 CanCan 的资源和维基:

https://github.com/plataformatec/devise

https://github.com/ryanb/cancan

我的模型如下所示:

角色.rb

class Role < ActiveRecord::Base
  has_and_belongs_to_many :users
end

用户.rb

class User < ActiveRecord::Base
  has_many :accounts
  has_and_belongs_to_many :roles

  # Include default devise modules. Others available are:
  # :token_authenticatable, :confirmable, :lockable and :timeoutable
  devise :database_authenticatable,
         :recoverable, :rememberable, :trackable, :validatable

  # Setup accessible (or protected) attributes for your model
  attr_accessible :email, :username, :password, :password_confirmation, :remember_me, :role_ids

  def role?(role)
    return !!self.roles.find_by_name(role.to_s.camelize)
  end

end

能力.rb

class Ability
  include CanCan::Ability

  def initialize(user)
    user ||= User.new # guest user

    if user.role? :administrator
      can :manage, :all
    elsif user.role? :operator
      can :read, Account
      can :read, Server
    elsif user.role? :customer
      can :manage, Account
      can :read, Server
    end
  end
end

在控制器中你必须只添加这两行:

class YourController < ApplicationController
  before_filter :authenticate_user!
  load_and_authorize_resource

  ...

end

【讨论】:

这是我的建议。我喜欢这种方法的地方在于它鼓励将业务策略与应用程序逻辑的其余部分分离。 是的,这种分离提供了良好而灵活的能力。我在一个项目中使用它,开发人员制作了非常不舒服的身份验证系统,为了添加新的角色和能力,我必须在每个控制器和模型中编写大量代码。我花了很多时间来删除旧系统,并且花了将近两个小时来安装和配置 Devise 和 CanCan。现在我很高兴。当我有空闲时间时,我计划编写用户界面来管理角色和能力。有了这些系统,这项任务就变得简单了。 我们已经在多个应用程序中使用了它,并且我们还发现 devise-cancan 组合是“同类最佳”方法。许多组织正在迅速采用它作为今年的身份验证/授权标准。【参考方案2】:

如果您需要特定于角色或(例如管理员或版主)的代码,另一种解决方案是创建一个所有其他类都继承自的基本用户模型。然后,您可以创建从 User 模型继承的 Admin 类和 Moderator 类。这意味着您可以避免不断检查代码中的用户角色,例如current_user.do_some_admin_thing if current_user.is_admin?。你的课程看起来像这样

class User < ActiveRecord::Base
  # base user methods in here
end

class Moderator < User
  def do_moderator_thing
    # perform a moderator task
  end
end

class Admin < Moderator
  def do_admin_thing
    # perform an admin task
  end
end

在这种情况下,用户类具有最基本的权限,版主可以做任何用户可以做的事情加上版主特定的方法,管理员可以做任何用户和版主可以做的事情加上管理员特定的方法。

所有不同的用户角色都将使用数据库中的同一张表,但您的关注点被整齐地划分为类,从而通过您的代码检查用户始终是什么角色来避免过多的条件。

创建新用户也很简单Admin.new :name =&gt; 'bob' Admin 类然后负责如何将用户定义为管理员,它提供了一个很好的界面,您无需了解角色系统的内部工作方式即可进行交互与用户。

【讨论】:

请编辑您的问题,明确说明这是使用单表继承这一事实,并链接到其上的官方 Rails 文档。 这个问题没有谈论不同用户的不同行为,只是不同的授权来做不同的事情。除非 User 模型的实例实际上表现不同,否则创建不同的子类过于复杂。过度使用继承是一种反模式。【参考方案3】:

我认为您不必创建不同的模型,因为每个模型都没有特定的字段。所以你只需要设置每个用户的“角色”。两种选择:创建角色表或在用户表中添加角色字段。两种解决方案都有效,第二种更灵活但优化程度较低。

但是,在您的特定情况下,您没有复杂的角色管理,因此您可以找到更简单的解决方案。如果您的所有用户都是艺术家,则您不必在代码中指定这一点,它包含在用户是什么的隐式描述中。所以你只需要保存用户是否是管理员,我认为最好的解决方案是创建一个布尔字段“is_admin”。

之后,您必须在受保护的控制器中创建一些 before_filter,如下所示:

before_filter => :authorize, :only => :new, :edit, :create, :update, :destroy

def authorize
  redirect_to :root if not current_user.is_admin?
end

你可以有这样的简单请求:

@artists = User.all
@moderators = User.where(:is_admin => true)

如果您正在寻找更完整的授权系统,您可以查看这个小宝石:https://github.com/ryanb/cancan

但我认为目前情况并非如此。如果您有一个简单的问题,请寻找一个简单的解决方案!

【讨论】:

AR 中的布尔值产生foo? 方法,非常适合条件语句,而Ruby 有更好的if not unless 形式。将它们组合在一起:redirect_to :root unless current_user.is_admin? +1 用于简单的解决方案。 ruby 的好处在于,当您更改为单独的角色模型或其他内容时,您可以使用布尔字段并保留is_admin? 接口。【参考方案4】:

虽然我同意 Devise 和 CanCan 的组合功能强大且有效。让我们以不同的视角来看待它,同时牢记关联和委托。

关联:在面向对象编程中,关联定义了一个 允许一个对象的对象类之间的关系 实例以使另一个实例代表它执行操作。

委托:委托允许对象的行为 根据另一个对象的行为来定义。术语 “委托”是指责任的委托。首要的 委托的重点是在对象可以传递的消息传递 将无法处理的消息的责任委托给对象 这可能(其代表)。

这样,如果我们这样设计用户和角色会怎样。没有 Role 类,并且 User 没有继承或专门化特定类( Artist, Admin ),而是所有(Role)类都包含 User 对象和委托。我的想法和使用 Rails 实现的方式是这样的:

class User < AR::Base
  def user_method
  end
end 

class Artist < AR::Base
  has_one :user

  def artist_method
    # perform an admin task
  end
end

class Admin < AR::Base
  has_one :user

  def admin_method
    # perform an admin task
  end
end

这个role class model 由 Francis G. Mossé 在他关于建模角色的文章中描述。

【讨论】:

【参考方案5】:

这是我使用的declarative authorization gem 的基本设置。但是,如果您的授权要求只是询问用户拥有的角色类型,则您可以在没有 gem 的情况下按原样使用它。

它确实需要一个 roles 表等,所以这可能不是你真正喜欢的。

class Role < ActiveRecord::Base
  belongs_to :user
end

class User < ActiveRecord::Base
  has_many :roles

  def role_symbols
    roles.map  |r| r.title.to_sym 
  end

  def admin?
    has_role?(:admin)
  end
  # include more role booleans or write some ruby magic to be DRY
  # ...

  def has_role?(r)
    role_symbols.include?(r.to_sym)
  end
end

# give or take
user = User.new
user.roles << Role.new :title => "admin"
user.roles << Role.new :title => "artist"

user.role_symbols # => [:admin, :artist]
user.admin? # => true
user.has_role?(:artist) # => true

【讨论】:

【参考方案6】:

您可以有两个模型用户和角色。并且角色属于用户。

在角色模型中指定用户的角色(如管理员、版主)。

【讨论】:

以上是关于用户的 Rails 模型结构的主要内容,如果未能解决你的问题,请参考以下文章

rails模型的影子表

Rails 创建没有迁移的表

Rails 3.1 & RestKit 0.9.3 - 引用映射

Rails Carrierwave和Imagemagick,使用条件调整图像大小

使用关注点扩展具有角色的用户模型

Rails:如何跨视图访问模型和控制器数据?