Reputation: 802
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
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