首先,我知道这个问题之前已经被回答过here,它使用第三方包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)
我正在尝试通过 Celery 从
send_mail
类的 PasswordResetForm
方法发送邮件。我的意思是用 Celery调用
super().send_mail(...)
。我的 Celery的
send_mail
文件中还有一个 tasks.py
函数,我试图将 super().send_mail
方法作为参数传递以从那里调用它。
现在我的
tasks.py
文件看起来像这样:
from __future__ import absolute_import, unicode_literals
@shared_task
def send_mail():
pass
我使用
RabbitMQ
作为消息代理以及 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):
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
)
与上一个类似,但以精简的方式
表格.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)
任务.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)
最后使用开头扩展的形式 url.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'
),
# ...
]
已测试!
我的解决方案:
用户/表单.py
from django.contrib.auth.forms import PasswordResetForm
from django.template import loader
from core.tasks import async_send_mail
class PasswordResetFormWithAsyncEmail(PasswordResetForm):
def send_mail(
self,
subject_template_name,
email_template_name,
context,
from_email,
to_email,
html_email_template_name=None,
):
subject = loader.render_to_string(subject_template_name, context)
subject = ''.join(subject.splitlines())
body = loader.render_to_string(email_template_name, context)
if html_email_template_name is not None:
html_email = loader.render_to_string(html_email_template_name,
context)
else:
html_email = None
return async_send_mail.delay(
subject=subject,
message=body,
from_email=from_email,
recipient_list=[to_email],
html_message=html_email,
)
用户/urls.py
from django.contrib.auth.views import PasswordResetView
from django.urls import path
from users.forms import PasswordResetFormWithAsyncEmail
urlpatterns = [
path(
'password_reset/',
PasswordResetView.as_view(
template_name='users/password_reset_form.html',
form_class=PasswordResetFormWithAsyncEmail,
),
name='password_reset_form'
),
]
核心/tasks.py
from celery import shared_task, Task
from django.core.mail import send_mail
class BaseTaskWithRetry(Task):
autoretry_for = (Exception, KeyError)
retry_kwargs = {'max_retries': 5}
retry_backoff = True
retry_jitter = True
@shared_task(bind=True, base=BaseTaskWithRetry)
def async_send_mail(
self,
subject,
message,
from_email,
recipient_list,
html_message=None,
):
return send_mail(
subject=subject,
message=message,
from_email=from_email,
recipient_list=recipient_list,
html_message=html_message,
fail_silently=False,
)