Как передать запрос параметру задачи Celery в проекте Django? - PullRequest
0 голосов
/ 26 апреля 2018

Я использую 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'],)))

Как вы можете видеть в конце, я отправляю электронное письмо с заданием (send_email), используя celery. Однако наиболее трудоемким процессом является процесс создания 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},
    )

Проблема, с которой я сталкиваюсь, заключается в том, что всякий раз, когда я пытаюсь передать все параметры в задачу сельдерея, выдается ошибка, поскольку request не сериализуем. Как я могу перенести запрос в свою задачу сельдерея и сэкономить время при создании PDF? Эта задача занимает почти три секунды, что очень много.

1 Ответ

0 голосов
/ 26 апреля 2018

Если вы используете PDFTemplateResponse только для создания PDF-файла и отправки его по электронной почте (не в виде http-ответа), вы можете смоделировать его в задаче сельдерея.

Ваше задание на сельдерее, в <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'],)))

Я надеюсь, что это должно работать.

Если возникнут какие-либо проблемы с ошибками сериализации (возможно с полем address в переменной context), измените address = challan.recipient_address на address = challan.recipient_address.pk в методе get. Затем, внутри задачи сельдерея, получите адрес с указанным pk (например, address = YourAddressModel.objects.get(pk=context['address']), и замените context['address'] на context['address'] = address.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...