Djoser сброс пароля по электронной почте с помощью Celery
Я хочу отправить письмо со сброшенным паролем, используя Celery. Я пытаюсь переопределить метод reset_password
класса djoser.views.UserViewSet
:
class CustomUserViewSet(UserViewSet):
@action(["post"], detail=False)
def reset_password(self, request, *args, **kwargs):
serializer = self.get_serializer(data=request.data)
serializer.is_valid(raise_exception=True)
user = serializer.get_user()
if user:
send_reset_password_email.delay(
self.request,
{'user': user},
[get_user_email(user)]
)
return Response(status=status.HTTP_200_OK)
Но я получаю ошибку:
Задача Сельдерея:
@app.task(bind=True, default_retry_delay=5 * 60)
def send_reset_password_email(self, request, context, email):
try:
PasswordResetEmail(request, context).send(email)
except Exception as exc:
raise self.retry(exc=exc, countdown=60)
Что я уже пробовал:
Я попытался изменить сериализатор для задачи на 'pickle'
:
@app.task(bind=True, default_retry_delay=5 * 60, serializer='pickle')
Но я получаю новую ошибку:
Как я могу решить эту проблему?
Или как еще я могу это сделать?
Проблема решена!
Вместо запроса я передаю необходимые поля в контекст, а вместо пользователя - user.id:
@action(['post'], detail=False)
def reset_password(self, request, *args, **kwargs):
serializer = self.get_serializer(data=request.data)
serializer.is_valid(raise_exception=True)
user = serializer.get_user()
if user:
send_reset_password_email.delay(
{
'user_id': user.id,
'domain': request.get_host(),
'protocol': 'https' if request.is_secure() else 'http',
'site_name': request.get_host()
},
[get_user_email(user)]
)
return Response(status=status.HTTP_200_OK)
Это работает, потому что если вы не посылаете запрос на Password Reset E-mail, Djoser ищет необходимые поля в контексте:
class BaseEmailMessage(mail.EmailMultiAlternatives, ContextMixin):
def get_context_data(self, **kwargs):
ctx = super(BaseEmailMessage, self).get_context_data(**kwargs)
context = dict(ctx, **self.context)
if self.request:
site = get_current_site(self.request)
domain = context.get('domain') or (
getattr(settings, 'DOMAIN', '') or site.domain
)
protocol = context.get('protocol') or (
'https' if self.request.is_secure() else 'http'
)
site_name = context.get('site_name') or (
getattr(settings, 'SITE_NAME', '') or site.name
)
user = context.get('user') or self.request.user
else:
domain = context.get('domain') or getattr(settings, 'DOMAIN', '')
protocol = context.get('protocol') or 'http'
site_name = context.get('site_name') or getattr(
settings, 'SITE_NAME', ''
)
user = context.get('user')
context.update({
'domain': domain,
'protocol': protocol,
'site_name': site_name,
'user': user
})
return context
В задаче я получаю пользователя по id, который я передал в контекст:
@app.task(bind=True, default_retry_delay=5 * 60)
def send_reset_password_email(self, context, email):
try:
context['user'] = CustomUser.objects.get(id=context.get('user_id'))
PasswordResetEmail(context=context).send(email)
except Exception as exc:
raise self.retry(exc=exc, countdown=60)