Пользовательская модель Ruby On Rails для нескольких типов - PullRequest
10 голосов
/ 18 октября 2010

Я изучаю RoR из многих лет c # и MSSQL.

Я выбрал проект для создания веб-сайта для моего брата, который является менеджером по аренде недвижимости.Я подумал, что это должно быть довольно легко, так как модели должны быть прямыми, но мне кажется, что я переборщил со всем или у меня возникли проблемы с отказом от «старого» пути.В любом случае здесь проблема.Я начинаю только с двух моделей (Пользователь и Собственность).Модель собственности проста, пользователя не так много.Я понял, что у нас есть три типа пользователей в системе.Арендаторы, Владельцы и Менеджеры (мой брат будет единственным менеджером, но я решил, что планирую его развивать). Он управляет объектами недвижимости для нескольких владельцев, каждый из которых может владеть многими объектами.Каждое свойство будет иметь одного владельца, одного арендатора и одного управляющего.

Арендаторы смогут войти в систему и просто просмотреть арендуемое им имущество, чтобы, возможно, заполнить запрос на техническое обслуживание или что-то в этом роде… (никаких реальных требований вэтот пункт даже дает арендатору логин в систему, но я подумал, что это будет хорошее упражнение)

То же самое касается владельцев, никому из них не нужен доступ к системе (они нанимают моего брата, чтобы онине должен быть вовлечен), но я подумал, что это может быть хорошим и снова хорошим упражнением.

Я использовал 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

Мой вопрос: выглядит ли это приемлемым способом моделирования ситуации?n Я описал?

Должен ли я использовать наследование одной таблицы и создать модель арендатора;модель менеджера;а владелец модели?Проблема, с которой я столкнулся, заключалась в том, что один пользователь мог быть и менеджером, и владельцем.Эту проблему можно решить, имея таблицы ролей для пользователя, где у пользователя много ролей, а у роли много пользователей.Я также просмотрел таблицу профилей с однозначным соответствием с пользовательской таблицей и сделал ее полиморфной, но я не думал, что эта ситуация действительно требует этого, и это не решило проблему, когда пользователь может быть владельцеми менеджер .....

Это когда я начал думать, что, может быть, я переборщил с проблемой и придумал, что вы видите здесь.

Я приветствую любые ваши конструктивные комментарии.Пожалуйста, имейте в виду, что я никогда ничего не создавал в Rails, и это все первая попытка, неделю назад я даже не устанавливал rails на свой компьютер.

Я не знаю, имеет ли это значение, но яполагал, что администратор / менеджер будет отвечать за создание пользователей.Это не будет самоподписывающийся тип сайта.Менеджер добавит новых владельцев, когда он зарегистрирует нового владельца, и то же самое пойдет на арендаторов.Это облегчит определение типа создаваемого им пользователя.

Спасибо за понимание, которое у вас есть.

Ответы [ 2 ]

8 голосов
/ 18 октября 2010

FWIW, это выглядит хорошо для меня.Я мог бы взглянуть на Declarative_authorization , чтобы управлять вашими ролями, что может оказаться несколько сложным, особенно с точки зрения пользовательского интерфейса.В этой ситуации управление пользователями с несколькими ролями представляется более подходящим, чем STI, поскольку, как вы говорите, пользователь может быть как администратором, так и арендатором и т. Д.

Один подход к разделению кода для менеджера, арендатора и владельцахотя допускается использование всех трех ролей в одном экземпляре, возможно, будет динамически включать модули, представляющие эти роли, в зависимости от того, какие роли имеет данный пользователь.У вас все еще был бы базовый класс User, но вместо использования STI, который ограничивал бы вас единственной структурой наследования, вы могли бы смешивать модули в зависимости от ролей каждого пользователя, что могло бы просто сводиться к тому, какие Properties принадлежат данномупользователь во время инициализации.

Для этого я мог бы создать фабрику пользователей, которая может проверять пользователя на различные роли, которые он играет, и расширять одноэлементный класс этого пользователя соответствующим модулем: расширять его модулем Tenant дляпользователи, у которых есть свойства арендатора, расширяются с помощью модуля Manager для пользователей, у которых есть управляемые свойства и т. д.

