ruby-on-rails - 多种类型的Ruby On Rails用户模型

标签 ruby-on-rails model

我正在学习来自多年C#和MSSQL的RoR。

我选择了一个项目来为我的兄弟(一家出租属性(property)经理)建立一个网站。我认为这应该很容易,因为模型应该简单明了,但是它认为我可能考虑得太多了,或者我无法摆脱“旧的”方式。无论如何,这就是问题所在。我从两个模型(User和Property)开始。属性模型很简单,用户就不那么多了。我认为系统中有三种类型的用户。租户,所有者和管理者(我的兄弟将是唯一的管理者,但我认为我会设计它来发展)。他为多个所有者管理属性(property),每个所有者可以拥有许多属性(property)。每个属性(property)将有一名所有者,一名租户和一名管理人员。

租户将能够登录,并看到他们租用的房地产以填写维护请求或类似的内容……(目前甚至没有真正的要求,甚至可以让租户登录系统,但我认为这是一个很好的选择锻炼)

所有者也有同样的想法,他们中的任何一个都不真正需要访问系统(他们雇用了我的兄弟,所以他们不必参与其中),但我认为这可能是不错的选择,而且还是不错的练习。

我使用Nifty_generator生成了一个用户,该用户仅提供电子邮件,密码等。我将其扩展如下:

class AddProfileDataToUsers < ActiveRecord::Migration
  def self.up
    add_column :users, :first_name, :string
    add_column :users, :last_name, :string
     add_column :users, :address1, :string
     add_column :users, :address2, :string
     add_column :users, :city,:string
     add_column :users, :state, :string
     add_column :users, :zip, :string
     add_column :users, :phone, :string
     add_column :users, :email, :string
     add_column :users, :user_type, integer
  end

  def self.down 
    remove_column :users, :first_name 
    remove_column :users, :last_name
   remove_column :users, :address1
   remove_column :users, :address2
   remove_column :users, :city
   remove_column :users, :state
   remove_column :users, :zip 
   remove_column :users, :phone 
   remove_column :users, :email 
   remove_column :users, :user_type
  end
end

这是创建属性表的代码
class CreateProperties < ActiveRecord::Migration
  def self.up
    create_table :properties do |t|
      t.string :address
      t.string :city
      t.string :type
      t.integer :beds
      t.float :baths
      t.float :price
      t.float :deposit
      t.string :terms
      t.string :laundry
      t.datetime :date_available
      t.integer :sqft
      t.integer :owner_id
      t.integer :manager_id
      t.integer :tenant_id
      t.timestamps
    end
  end

  def self.down
    drop_table :properties
  end
end

我将以下内容添加到由nifty_authentication生成器生成的用户模型中
class User < ActiveRecord::Base

  #other stuff in the user model up here......
  validates_length_of :password, :minimum => 4, :allow_blank => true

  #this is the stuff that I have added to the user model
  has_many :managed_properties, :class_name => "Property", :foreign_key => "manager_id"
  has_many :owned_properties, :class_name => "Property", :foreign_key => "owner_id"
  has_one :rented_property, :class_name => "Property", :foreign_key => "tenant_id"

然后,我将其添加到属性模型中。
class Property < ActiveRecord::Base
    belongs_to :manager, :class_name => "User" #picked up by the manager_id
    belongs_to :owner, :class_name => "User"  #picked up by the owner_id
    belongs_to :tenant, :class_name => "User"  #picked up by the tenant_id
end

我的问题是,这看起来像是一种对我描述的情况进行建模的可接受方法吗?

我应该使用单表继承并创建租户模型吗?经理模式;和所有者模型?我这样做的问题是,单个用户既可以是管理员,也可以是所有者。然后可以通过为用户提供一个角色表来解决此问题,其中一个用户具有多个角色,而一个角色具有多个用户。我还查看了一个与用户表一对一匹配的配置文件表,并使其具有多态性,但我不认为这种情况确实需要这样做,也无法解决用户可以成为所有者的问题和经理.....

这是当我开始认为也许我正在考虑问题并提出您在此处看到的内容时。

我欢迎您提出任何 build 性意见。请记住,我从未真正在Rails中构建任何东西,这是第一次尝试,一个星期前,我什至从未在计算机上安装过Rails。

我不知道这是否重要,但我认为管理员/经理将负责创建用户。这将不是网站的自助注册类型。经理在注册新所有者时将添加新所有者,租户也将添加新所有者。这将使确定他正在创建的用户类型变得更加容易。

感谢您的任何见解。

最佳答案

FWIW,这对我来说很好。我可能会考虑使用declarative_authorization来管理您的角色,这最终可能会涉及到某些方面,尤其是从UI角度来看。在这种情况下,管理具有多个角色的用户似乎比STI更合适,因为正如您所说,用户既可以是管理员,也可以是租户,等等。

