Strona główna Polish Python Coders Group
   Strona główna   Pomoc Zaloguj się Rejestracja  
Witamy, Gość. Zaloguj się lub zarejestruj.
Czy dotarł do Ciebie email aktywacyjny?

Zaloguj się podając nazwę użytkownika, hasło i długość sesji

Aktualności: PyStok #42 - wystąpią Łukasz Langa i Dominik Kozaczko
Szukaj Szukaj
Strony: [1]   Do dołu
Drukuj
Wątek: Jeden użytkownik ma dostęp do wielu firm (SaaS like app)  (Przeczytany 322 razy)
« : 19:02 29/05/19 »
python_django Offline
Hello World!

Zobacz profil
*

Reputacja: 1
Wiadomości: 8


Flow:

1. Onboarding: w panelu admina tworzę ręcznie firmę i użytkownika z role company_admin, następnie użytkownik ten dostaje maila z "invitation-url"
2. company_admin otwiera URL, ustawia swoje hasło, wypełnia profil i może wysyłać zaproszenia do innych użytkowników

Problem:

Dwie różne firmy nie mogą dodać użytkownika o tym samym adresie email. Jaki jest najlepszy sposób na rozwiązanie tego problemu?

Admini Company A i Company B powinni mieć możliwość dodania Usera X. Jeżeli nie zrobię osobnych baz danych per firma i całości na osobnych subdomenach to jakie mam inne możliwości?

Kod
class Company(models.Model):
   name = models.CharField(...)
 
 
class UserType(models.Model):
   name = models.CharField(max_length=50)
   company = models.ForeignKey(Company, null=True)
   invitation_email_template = models.TextField(...)
   # other fields
 
 
class User(AbstractBaseUser):
   name = models.CharField(...)
   email = models.EmailField(...unique=True)
   company = models.ForeignKey(Company)
   user_type = models.ForeignKey(UserType)
   role = models.CharField(choices=USER_ROLE) # manager, company admin, employee
 
   USERNAME_FIELD = 'email'
   REQUIRED_FIELDS = []
Zapisane
« Odpowiedz #1 : 11:36 30/05/19 »
Guaz Online
Expert Python User

Zobacz profil
****

Reputacja: 69
Płeć: Mężczyzna
Wiadomości: 513


Ja bym to widział tak:

Na tabeli userów przy logowaniu, wprowadź kolumnę długim ciągiem bitowym (0, 1) która będzie reprezentować do których firm będzie miał dostęp user o danym e-mailu. Maska zależnie od uprawnień powinna dawać dostęp do określonych działów firm.

Jak user już znajduje się na łączonej tabeli (wszystkich userów), to aktualizujesz jego maskę, defakto jeśli dane tylko raz musi podawać przy dostępie do pierwszej, to może wszystko być zamknięte we wspólnej tabeli. Jeśli te dane są unikalne dla każdej firmy (jak na przykład stanowisko), to doradzałbym dla poszczególnych firm osobne tabele które te dane będą trzymać, powiązane kluczem głównym z kolumną loginów na głównej tabeli userów, bo tam najpierw się musi znaleźć user, a później mieć odpowiednią wartość w masce Uśmiech

Przynajmniej ja bym to widział w taki sposób, może ci to pomoże/da jeszcze inny pomysł Chichot
Zapisane

Python 3.5+ / Mint

Daje wędkę zamiast ryby. Chyba że ktoś się chce czegoś nauczyć, wtedy chętnie pomogę każdemu.
Za rybę niestety trzeba zapłacić Z politowaniem.
« Odpowiedz #2 : 06:10 01/06/19 »
python_django Offline
Hello World!

Zobacz profil
*

Reputacja: 1
Wiadomości: 8


Idea: firma może zaprosić użytkowników(requesterów) którzy mogą zgłaszać swoje problemy związane z użytkowaniem biura/całego budynku, następnie company_admin lub approver może zatwierdzić pomysł lub odrzucić.
 
Podstawowe role użytkowników: company_admin, approver, requester.
 
Firmy oraz pierwszy admin firmy są tworzone ręcznie z poziomu panelu admina.
 