С точки зрения декларативного_авторизации вы можете аналогичным образом объявить role_symbols на основе того, были ли заполнены такие ассоциации, как managed_properties, например:

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 предоставляют вам множество опций с помощью методов метапрограммирования , чтобы динамически украшать функциональность, необходимую отдельным моделям.Мой подход может или не может быть подходящим для вашего приложения, но есть бесчисленное множество других способов, которыми вы могли бы подойти к проблеме и сохранить свой код чистым и СУХИМЫМ.

В целом мне кажется, что ваша модель данных надежна, а вашаинстинкт не использовать STI для управления несколькими ролями является правильным.Мне не кажется, что ты обдумал это - я думаю, ты на правильном пути.Это на самом деле довольно Rails-ы для первого прохода.

РЕДАКТИРОВАТЬ: Вы знаете, чем больше я думаю об этом, я не уверен, в чем на самом деле преимущество сохранения функций менеджера / арендатора / владельца в отдельных модулях.В моем прежнем воплощении в качестве парня из Java / C # я был бы все о SRP / IOC и полном разделении интересов.Но в Ruby и Rails это не так уж важно, так как оно динамически типизировано, а связь не так велика или, по крайней мере, не такая, как в средах со статической типизацией.Возможно, вам будет хорошо, если вы поместите все функции отдельных ролей в модель одного пользователя и не будете беспокоиться о модулях, по крайней мере пока.

Я здесь на заборе, и приветствовал бы мнение других.Для меня одно из преимуществ классов Ruby, в отличие от классов / пакетов Java или классов / сборок .NET, заключается в том, что вы всегда можете выполнять рефакторинг по мере необходимости и не заботиться о том, какой класс, пакет, пространство имен, dll или jarя не говорю, что SRP не важен в Ruby, совсем нет.Но я не настолько параноик, как раньше.

РЕДАКТИРОВАТЬ: Пол Рассел делает отличную точку.Я думаю, что вы должны серьезно рассмотреть вопрос о разрешении нескольких арендаторов / менеджеров / арендодателей на недвижимость.В Rails это может быть выражено через реляционную таблицу и has_many: via association, плюс STI для описания различных типов отношений.Я также думаю, что необходимо будет изменить отношения между Пользователем (как Арендатором) и Собственностью.Собственность может иметь более одного Арендатора, но Арендатор не может жить более чем в одной собственности.(или, может, они могут? Не кажется правильным, но ...)

Может быть, что-то вроде (это очень быстро и грязно, поэтому, пожалуйста, простите все пропущенные детали):

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

Как я уже сказал, это быстро и грязно, первый проход высокого уровня. Обратите внимание, что теперь мы создали роли «Менеджер» и «Владение», которые могут содержать функции «Менеджер» и «Владелец», устраняя прежнюю дилемму в отношении того, следует ли использовать эти функции в отдельных модулях.

** Обратите внимание, что я также изменил роль арендатора / свойства - я думаю, что это было необходимым изменением в вашем домене. Очевидно, что резиденция может иметь более одного арендатора. Мне кажется (на данный момент), что вы можете сохранить специфическую для арендатора функциональность в модели User.

3 голосов
/ 18 октября 2010

Что меня поразило, так это то, что ваша текущая модель запрещает иметь нескольких арендаторов или арендодателей в данном имуществе и предполагает, что эти отношения постоянны.

В Великобритании, по крайней мере, очень часто возникает ситуация, когда собственность принадлежит супружеской паре и, следовательно, может иметь несколько арендодателей (например, мы с женой на этом месте). Точно так же для определенных типов аренды очень часто, когда в одном свойстве несколько арендаторов.

Если вы думаете, что я здесь, то стоит рассмотреть возможность введения объекта модели 'user_property_role' между пользователем и свойством. Тогда у вас будет отношение has_many от пользователя и свойства к user_property_role. Если у этой роли есть поле «тип отношений», которое вы можете установить, например, 'landlord', вы можете использовать объект has_many: through и named scopes (для user_property_role), например: property.users.landlords или property.users.tenants.

Использование этого подхода также позволит вам делать такие вещи, как назначать этим отношениям даты начала и окончания, регистрируя, например, тот факт, что свойство имеет несколько арендаторов во времени, или что свойство может управляться различными люди со временем. Опять же, вы должны иметь возможность встроить это в набор именованных областей, чтобы вы могли сделать, например, property.users.current.tenants или даже user.properties.current.tenants.

Надеюсь, это поможет, а не только добавит вашей путаницы !!

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...