On 5/02/2020 10:14 am, Zameer Ahmed wrote:
> Thanks Mike for taking time off to read long question and response is
> much appreciated.
> In all the discussion you just told me what not to do for laying down
> my model structure.
> Now I have idea somewhat not to do.
> Can you kindly laydown minimal structure where I can achieve multiple
> user roles?
There are as many different ways to do that as there are use-cases.
For my purposes (not necessarily anyone else's) I have a single user
with a user_profile to store 1:1 information such as preferences when
using the software or cellphone for MFA or (in my case) their company.
That is interesting because I decided early that for security reasons a
single login would only ever work for a single company. My software is
tenant-based with multiple companies. Users who work for multiple
companies must have multiple logins.
My basis for this is it seems to me to be the simplest and most flexible
for my purposes. Also, it means I can lift that entire section of my
code and use it virtually unchanged in other projects.
Everyone who uses the software is a human and all that is different
is(are) the role(s) they inhabit. In my software people have defined
roles, they can switch to have other roles and they can have multiple
roles at the same time.
The roles are named django_auth_groups which each get appropriate
permissions. A user needs to be in multiple groups if they need more
permissions than are available in a single group.
I have no users with their own permissions. They can only get their
permissions from group membership. I have omitted user individual
permissions entirely from the project. They are evil.
Then in a utils module I have a bunch of easy to remember functions like
this ...
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
#
def is_admin(user, name="admin"):
return is_member(user, name)
def is_author(user, name="author"):
return is_member(user, name)
def is_authority(user, name="authority"):
return is_member(user, name)
def is_consumer(user, name="consumer"):
return is_member(user, name)
def is_editor(user, name="editor"):
return is_member(user, name)
def is_manager(user, name="manager"):
return is_member(user, name)
#
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
#
def is_member(user, name):
if user and name and user.is_active and user.is_staff:
return user.groups.filter(name=name).exists()
#
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
I also have similar methods on the user model itself which return
booleans. Also one which returns a string of all the groups a user is a
member of.
So my experience may not suit your use-case but it suits me because it
is simple and flexible.
I have not extended the custome user much beyond extra methods. No extra
fields. I think a user_profile in a 1:1 relationship offers all that you
probably need for the stuff which is exclusively human-user-related.
Different user profile tables for different roles might be exactly what
you need or they might be overkill.
Maybe the way to think about it is to abstract the commonalities into a
single (ie simpler) user_profile table and then really examine the
differences and decide how best to deal with them in the context of what
you want the software to do.
That might be roles the way I do it. It might be multiple profile tables
which inherit from the common one.
I sometimes wonder how I might explain my project design to someone who
is taking it over and that usually stops all my complex ideas and forces
me to look for something closer to the real world which will be easier
to explain and if you code with real world language it will be easier to
hand over. And to understand the next time you look at it yourself!
Good luck
Mike
>
> I just need to extend user properly and remaining part I got
> figured out from docs for many2many and many2one relations.
>
> Best regards,
> Zaheer
>
> On Mon, Feb 3, 2020 at 6:59 PM Mike Dewhirst <miked@dewhirst.com.au
> <mailto:miked@dewhirst.com.au>> wrote:
>
> On 4/02/2020 10:41 am, Zameer Ahmed wrote:
> > Hi,
> > I've also posted this question on StackoverFlow and haven't got any
> > response surprisingly. Here is the link
> >
> <https://stackoverflow.com/questions/60048405/django-extending-models-with-multi-role-users-and-multiple-requirement-types>.
> >
> > I am new to Django only 1 week and I am stuck with scenario I need
> > help with.
> > This is kind of core concept and I apologize in advance for my
> lack of
> > knowledge.
> > I've extended the base user model like below with multiple
> roles. Now
> > each role has distinct requirements for their profiles.
> > I need help to understand that how to extend Students or Staff
> > further. There are two scenario I need to extend them.
> >
> > 1. Both can have similar extended models like addresses below.
> > 2. Both can have different extended models like Students have
> > CurrentCourse and Staff does not. Staff has Salary model and
> Students
> > does not.
> >
> > class User(AbstractUser):
> > is_student = models.BooleanField(default=True)
> > is_staff = models.BooleanField(default=True)
>
> is_staff already exists in AbstractUser for purposes of controlling
> whether the user may login to the Admin site. If you really need the
> concept it may be a good idea to think of a different name for your
> field to avoid possible confusion later.
>
> > class Student(models.Model):
> > user = models.OneToOneField(User, on_delete=models.CASCADE,
> null=True)
> > dob = models.CharField(max_length=30, blank=True)
> > location = models.CharField(max_length=30, blank=True)
>
> Having such a relationship makes User.is_student redundant. I
> wouldn't
> have is_student at all. Likewise is_staff.
>
> > class CurrentCourse(models.Model):
> > student = models.OneToOneField(Student,
> on_delete=model.CASCADE)# Can
> > I extend it like this or do i need some other approach?
>
> Not sure what you mean by CurrentCourse. If there are many
> students and
> many courses you probably want a ManyToManyField between (I presume)
> Course and User. The name CurrentCourse indicates to me that a
> student
> can be doing only one course (of many) at a time. Without knowing
> your
> intentions it is difficult to say much more.
>
>
> > ....
> >
> > class Staff(models.Model):
> > ROLES = (('Teacher', 'Teacher'), ('Professor', 'Professor'),
> > ('Administration', 'Administration'))
>
> You have omitted 'Tutor' and that might merge student and staff.
> However, you have the option to connect both Student and Staff to
> User
> so you retain flexibility to let students also join the staff.
>
> > user = models.OneToOneField(User, on_delete=models.CASCADE,
> null=True)
> > role = models.CharField(max_length=100, choices=ROLES)
> > website = models.CharField(max_length=100, blank=True)
> >
> > # Both types of Users can have multiple addresses and both Students
> > and Staff needs addressess, not sure how to extend this with
> ForeignKey.
>
> Just think about the real world. Do people share addresses? Do people
> have multiple addresses? Are students and staff both Users?
>
> Students and staff are both users so on the Address model you
> might want
> a ManyToManyField pointing to User.
>
> I quite like ManyToManyField because the through table which is
> automatically created can be adjusted to contain additional
> information
> which describes the relationship. For example, a student might have a
> local residential and a separate postal address as well as a more
> distant parental address. The enhanced through table would be
> useful for
> differentiating between them.
>
> If a User can have only one address then you need a ForeignKey on
> User
> which points to an address.
>
> >
> > class Address(models.Model):
> > street = models.CharField(max_length=200)
> > city = models.CharField(max_length=50)
> > country = models.CharField(max_length=50)
> >
> > class Salary(models.Model):
> > staff = models.OneToOneField(Staff, on_delete=models.CASCADE)
> > current_salary = models.DecimalField(max_digits=10,
> decimal_places=2)
> > Finally please let me know how can I apply validators on each model
> > for instance I did sub-classed 'User' to all models instead of
> Student
> > or Staff. How to apply a validator on OneToOneField like below:
> >
> > class Salary(models.Model):
> > staff = models.OneToOneField(User, on_delete=models.CASCADE,
> > validators=[some-validator])
> >
> >
> > Thank you in advance for your kind help for this.
> >
> > Best Regards,
> >
> > Zaheer
> > --
> > You received this message because you are subscribed to the Google
> > Groups "Django users" group.
> > To unsubscribe from this group and stop receiving emails from
> it, send
> > an email to django-users+unsubscribe@googlegroups.com
> <mailto:django-users%2Bunsubscribe@googlegroups.com>
> > <mailto:django-users+unsubscribe@googlegroups.com
> <mailto:django-users%2Bunsubscribe@googlegroups.com>>.
> > To view this discussion on the web visit
> >
> https://groups.google.com/d/msgid/django-users/51552bea-877b-4c66-8a7a-42a68ca761e5%40googlegroups.com
>
> >
> <https://groups.google.com/d/msgid/django-users/51552bea-877b-4c66-8a7a-42a68ca761e5%40googlegroups.com?utm_medium=email&utm_source=footer>.
>
> --
> You received this message because you are subscribed to the Google
> Groups "Django users" group.
> To unsubscribe from this group and stop receiving emails from it,
> send an email to django-users+unsubscribe@googlegroups.com
> <mailto:django-users%2Bunsubscribe@googlegroups.com>.
> To view this discussion on the web visit
> https://groups.google.com/d/msgid/django-users/84e69a79-d0bc-7fe4-8a6b-e757352c27ee%40dewhirst.com.au.
>
> --
> You received this message because you are subscribed to the Google
> Groups "Django users" group.
> To unsubscribe from this group and stop receiving emails from it, send
> an email to django-users+unsubscribe@googlegroups.com
> <mailto:django-users+unsubscribe@googlegroups.com>.
> To view this discussion on the web visit
> https://groups.google.com/d/msgid/django-users/CAAUw0KoP_uMPyMv7aXrA1iwW0ESCwFqn2f8RHnvQR1k9W9%3DMQw%40mail.gmail.com
> <https://groups.google.com/d/msgid/django-users/CAAUw0KoP_uMPyMv7aXrA1iwW0ESCwFqn2f8RHnvQR1k9W9%3DMQw%40mail.gmail.com?utm_medium=email&utm_source=footer>.
--
You received this message because you are subscribed to the Google Groups "Django users" group.
To unsubscribe from this group and stop receiving emails from it, send an email to django-users+unsubscribe@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/django-users/89352e4b-d5ae-0d8a-4338-84089711bed8%40dewhirst.com.au.
No comments:
Post a Comment