一种将经理,租户和所有者的代码分开的方法,同时允许在单个实例中使用所有三个角色,一种方法可能是根据任何给定用户所具有的角色来动态地包含代表这些角色的模块。您仍然具有User的基类,但是可以使用基于每个用户所具有的角色的模块来混合使用模块,而不是使用STI(将STI限制为单个继承结构),这可以简单地取决于给定属性的归属用户在初始化期间。

为此,我可以创建一个用户工厂,该工厂可以检查用户所扮演的各种角色,并使用相应的模块扩展该用户的singleton class:对于具有租户属性的用户,使用Tenant模块进行扩展;对于用户,使用Manager模块进行扩展。具有托管属性等

从declarative_authorization的角度来看,您可以类似地基于是否填充了诸如managed_properties之类的关联来声明role_symbols,例如:

def role_symbols  
  @roles ||= {:manager => :managed_properties, 
    :tenant => :rented_property, 
    :owner => :owned_properties}.map do |k,v|
      k if !send(v).blank?
    end.compact
end

或类似的东西。您可能想要设置角色并同时包含相应的模块。 Ruby和Rails通过metaprogramming techniques为您提供了许多选项,可以动态地修饰各个模型所需的功能。我的方法可能适合您的应用程序,也可能不适合您的应用程序,但是您可以通过无数其他方法来解决该问题并使代码保持干净和干燥。

总的来说,在我看来,您的数据模型是正确的,并且您本能不使用STI管理多个角色是正确的。在我看来,您似乎并没有想得太深-我认为您的方向正确。第一次通过实际上很漂亮。

编辑:您知道,我想得越多,我不确定在单独的模块中保留管理器/租户/所有者功能的好处是什么。在我以前作为Java/C#角色的化身中,我本来会关注SRP/IOC和完全分离的关注点。但是在Ruby和Rails中,这并不是一件大事,因为它是动态类型的,而耦合在静态类型的环境中也没有那么大,或者至少是同一种类型。您可以将所有单个角色功能都放在单个用户模型中,而不必担心模块,至少现在还不用担心。

我在栅栏上,欢迎其他人的意见。对我来说,与Java类/包或.NET类/程序集相比,Ruby类的好处之一是您可以始终根据需要进行重构,而不必担心哪个类,程序包, namespace ,dll或jar是什么。耦合到另一个,等等。我并不是说SRP在Ruby中并不重要,一点也不。但是我对此并不像以前那样偏执。

编辑:保罗·罗素提出了一个很好的观点。我认为您应该认真考虑允许每个属性允许多个租户/经理/房东。在Rails中,这可以通过关系表和has_many:through关联以及STI来描述不同类型的关系来表示。我也认为有必要颠倒用户(作为租户)和属性(property)之间的关系。一个属性(property)可以有多个租户,但是一个租户不能住在一个以上的属性(property)。 (或者他们可以?似乎不正确,但是...)

可能是这样的(这是非常快速又肮脏的,因此请原谅任何遗漏的细节):
class PropertyRole < ActiveRecord::Base
  belongs_to :user
  belongs_to :property
end

class Managership < PropertyRole
  # Manager functionality here
end

class Ownership < PropertyRole
  # Owner functionality here
end

class User < ActiveRecord::Base
  belongs_to :residence, :class_name => 'Property', :foreign_key => 'residence_id'
  has_many :managerships
  has_many :ownerships

  has_many :owned_properties, :through => :ownerships, :classname => 'Property'
  has_many :managed_properties, :through => :managerships, :classname => 'Property'
end

class Property < ActiveRecord::Base
  has_many :tenants, :class_name => 'User', :foreign_key => 'residence_id'
  has_many :managerships
  has_many :ownerships
  has_many :owners, :class_name => 'User', :through => :ownerships
  has_many :managers, :class_name => 'User', :through => :managerships
end

就像我说的那样,这是快速而又肮脏的,高级的第一步。请注意,我们现在已经创建了Managership和Ownership角色,这些角色可以包含Manager和Owner特定的功能,从而消除了以前是否需要在单独的模块中单独使用该功能的难题。

**还请注意,我也改变了承租人/属性(property)角色-我认为这是对您的域的必要更改。显然,一个住所可以有多个租户。在我看来(目前),您可以在User模型上保留特定于租户的功能。

关于ruby-on-rails - 多种类型的Ruby On Rails用户模型,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/3956588/

相关文章:

javascript - 对象无效问题

php - 当先前调用另一个模型时,如何将配置传递给模型?

ruby-on-rails - 什么是 devise_mapping 变量,如何包含它?

ruby-on-rails - 递归包括所有模型子目录

asp.net-mvc-2 - 在mvc2中创建ModelStateErrorMessage

javascript - 没有获取正确的 url 问题

database - Web 应用程序模型原型(prototype)制作

ruby-on-rails - 意外的 keyword_end,期望输入结束

ruby-on-rails - 在模板 Ruby on Rails 中渲染发布请求

ruby-on-rails - 设计 - 仅由管理员创建用户