I created a bunch of groups fallowing this link: https://stackoverflow.com/questions/22250352/programmatically-create-a-django-group-with-permissions#:~:text I subscribed the User class adding a choice field called "role". Basically, that means what role that user has. That way if a user has the role of "staff" . Thus It'll have permissions of the group staff. The problem is: I can't get django to respond the way I expected. I put a signal after saving that it should add the user to the group according to their role. The program looks like this:
# project/app/model.py
class User(AbstractUser):
class Roles(models.IntegerChoices):
SUPER = 0, _('SuperAdmins')
COMPANY = 1, _('Company')
UNITY = 2, _('Unity')
STAFF = 3, _('Staff')
role: Roles = models.IntegerField(choices=Roles.choices, default=Roles.STAFF, verbose_name=_("Role"))
My signal is like:
GROUPS = ['SuperAdmins', 'Company', 'Unity', 'Staff']
@receiver(post_save, sender=User)
def user(sender: User, instance: User, created: bool, **kwargs) -> None:
"""
This receiver function will set every staff pages that is created to the group staff.
:param sender: the model that will trigger this receiver
:param instance: the instance
:param created: if it was already created
:param kwargs:
:return: None
"""
if created:
group = Group.objects.get(name=GROUPS[instance.role])
instance.groups.add(group)
else:
group = Group.objects.get(name=GROUPS[instance.role])
instance.groups.add(group)
print("update")
When I go to the admin page and I create a new user, everything works as expected. But when I edit the role of an existing user, it just prints the "update" fron the print function but nothing changes. What am I doing wrong?
CodePudding user response:
With your current code, when changing the user role it will be added to a new group (without being removed from the previous one). For example if the user is part of the group Staff
initially and then its role changes to Company
it will be added to the Company
group. As the relationship between the User
model and the Group
model is a Many to Many relationship, the user won't be removed from the Staff
group. To do that there are to ways (which are basically the same):
First option with clear
then add
:
if created:
group = Group.objects.get(name=GROUPS[instance.role])
instance.groups.add(group)
else:
group = Group.objects.get(name=GROUPS[instance.role])
instance.groups.clear() # Dissasociates any group the user belonged to
instance.groups.add(group) # Adds the group
Second option with set
:
if created:
group = Group.objects.get(name=GROUPS[instance.role])
instance.groups.add(group)
else:
group = Group.objects.get(name=GROUPS[instance.role])
instance.groups.set([group], clear=True) # Dissasociates any group the user belonged to and sets the new group. clear=True is necessary here because otherwise the Group instances would be deleted
With all this, if you decide to go with the second option, you can go with set
also for users that are being created:
GROUPS = ['SuperAdmins', 'Company', 'Unity', 'Staff']
@receiver(post_save, sender=User)
def user(sender: User, instance: User, **kwargs) -> None:
"""
This receiver function will set every staff pages that is created to the group staff.
:param sender: the model that will trigger this receiver
:param instance: the instance
:param kwargs:
:return: None
"""
group = Group.objects.get(name=GROUPS[instance.role])
instance.groups.set([group], clear=True)
and thus you don't have to use the created
param. For more info on clear
and set
you can read the docs https://docs.djangoproject.com/en/4.0/ref/models/relations/#django.db.models.fields.related.RelatedManager.clear and see how they can be used!
CodePudding user response:
I'll post the solution here in case anyone encounters the same problem. Along with the contribution of @mtzd. I found a tip for the solution based on the description of a problem that was in this post: https://stackoverflow.com/a/1925784/18600263. So the pos_save signal will be:
from django.db import transaction
GROUPS = ['SuperAdmins', 'Company', 'Unity', 'Staff']
@receiver(post_save, sender=User)
def user(sender: User, instance: User, **kwargs) -> None:
group = Group.objects.get(name=GROUPS[instance.role])
transaction.on_commit(lambda: instance.groups.set([group], clear=True))