sarangkkl
sarangkkl

Reputation: 802

How to configure a button in Django Admin Change Form to Download PDF File

I have an order field in my models

class Order(models.Model):
    # ...
    order_number = models.CharField(max_length=20,blank=True, null=True)
    first_name = models.CharField(max_length=50,blank=True, null=True)
    last_name = models.CharField(max_length=50,blank=True, null=True)
    

    def full_name(self):
        return f'{self.first_name} {self.last_name}'
    
    
        
    def full_address(self):
        return f'{self.address_line_1} {self.address_line_2}'

    def __str__(self):
        return self.order_number  

based on the id of the url i have an endpoint /order/download-bill/{order.id}

So what i want in my admin panel i want the admin have a button on which he click and the url trigger which will automatically download the bill

Upvotes: 0

Views: 576

Answers (1)

Faisal Nazik
Faisal Nazik

Reputation: 2893

You need to configure a link callback URL for that specific purpose just configure a template to link the button.

Step 1: Need to configure a download_pdf_button.html to add a button in the admin

{% extends 'admin/change_form.html' %}
{% block submit_buttons_bottom %}
{{ block.super }}
<div class="submit-row">
    <input type="submit" value="Download PDF" name="_download_pdf">
</div>
{% endblock %}

Step 2. Configure change form Response of that Model Objects Inside admin.py Related Imports :

xhtml2pdf is an external Pypi Package to install it , pip install xhtml2pdf

Also, Configure link_callback in the same dir where admin.py is located. create a file link_callback.py and add this code.

import os
from django.conf import settings
from django.contrib.staticfiles import finders


def link_callback(uri, rel):
    """
    Convert HTML URIs to absolute system paths so xhtml2pdf can access those
    resources
    """
    result = finders.find(uri)
    if result:
        if not isinstance(result, (list, tuple)):
            result = [result]
        result = list(os.path.realpath(path) for path in result)
        path = result[0]
    else:
        sUrl = settings.STATIC_URL        # Typically /static/
        sRoot = settings.STATIC_ROOT      # Typically /home/userX/project_static/
        mUrl = settings.MEDIA_URL         # Typically /media/
        mRoot = settings.MEDIA_ROOT       # Typically /home/userX/project_static/media/

        if uri.startswith(mUrl):
            path = os.path.join(mRoot, uri.replace(mUrl, ""))
        elif uri.startswith(sUrl):
            path = os.path.join(sRoot, uri.replace(sUrl, ""))
        else:
            return uri

    # make sure that file exists
    if not os.path.isfile(path):
        raise Exception(
            'media URI must start with %s or %s' % (sUrl, mUrl)
        )
return path

Import them.

from django.template.loader import get_template
from django.http import HttpResponse
from xhtml2pdf import pisa             # import python module
from .link_callback import link_callback

Need to configure Model Admin

class OrderAdmin(admin.ModelAdmin):
    # ...
    change_form_template = "download_pdf_button.html"  # That will be the button that can be seen in the end of change form. 
    def response_change(self, request, obj):
        template_path = 'order_pdf.html' # template to be rendered in pdf
        context = {
        'order_number ': obj.order_number,
            # add more things for the template if needed
        }

        # Create a Django response object, and specify content_type as pdf
        response = HttpResponse(content_type='application/pdf')
        response['Content-Disposition'] = 'attachment; filename="OrderPDF.pdf"'
        # find the template and render it.
        template = get_template(template_path)
        html = template.render(context)            # close output file
        # create a pdf
        pisa_status = pisa.CreatePDF(
            html, dest=response, link_callback=link_callback)
        # if error then show some response
        if pisa_status.err:
            return HttpResponse('We had some errors <pre>' + html + '</pre>')
        return response

Step: 3 Configure the Download Template for the order

In this example, I used it as order_pdf.html

<!DOCTYPE html>
<html>
    <head>
     <title>Order PDF</title>
        
    </head>
<body>
 
<h1>{{ order_number }}</h1>
</body>

</html>

Now you will have a button at the end of the change form that will give a pdf file on click

Using a similar way you can configure it anywhere in django. If you don't need a button just want to plug inside a view. then you can easily reproduce that using that approach.

Upvotes: 1

Related Questions