如何使用 Celery(无第三方包)发送 Django 的密码重置邮件?

How to send Django's password reset email using Celery (without third-party package)?

首先我知道这个问题之前已经回答过 which use a third-party package django-celery-email,但我想弄清楚如何在不依赖任何第三方库的情况下完成这样的工作。

所以我需要用 Celery 异步发送密码重置邮件。

我的 forms.py 文件如下所示:

from django import forms
from accounts.tasks import send_mail
from django.contrib.auth.forms import PasswordResetForm as PasswordResetFormCore


class PasswordResetForm(PasswordResetFormCore):
    email = forms.EmailField(max_length=254, widget=forms.TextInput(
        attrs={
            'class': 'form-control',
            'id': 'email',
            'placeholder': 'Email'
        }
    ))

    def send_mail(self, subject_template_name, email_template_name,
                  context, from_email, to_email, html_email_template_name=None):
        """
        This method is inherating Django's core `send_mail` method from `PasswordResetForm` class
        """
        super().send_mail(subject_template_name, email_template_name,
                  context, from_email, to_email, html_email_template_name)

我正在尝试通过 CeleryPasswordResetForm class 的 send_mail 方法发送邮件。我的意思是用 Celery 调用 super().send_mail(...)。我的 Celerytasks.py 文件中也有一个 send_mail 函数,我试图将 super().send_mail 方法作为参数传递给它来调用它从那里开始。

现在我的 tasks.py 文件看起来像这样:

from __future__ import absolute_import, unicode_literals


@shared_task
def send_mail():
    pass

我将 RabbitMQCelery

一起用作消息代理

好的,我已经找到了一个可行的解决方案。这是我的解决方案。

我已经改forms.py如下

from django import forms
from accounts.tasks import send_mail
from django.contrib.auth.forms import PasswordResetForm as PasswordResetFormCore


class PasswordResetForm(PasswordResetFormCore):
    email = forms.EmailField(max_length=254, widget=forms.TextInput(
        attrs={
            'class': 'form-control',
            'id': 'email',
            'placeholder': 'Email'
        }
    ))

    def send_mail(self, subject_template_name, email_template_name, context, 
                  from_email, to_email, html_email_template_name=None):
        context['user'] = context['user'].id

        send_mail.delay(subject_template_name=subject_template_name, 
                        email_template_name=email_template_name,
                        context=context, from_email=from_email, to_email=to_email,
                        html_email_template_name=html_email_template_name)

变化后的tasks.py如下

from __future__ import absolute_import, unicode_literals
from accounts.models import User
from django.contrib.auth.forms import PasswordResetForm


@shared_task
def send_mail(subject_template_name, email_template_name, context,
              from_email, to_email, html_email_template_name):
    context['user'] = User.objects.get(pk=context['user'])

    PasswordResetForm.send_mail(
        None,
        subject_template_name,
        email_template_name,
        context,
        from_email,
        to_email,
        html_email_template_name
    )

与上一个类似,但采用浓缩方式

forms.py

"""users/forms.py"""
from django.contrib.auth.forms import PasswordResetForm
from users.tasks import send_reset_password_email_async

class CustomPasswordResetForm(PasswordResetForm):
    """
    Extend Auth Form for adding support to Async email using Celery
    """
    # keys -> email, domain, site_name, uid, user, token, protocol
    context = args[2]
    context['user'] = context['user'].pk 
    send_reset_password_email_async.delay(*args, **kwargs)

tasks.py

"""users/tasks.py"""
from users.models import User
from celery import shared_task
from django.contrib.auth.forms import PasswordResetForm

@shared_task
def send_reset_password_email_async(*args, **kwargs):
    """
    Description
    -----------
    - User must be restore from None to object.
    - Send the reset password email as async task
    """
    # Restoring user
    context = args[2]
    context['user'] = User.objects.get(pk=context['user'])
    PasswordResetForm.send_mail(None, *args, **kwargs)

最后使用开头扩展的形式 urls.py

"""proj/urls.py"""
from django.urls import path, include,

urlpatterns = [
    # ...
        path(
        'password_reset/',
        auth_views.PasswordResetView.as_view(
            form_class=CustomPasswordResetForm,  # <-- async email support
            # ...
        ),
        name='password_reset'
    ),
    # ...
]

已测试!