Model użytkownika Ruby On Rails dla wielu typów

Uczę się RoR pochodzących z wielu lat c # i MSSQL.

Wybrałem projekt budowy strony internetowej dla mojego brata, który jest zarządcą nieruchomości. Pomyślałem, że powinno to być dość łatwe, ponieważ modele powinny być proste, ale myślę, że mogę być za myśleniem o wszystkim lub mam problem z odpuszczeniem "starego" sposobu. W każdym razie tu jest problem. Zaczynam od zaledwie dwóch modeli (User I Property). Model nieruchomości jest łatwy, użytkownik nie tak bardzo. I uznałem, że mamy trzy typy użytkowników w systemie. Najemcy, właściciele i menedżerowie (mój brat będzie jedynym menedżerem, ale pomyślałem, że zaprojektuję go, aby rosnąć) zarządza nieruchomościami dla kilku właścicieli, z których każdy może posiadać wiele nieruchomości. Każda nieruchomość będzie miała jednego właściciela, jednego najemcę i jednego żłobka.

Najemcy będą mogli się zalogować i po prostu zobaczyć nieruchomość, którą wynajmują, aby może wypełnić żądanie konserwacji lub coś w tym stylu...(nie ma rzeczywistego wymogu w tym momencie, aby nawet dać lokator a login do systemu, ale myślałem, że to będzie dobre ćwiczenie)

To samo dotyczy właścicieli, żaden z nich nie potrzebuje dostępu do systemu (zatrudniają mojego brata, więc nie muszą się angażować), ale pomyślałem, że może to być miłe i znowu dobre ćwiczenie.

Użyłem Nifty_generator do wygenerowania użytkownika, który po prostu podaje email, hasło itp. Rozszerzyłem go w następujący sposób ...

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

Oto kod do utworzenia tabeli właściwości

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

Dodałem do modelu użytkownika wygenerowanego przez generator 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"

Dodałem to do modelu właściwości....

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

Moje pytanie brzmi, czy wygląda to na akceptowalny sposób modelowania sytuacji, którą opisałem?

Czy powinienem używać dziedziczenia pojedynczej tabeli i tworzyć model dzierżawcy, model menedżera i model właściciela? Problem, który widziałem z robieniem tego, że pojedynczy użytkownik może być zarówno menedżerem, jak i właścicielem. To może być rozwiązane przez to o role tabele dla użytkownika, gdzie użytkownik ma wiele ról i rola ma wielu użytkowników. Spojrzałem również na tabelę profilu z dopasowaniem jeden do jednego z tabelą użytkownika i dokonaniem tego polimorficznego, ale nie sądziłem, że ta sytuacja naprawdę tego wymaga i nie rozwiązało problemu, w którym użytkownik może być właścicielem i menedżerem.....

To wtedy zacząłem myśleć, że może nad tym myślałem i wymyśliłem to, co tu widzisz.

Witam wszelkie konstruktywne uwagi. Proszę pamiętać, że nigdy nie zbudowałem niczego w Rails i to wszystko jest pierwsza próba, tydzień temu nigdy nawet nie zainstalowałem rails na moim komputerze.

Nie wiem, czy to ma znaczenie, ale pomyślałem, że administrator / menedżer będzie odpowiedzialny za tworzenie użytkowników. Nie będzie to strona typu self sign up. Menedżer doda nowych właścicieli, gdy zarejestruje nowego właściciela, a to samo dotyczy najemców. Ułatwi to Określ typ użytkownika, którego tworzy.

Dzięki za wszelkie spostrzeżenia.
Author: Chris, 2010-10-18

2 answers

FWIW, jak dla mnie wygląda dobrze. Mogę spojrzeć na declarative_autoryzation , Aby zarządzać swoimi rolami, które mogą być nieco zaangażowane, szczególnie z punktu widzenia interfejsu użytkownika. Zarządzanie użytkownikami z wieloma rolami wydaje się bardziej odpowiednie niż STI w tej sytuacji, ponieważ, jak mówisz, użytkownik może być zarówno menedżerem, jak i dzierżawcą itp.

Jednym z rozwiązań pozwalających na oddzielenie kodu dla menedżera, dzierżawcy i właściciela, przy jednoczesnym umożliwieniu użycia wszystkich trzech ról w jednej instancji, może być dynamiczne dołączanie modułów reprezentujących te role w zależności od ról danego użytkownika. Nadal masz podstawową klasę użytkownika, ale zamiast używać STI, które ograniczałoby Cię do pojedynczej struktury dziedziczenia, możesz mieszać Moduły w oparciu o role, które ma każdy użytkownik, co może po prostu obracać się na jakie właściwości belong_to danego użytkownika podczas inicjalizacji.

W tym celu mogę utworzyć fabrykę użytkowników, która może sprawdzić użytkownika pod kątem różnych ról, które odgrywa i rozszerzyć Klasa singleton tego użytkownika z odpowiednim modułem: extend z modułem dzierżawca dla użytkowników posiadających właściwości dzierżawne, extend z modułem Manager dla użytkowników posiadających właściwości zarządzane, itd.