Każdy firma może utworzyć wiele typów requestera np. employee, contractor, conference speaker (mój model UserType w którym są przechowywane ustawienia dla poszczególnych typów, np. szablon email, limity itp.). Dlatego jest tam Company na UserType @Tomasz Pycia
 
 
Case: Firma Google tworzy typ requestera "contractor", następnie w formularzu "zaproś użytkownika" wpisuje adres email Johna, imię i nazwisko i wybiera z select box typ requestera(model UserType), klika przycisk zaproś. Wszystko pięknie dopóki John nie zacznie pracować w firmie Microsoft i company_admin Microsoftu nie będzie chciał zaprosić Johna.
 
Chciałbym uniknąć osobnej bazy per klient i osobnego schema oraz subdomen.
Zapisane
« Odpowiedz #3 : 12:32 01/06/19 »
raydeal Offline
Expert Python User

Zobacz profil
****

Reputacja: 85
Wiadomości: 503


Wydaje mi się, że dobrze do tego pasuje tabela pośrednia między Company a User.
Zapisane
« Odpowiedz #4 : 13:32 01/06/19 »
python_django Offline
Hello World!

Zobacz profil
*

Reputacja: 1
Wiadomości: 8


Problemem jest też to, że User może mieć inny UserType w każdej firmie.
Zapisane
« Odpowiedz #5 : 14:55 01/06/19 »
Guaz Online
Expert Python User

Zobacz profil
****

Reputacja: 69
Płeć: Mężczyzna
Wiadomości: 513


A nie możesz w UserType trzymać listy kilkuelementowej na przykład?
Zwiększy to rozmiar, który musi być zarezerwowany.

Oczywiście jeśli jeszcze masz opcję dodawać takie modyfikacje. Ale to też tylko koncepcja, zależnie jak tam resztę w środku masz zorganizowaną.
Zapisane

Python 3.5+ / Mint

Daje wędkę zamiast ryby. Chyba że ktoś się chce czegoś nauczyć, wtedy chętnie pomogę każdemu.
Za rybę niestety trzeba zapłacić Z politowaniem.
« Odpowiedz #6 : 21:57 01/06/19 »
raydeal Offline
Expert Python User

Zobacz profil
****

Reputacja: 85
Wiadomości: 503


Problemem jest też to, że User może mieć inny UserType w każdej firmie.
Raczej nie może mieć bo ten sam user nie może być w kilku firmach
Cytuj
Dwie różne firmy nie mogą dodać użytkownika o tym samym adresie email.
Jeśli masz unique na email to tak jak by ten email był PK w tabeli User. Jak się teraz jeszcze raz przyjrzałem to Twoje modele przy tych założeniach są wystarczające, chyba że jednak czegoś nie rozumiem. Unique na email gwarantuje Ci że nie będziesz miał więcej takich użytkowników a on będzie miał jedną firmę więc będziesz miał zagwarantowane że jeden użytkownik o danym email będzie tylko w jednej firmie.
Zapisane
« Odpowiedz #7 : 23:03 01/06/19 »
python_django Offline
Hello World!

Zobacz profil
*

Reputacja: 1
Wiadomości: 8


@Guaz @raydeal dzięki za zainteresowanie

Cytuj
Unique na email gwarantuje Ci że nie będziesz miał więcej takich użytkowników a on będzie miał jedną firmę więc będziesz miał zagwarantowane że jeden użytkownik o danym email będzie tylko w jednej firmie.
Wysłane: Dzisiaj o 14:55

Chyba słabo to opisałem. Właśnie celem jest przerobienie tego w taki sposób żeby jeden użytkownik mógł być w dwóch różnych firmach (i oczywiście mięć różny UserType w każdej firmie)
Zapisane
« Odpowiedz #8 : 09:55 02/06/19 »
raydeal Offline
Expert Python User

Zobacz profil
****

Reputacja: 85
Wiadomości: 503


To tabela pośrednia powinna pomóc.
Modele wtedy byłyby:
Kod
class Company(models.Model):
   name = models.CharField(...)
 
 
class UserType(models.Model):
   name = models.CharField(max_length=50)
   company = models.ForeignKey(Company, null=True)
   invitation_email_template = models.TextField(...)
   # other fields
 
 
class User(AbstractBaseUser):
   name = models.CharField(...)
   email = models.EmailField(...unique=True)
   company = models.ManyToMany(Company, through='UserCompany')
 
class UserCompany:
   user = models.ForeignKey(User)
   company = models.ForeignKey(Company)
   user_type = models.ForeignKey(UserType)
   role = models.CharField(choices=USER_ROLE) # manager, company admin, employee
Zapisane
« Odpowiedz #9 : 16:27 12/06/19 »
pablo663 Offline
Hello World!

