Как получить доступ к request.user в def save() в модели?

Я хочу создать пользователя при создании объекта. Этот объект связан с пользователем внешним ключом. Я переопределил метод def_save(), чтобы создать пользователя и связать его с объектом.

Проблема: я генерирую случайный пароль для этого пользователя и хочу отправить его по электронной почте не только что созданному пользователю, а пользователю.

    def save(self, *args, **kwargs):
        if self._state.adding:
            super(Machine, self).save(*args, **kwargs)
            username = f"machine_{slugify(self.site.client.name).lower()}_{self.id}"
            password = User.objects.make_random_password()
            self.user = User.objects.create(
                username=username,
                password=password
            )
            
            self.save(update_fields=['user'])

            send_mail(
                f'Password of {username}',
                f'Password: {password}',
                settings.DEFAULT_FROM_EMAIL,
                [self.request.user.email],
                fail_silently=True,
            )
        else:
            super(Machine, self).save(*args, **kwargs)

Проблема в том, что у меня нет доступа к self.request в этом методе. Как я могу получить доступ к request в моем def save()?

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

Я не думаю, что переопределение метода save_model - это лучший вариант. Представьте, например, что вы хотите сохранить информацию о пользователе или проверить модель на основе информации о пользователе, и что save() не исходит из представления или самого админсайта.

Что люди спрашивают, так это конструкции, подобные этой:

def save(..)
    self.user = current_user()

или

def save(..)
    user = current_user()
    if user.group == 'XPTO':
        error('This user cannot edit this record')

Лучший подход, который я нашел до сих пор, следующий:

здесь

Я думаю, что вы должны разработать это по-другому.

Если всегда есть представление, это говорит о том, что единственное законное место, где может быть создан этот объект и связанный с ним пользователь, находится внутри конкретного представления. Поэтому используйте get_or_create и если он был создан, то вызовите логику для создания и связывания нового пользователя и отправки пароля текущему пользователю Django.

Вы можете защитить его от создания объектов вне соответствующего представления, используя вместо этого

try:
    existing_instance = MyModel.objects.get( ...)
except MyModel.DoesNotExist
    new = MyModel( ...)
    # create and associate the User object here

    setattr( new, '_foo_bar', 'not_Molly')   # a Mollyguard
    new.save()

и проверьте в методе сохранения MyModel, что self._foo_bar присутствует и корректен. В противном случае выведите значимую ошибку. Это позволит избежать случайного создания экземпляров MyModel без связанного с ними Пользователя, скажем, вновь нанятыми сотрудниками, которые не до конца понимают, к чему это может привести.

Если очень-очень хочется, можно было бы передать текущее request.User в качестве значения атрибута, и проверить isinstance( self._foo_bar, User), а затем, выйдя из строя, если у вас нет валидного User, поместить логику в метод сохранения. Мне кажется, что это неправильно.

Чтобы ответить на ваш вопрос напрямую (я определенно думаю, что вам также следует прочитать предложения по проектированию здесь), но чтобы получить объект запроса на протяжении всего цикла запроса, одним из решений является threadlocals. Промежуточное ПО Threadlocals помещает объект запроса в потокодоступное хранилище, а затем предоставляет обработчик get_current_request, который вы можете импортировать куда угодно и взять запрос из локального хранилища

Здесь много предостережений: Разработчики ядра Django намеренно не включили эту функциональность, здесь есть отличное обсуждение того, почему вы не должны этого делать, Python не является 100% потокобезопасным, это может быть (и, вероятно, является) анти-паттерном, и рассмотрите случаи, приведенные в этой теме.

Вернуться на верх