如何在 Django 项目中将请求传递给 Celery 任务参数?
Posted
技术标签:
【中文标题】如何在 Django 项目中将请求传递给 Celery 任务参数?【英文标题】:How to pass request to Celery task parameter in Django project? 【发布时间】:2018-10-07 00:08:12 【问题描述】:我正在使用wkhtmltopdf
和 Django 生成 PDF 文件并将其通过电子邮件发送给某人。这是我的看法:
class ChallanEmail(AtomicMixin, View, LoginRequiredMixin):
template = "europarts/challan/email_template.html"
def get(self, request, **kwargs):
challan = Challan.objects.get(pk=kwargs['pk'])
ref_no = challan.ref_no
date = challan.created
recipient = challan.recipient
address = challan.recipient_address
challan_rows = ChallanRow.objects.filter(challan=challan)
context =
"ref_no": ref_no,
"date": date,
"recipient": recipient,
"address": address,
"challan_rows": challan_rows,
response = PDFTemplateResponse(
request=request,
template=self.template,
filename='challan_email.pdf',
context=context,
show_content_in_browser=True,
cmd_options='margin-top': 10,
'zoom': 1,
'viewport-size': '1366 x 513',
'javascript-delay': 1000,
'no-stop-slow-scripts': True,
)
file_path = os.path.join(settings.BASE_DIR, settings.MEDIA_ROOT, 'challan_email.pdf')
with open(file_path, 'wb') as f:
f.write(response.rendered_content)
subject = 'From Design Ace Limited'
body = self.request.GET.get('email_body', '')
from_email = 'Sorower Hossain <sorower@europartsbd.com>'
to = [''.format(self.request.GET.get('to_address'))]
attachment = os.path.join(settings.MEDIA_ROOT, 'challan_email.pdf')
send_email(subject, body, from_email, to, attachment)
return HttpResponseRedirect(reverse('europarts:challan_details', args=(kwargs['pk'],)))
正如您在最后看到的那样,我正在使用celery
发送带有任务(send_email
)的电子邮件。但是,最耗时的过程是 PDF 创建过程。这是执行此操作的代码部分:
response = PDFTemplateResponse(
request=request,
template=self.template,
filename='challan_email.pdf',
context=context,
show_content_in_browser=True,
cmd_options='margin-top': 10,
'zoom': 1,
'viewport-size': '1366 x 513',
'javascript-delay': 1000,
'no-stop-slow-scripts': True,
)
我面临的问题是,每当我尝试将所有参数传输到 celery 任务时,它都会显示错误,因为 request
不可序列化。如何将请求转移到我的 celery 任务并节省 PDF 生成时间?这个任务几乎要花三秒钟时间。
【问题讨论】:
您生成的 PDF 只是为了通过电子邮件发送? @SzymonP。是的,我必须发送一个附件。 【参考方案1】:如果您仅使用PDFTemplateResponse
生成pdf 文件并通过电子邮件发送(不是http 响应),您可以在celery 任务中模拟它。
你的 celery 任务,在<app_name>/tasks.py
文件中:
from django.test.client import RequestFactory
# other necessary imports
...
@shared_task
def create_pdf_and_send_email(template, context, email_body, to_address):
request = RequestFactory().get('') # any valid url
challan_rows = ChallanRow.objects.filter(pk__in=context['challan_rows'])
context['challan_rows'] = challan_rows
response = PDFTemplateResponse(
request=request,
template=template,
filename='challan_email.pdf',
context=context,
show_content_in_browser=True,
cmd_options='margin-top': 10,
'zoom': 1,
'viewport-size': '1366 x 513',
'javascript-delay': 1000,
'no-stop-slow-scripts': True,
)
file_path = os.path.join(settings.BASE_DIR, settings.MEDIA_ROOT, 'challan_email.pdf')
with open(file_path, 'wb') as f:
f.write(response.rendered_content)
subject = 'From Design Ace Limited'
body = email_body
from_email = 'Sorower Hossain <sorower@europartsbd.com>'
to = [''.format(to_address)]
attachment = os.path.join(settings.MEDIA_ROOT, 'challan_email.pdf')
send_email(subject, body, from_email, to, attachment)
还有你重构的get
方法:
from .tasks import create_pdf_and_send_email
...
def get(self, request, **kwargs):
challan = Challan.objects.get(pk=kwargs['pk'])
ref_no = challan.ref_no
date = challan.created
recipient = challan.recipient
address = challan.recipient_address
challan_rows = ChallanRow.objects.filter(challan=challan).values_list('pk', flat=True)
context =
"ref_no": ref_no,
"date": date,
"recipient": recipient,
"address": address,
"challan_rows": challan_rows,
email_body = self.request.GET.get('email_body', '')
to_address = self.request.GET.get('to_address')
create_pdf_and_send_email.delay(template, context, email_body, to_address)
return HttpResponseRedirect(reverse('europarts:challan_details', args=(kwargs['pk'],)))
我希望它应该有效。
如果出现序列化错误的任何问题(可能与context
变量中的address
字段有关),请在get
方法中将address = challan.recipient_address
更改为address = challan.recipient_address.pk
。
然后,在 celery 任务中,使用给定的 pk 检索地址(例如 address = YourAddressModel.objects.get(pk=context['address'])
,并将 context['address']
替换为 context['address'] = address
。
【讨论】:
使用RequestFactory
解决了我的问题,但是,现在它显示<QuerySet [<ChallanRow: 456>]> is not JSON serializable
。我猜我的查询集需要以不同的方式传递。
这是唯一的方法吗?为此目的以上是关于如何在 Django 项目中将请求传递给 Celery 任务参数?的主要内容,如果未能解决你的问题,请参考以下文章