Как добавить пользователя в группу при обновлении и создании пользователя

Я создал кучу групп по этой ссылке: https://stackoverflow.com/questions/22250352/programmatically-create-a-django-group-with-permissions#:~:text. Я подписал класс User, добавив поле выбора под названием "role". По сути, это означает, какую роль имеет пользователь. Таким образом, если пользователь имеет роль "персонал". Таким образом, он будет иметь права доступа к группе staff. Проблема в том, что я не могу заставить django реагировать так, как я ожидал. Я поместил сигнал после сохранения, что он должен добавить пользователя в группу в соответствии с его ролью. Программа выглядит следующим образом:

# 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"))

Мой сигнал выглядит так:

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")

Когда я перехожу на страницу администратора и создаю нового пользователя, все работает как ожидалось. Но когда я редактирую роль существующего пользователя, он просто печатает "update" перед функцией print, но ничего не меняется. Что я делаю не так?

В вашем текущем коде при изменении роли пользователя он будет добавлен в новую группу (без удаления из предыдущей). Например, если пользователь изначально входит в группу Staff, а затем его роль меняется на Company, он будет добавлен в группу Company. Поскольку отношения между моделью User и моделью Group являются отношениями типа "многие ко многим", пользователь не будет удален из группы Staff. Для этого есть несколько способов (которые в основном одинаковы):

Первый вариант с clear затем 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

Второй вариант с 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

При всем этом, если вы решите выбрать второй вариант, вы можете использовать set также для создаваемых пользователей:

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)

и поэтому вам не нужно использовать параметр created. Для получения дополнительной информации о clear и set вы можете прочитать документацию https://docs.djangoproject.com/en/4.0/ref/models/relations/#django.db.models.fields.related.RelatedManager.clear и посмотреть, как их можно использовать!

Я опубликую решение здесь на случай, если кто-нибудь столкнется с такой же проблемой. Вместе с вкладом @mtzd. Я нашел подсказку для решения, основанную на описании проблемы, которое было в этом посте: https://stackoverflow.com/a/1925784/18600263. Итак, сигнал pos_save будет выглядеть так:

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))
Вернуться на верх