Z punktu widzenia declarative_autoryzation, Można zadeklarować role_symbols podobnie na podstawie tego, czy Asocjacje takie jak managed_properties zostały wypełnione, na przykład:

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
Albo coś podobnego. Prawdopodobnie chciałbyś ustawić role i dołączyć do odpowiedni moduł w tym samym czasie. Ruby i Rails daje wiele opcji za pomocą technik metaprogramowania , aby dynamicznie dekorować funkcjonalnością potrzebną poszczególnym modelom. Moje podejście może, ale nie musi być odpowiednie dla Twojej aplikacji, ale istnieje wiele innych sposobów podejścia do problemu i utrzymania kodu w czystości i suchości.

Ogólnie wydaje mi się, że twój model danych jest dobry, a twój instynkt, aby nie używać STI do zarządzania wieloma rolami, jest prawidłowy. Nie ma myślę, że jesteś na dobrej drodze. Całkiem nieźle jak na pierwsze podanie.

EDIT: wiesz, im więcej o tym myślę, nie jestem pewien, jakie korzyści daje utrzymanie funkcjonalności menedżera/najemcy/właściciela w oddzielnych modułach. W moim poprzednim wcieleniu jako Java / C# guy byłbym za SRP/MKOl i całkowite rozdzielenie obaw. Ale w Ruby i Rails nie jest to aż tak wielka sprawa, ponieważ jest typowanie dynamiczne i sprzężenie nie jest tak duże, a przynajmniej takie samo, co w środowiskach typowanych statycznie. Możesz być całkowicie w porządku, po prostu umieszczając wszystkie funkcje poszczególnych ról w modelu pojedynczego użytkownika i nie martw się modułami, przynajmniej jeszcze nie teraz.

Jestem na ogrodzeniu tutaj, i mile widziane wejście od innych. Dla mnie jedną z zalet klas Ruby, w przeciwieństwie do klas/pakietów Javy lub klas/zestawów. NET, jest to, że zawsze możesz refactor w razie potrzeby i nie być prawie tak zaniepokojony, która klasa, pakiet, przestrzeń nazw, dll lub jar jest powiązana z inną, itd. Nie mówię, że SRP nie jest ważne w Ruby, wcale. Ale nie jestem pod tym względem aż tak paranoiczna jak kiedyś.

EDIT: Paul Russell ma rację. Myślę, że powinieneś poważnie rozważyć dopuszczenie wielu najemców / zarządców / właścicieli na nieruchomość. W Rails można to wyrazić poprzez relacyjną tabelę i has_many: poprzez asocjację, plus STI, aby opisać różne rodzaje relacji. Myślę też, że konieczne będzie odwrócenie relacji między Użytkownikiem (jako najemcą) a nieruchomością. Nieruchomość może mieć więcej niż jednego Najemcę, ale Najemca nie może mieszkać w więcej niż jednej nieruchomości. (a może mogą? To nie w porządku, ale...)

Może coś w stylu (to jest bardzo szybkie i brudne, więc wybacz wszystkie pominięte szczegóły proszę):

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

Jak mówiłem, to jest szybkie i brudne, pierwsze podanie na wysokim poziomie. Zauważ, że mamy teraz stworzono role zarządzania i własności, które mogą zawierać funkcje Menedżera i właściciela, eliminując poprzedni dylemat, czy usunąć tę funkcjonalność w oddzielnych modułach.

** zauważ również, że odwróciłem rolę najemcy / nieruchomości-myślę, że była to konieczna zmiana w Twojej domenie. Oczywiście rezydencja może mieć więcej niż jednego najemcę. Wydaje mi się (w tej chwili), że można zachować funkcjonalność specyficzną dla Najemcy w modelu użytkownika.

 8
Author: Dave Sims,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-10-18 07:00:12

Uderza mnie to, że twój obecny model zabrania posiadania wielu najemców lub właścicieli w danej nieruchomości i zakłada, że te relacje są trwałe.

Przynajmniej w Wielkiej Brytanii bardzo często występuje sytuacja, w której nieruchomość jest własnością małżeństwa, a zatem może mieć wielu właścicieli (na przykład moja żona i ja jesteśmy w tej sytuacji). Podobnie w przypadku niektórych rodzajów najmu bardzo często zdarza się, że w jednym jest wielu najemców własność.

Jeśli uważasz, że jestem tutaj, warto rozważyć wprowadzenie obiektu modelu' user_property_role ' pomiędzy Użytkownikiem a właściwością. Następnie mielibyśmy relację has_many z user i property do user_property_role. Jeśli ta rola miała pole 'relation type', które można ustawić na np. 'landlord', można użyć has_many: through i nazwanych zakresów (na obiekcie user_property_role)do wykonania np. właściwości.użytkowników.właściciele lub własność.użytkowników.lokatorów.

Biorąc to podejście pozwoli również zrobić rzeczy, takie jak dać te relacje "początek" i "koniec" daty, rejestrując fakt, że nieruchomość ma wielu najemców w czasie, na przykład, lub że nieruchomość może być zarządzana przez różnych ludzi w czasie. Ponownie, powinieneś być w stanie zbudować to w zestaw nazwanych zakresów tak, że można zrobić np. właściwość.użytkowników.aktualne.lokatorów, a nawet użytkowników.właściwości.aktualne.lokatorów.

Mam nadzieję, że to pomoże, a nie wystarczy dodać do zamieszania!!

 3
Author: Paul Russell,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-10-18 05:59:37