Search code examples
pythondjangoemailxhtml2pdf

xhtml2pdf - Creating a pdf and emailing as an attachment


I am trying to create a pdf of a template and then have it automatically send an email to the contractor with the created pdf as an attachment.

Emails: I am able to send basic email without any issues

Create PDF: I am able to create the pdf with out any issues.

The issue is trying to combine the two features. I am not sure if I have to have the pdf automatically save to a location on the server and then attach the pdf to an email or if I can create the pdf and then email out in the same code.

def vendorwopdf(request, *args, **kwargs):
    wonumb = kwargs.get('WorkOderNumb')
    email = None
    
    searchresult = WorkOrders.objects.get(WorkOderNumb=wonumb)
    
    template = get_template("workorders/workorderpdf.html")
    html = template.render({"searchresult":searchresult})
    result = io.BytesIO()
    pdf = pisa.CreatePDF(html, result)
    result.seek(0)
    result = result.read()
    pdf_content = base64.b64encode(result).decode()

    
    email = EmailMessage(
    "RE: Authorized Workorder " + wonumb ,
    "Greetings please find attached the approved workorder",
    "[email protected]",
    ["[email protected]"],
    )
    email.attach("workorder.pdf", pdf_content, "pdf")
    
    email.send()

    return render(request,'workorders/vendorwo.html',{"searchresult":searchresult})

template

{% load static from static %}

<html>
    <head>
        <meta charset='utf-8'>
        <title>Work Order Invoice</title>
    <style type="text/css">
        th,td{
        padding:3px;
        }
        th{
        background-color:white;
        color:black;
        }
    
    @page {
            size: letter portrait;
            @frame header_frame{
                -pdf-frame-content:header_content;
                left:50pt;
                width:512pt;
                top:15pt;
                height:100pt;           
            }
            @frame footer_frame{
                -pdf-frame-content:footer_content;
                left:50pt;
                width:512pt;
                top:725pt;
                height:100pt;       
            }
            @frame content_frame{
                left:50pt;
                width:512pt;
                top:100pt;
                height:580pt;
            }
            
             
    }
    
    </style>
    
    </head>
<body>
<div id="header_content">
    <table>
        <center><h2> </h2></center>
        <tr>
            <th align="left"><img src="http://999.999.1.183:8000/static/rclogo.jpg" alt="image" width="250" height="80" /></th>
            <th></th>
            <th align="left"><p><center><h4>WORKORDER #: {{ searchresult.WorkOderNumb }}<br>DATE CREATED: {{ searchresult.DateCreated }}<br>VENDOR: {{ searchresult.Vendor }} </center></h4>
            </th>
            <th align="Left">PRIORITY: <u>{{ searchresult.Priority }}</u> <br>      </th>
        </tr>
    </table>
    <hr>      
</div>
<br>       
<div id="content_content">
    <div class="container"> 
    <div class="row justify-content-between rounded" style="margin-left:auto; margin-right:auto;"">
        <table>
            <center><h2> </h2></center>
            <tr>
                <th align="left">
                    <h3><u>PROPERTY DETAILS </u> </h3>
                    <p>{{ searchresult.PropertyName }} <br>
                    {{ searchresult.CityLocality }}, {{ searchresult.Province }} <br>
                    {{ searchresult.PostalCode }} </p>
                </th>
                <th>
 
            
                </th>
                <th align="Left">
                    <h3><u>CONTACT DETAILS</u> </h3>
                    <p>{{ searchresult.ContactInfo|safe|linebreaks}} </p>
                </th>
            </tr>
        </table>
    </div>
    <br>
    <hr>
    <br>
    <div class="row justify-content-between rounded" style="margin-left:auto; margin-right:auto;"">

    </div>
    
    <hr>
    
    <div class="row justify-content-between rounded" style="margin-left:auto; margin-right:auto;"">
        <div class="col-md-12">
        <div class="border bg-light">   
        <br>
        <h4><u>DESCRIPTION OF SERVICE/REPAIRS REQUIRED</u></h4>
        <p>{{ searchresult.Description|safe|linebreaks }} </p>
        </div>
        </div>
    </div>
    
    <div class="row justify-content-between rounded" style="margin-left:auto; margin-right:auto;"">
        <p><br></p>
    </div>
    
    <div class="row justify-content-between rounded" style="margin-left:auto; margin-right:auto;"">
        <div class="col-md-12">
        <div class="border bg-light">   
        <h4><u></u></h4>
        <h4><u>Terms of Acceptance</u></h4>
        {% if searchresult.StudentRental == True %}
        
        {% endif %}
        </div>
        </div>
    </div>
    
</div>
</body>
</html>

This view will produce the working pdf but I can not attach to email.


def vendorwopdf(request, *args, **kwargs):
    wonumb = kwargs.get('WorkOderNumb')
    
    searchresult = WorkOrders.objects.get(WorkOderNumb=wonumb)
    
    template = get_template("workorders/workorderpdf.html")
    context = {'searchresult': searchresult}
    response = HttpResponse(content_type='application/pdf')
    #response['Content-Disposition'] = 'attachment; filename="workorder.pdf"'
    response['Content-Disposition'] = 'filename="workorder.pdf"'
    template = get_template(template_path)
    html = template.render(context)

   
    pisa_status = pisa.CreatePDF(
        html, dest=response)
    if pisa_status.err:
        return HttpResponse('We had some errors <pre>' + html + '</pre>')
    return response

Solution

  • You can do something like this to only send the attachment in the mail without saving it in any database.

    import io
    from django.template.loader import get_template
    from xhtml2pdf import pisa
    from django.core.mail import EmailMessage
    
    
    template = get_template("template.html")
    html = template.render({"context": context_data})
    result = io.BytesIO()
    pdf = pisa.CreatePDF(html, result)
    result.seek(0)
    result = result.read()
    pdf_content = base64.b64encode(result).decode()
    
    

    After that, you can attach the pdf file to your email:

    email = EmailMessage(
        subject,
        body,
        from_mail,
        to_mail,
        cc,
        bcc,
    )
    email.attach("pdf_name", pdf_content, "pdf")
    

    Notice the encoding at last, that is because the pdf content sent in as an attachment should be base64 encoded, or else it'll throw an error.