Zobacz profil
*

Reputacja: 0
Wiadomości: 5


@raydeal a tak nie jest łatwiej? Wtedy masz wszystkie możlwe połączenia user - firma w UserProfile i podpiętą tylko jedną firmę na raz do usera (FK). Dorabiasz jakiś switcher tak jak np. na Slacku. Switch to company X i wtedy tylko robisz przekierowanie na home page i zmieniasz wartość w:    
Kod
company = models.ForeignKey(UserCompany)
na wybraną firmę.

Kod
class Company(models.Model):
   name = models.CharField(...)
 
 
class UserType(models.Model):
   name = models.CharField(max_length=50)
   company = models.ForeignKey(Company, null=True)
   invitation_email_template = models.TextField(...)
   # other fields
 
 
class User(AbstractBaseUser):
   name = models.CharField(...)
   email = models.EmailField(...unique=True)
   company = models.ForeignKey(UserProfile)
 
class UserProfile:
   user = models.ForeignKey(User)
   company = models.ForeignKey(Company)
   user_type = models.ForeignKey(UserType)
   role = models.CharField(choices=USER_ROLE) # manager, company admin, employee
 
   class Meta:
       unique_together = ('user', 'company')

W appce wszystkie querysety opierasz o request.user.company Chyba łatwiej będzie Ci przerobić z istniejącego stanu appki (modele z pierwszego wpisu)

Może ktoś doświadczony oceni tę propozycję. Pzdr.
Zapisane
« Odpowiedz #10 : 20:35 12/06/19 »
raydeal Offline
Expert Python User

Zobacz profil
****

Reputacja: 85
Wiadomości: 503


@pablo663 moim zdaniem nie ma to za bardzo sensu.

No może w jakiś bardzo wyjątkowych przypadkach...

W tym rozwiązaniu masz relacje UserCompany->User->UserCompany co jest pewnego rodzaju "zapętleniem" relacji w modelach. W tym przypadku będziesz miał problem z dodaniem Usera bo musisz podać wartość dla company a nie możesz tego zrobić bo jeszcze nie masz UserCompany i nie będziesz miał dopóki nie wstawisz User. Pole User.company musiałbyś zrobić nie wymagane, a to nie jest dobre.

Taki sam efekt można osiągnąć przez dodanie pola np. typu Boolean do UserCompany które będzie informować która firma jest używana.
Zapisane
« Odpowiedz #11 : 13:14 14/06/19 »
python_django Offline
Hello World!

Zobacz profil
*

Reputacja: 1
Wiadomości: 8


btw. czy są jakieś strategie migracji danych np. ze struktury modeli z pierwszego wpisu do tego zaproponowanego przez @raydeal ? Z góry dzięki za każdy protip.

Kod
class Company(models.Model):
   name = models.CharField(...)
 
 
class UserType(models.Model):
   name = models.CharField(max_length=50)
   company = models.ForeignKey(Company, null=True)
   invitation_email_template = models.TextField(...)
   # other fields
 
 
class User(AbstractBaseUser):
   name = models.CharField(...)
   email = models.EmailField(...unique=True)
   company = models.ManyToMany(Company, through='UserCompany')
 
class UserCompany:
   user = models.ForeignKey(User)
   company = models.ForeignKey(Company)
   user_type = models.ForeignKey(UserType)
   role = models.CharField(choices=USER_ROLE) # manager, company admin, employee
Zapisane
« Odpowiedz #12 : 18:47 14/06/19 »
raydeal Offline
Expert Python User

Zobacz profil
****

Reputacja: 85
Wiadomości: 503


Jeśli stawiasz nową strukturę w innej bazie to jest to w miarę proste, bo część danych z tabeli User przenosisz do UserCompany metodą dowolną. Reszta pozostaje bez zmian.
W zależności jak dużo tabel i jak powiązane to albo robisz backup pozostałych tabel (bez User i UserCompany) i import albo używając Pythona przenosisz dane z tabeli do tabeli.

Jeśli chodziło Ci o przeniesienie danych w ramach tej samej bazy przy wykonywaniu migracji Djangowych to mocno się to komplikuje. Trzeba robić to w kilku krokach uważając żeby nie stracić danych.
Zapisane
Strony: [1]   Do góry
Drukuj
Skocz do:  

© 2007 - 2019 Polish Python Coders Group
Powered by SMF 1.1.21 | SMF © 2006-2009, Simple Machines | Theme by PixelSlot