Reputation: 6107
Is it possible to call the same PageTemplate multiple times within one doc.build?
For example if you have a PageTemplate for orders that uses order instance attributes in the header and footer, how would you go about creating a PDF of all orders for a given date?
class OrderTemplate(BaseDocTemplate):
def __init__(self, filename, **kwargs):
self.order = kwargs['order']
BaseDocTemplate.__init__(self, filename **kwargs)
main_frame = Frame(0, 0, self.pagesize[0], self.pagesize[1], id='main_frame')
template = PageTemplate(id='header_footer_frame', frames=[main_frame],
onPage=self.header_footer)
self.addPageTemplates([template])
# title
canvas.setFont('Helvetica', 12)
company_name = self.order.name.upper()
first_row = canvas.beginText(0, doc.pagesize[1] - 10)
first_row.textLine(company_name)
canvas.drawText(first_row)
class Report:
@staticmethod
def build_pdf(**kwargs):
buffer = BytesIO()
doc = OrderTemplate(buffer, pagesize=kwargs['pagesize'],
title=kwargs['page_title'],
order=kwargs['order'])
doc.build(kwargs['story'], canvasmaker=NumberedCanvas)
# Get the value of the BytesIO buffer and write it to the response.
pdf = buffer.getvalue()
buffer.close()
return pdf
@staticmethod
def build_order(**kwargs):
orders = kwargs['orders']
pagesize = letter
# Flow-able content
story = []
for order in orders:
lines = OrderLine.objects.filter(order=order).order_by('pk')
story.append(table)
story.append(PageBreakIfNotEmpty())
return Report.build_pdf(pagesize=pagesize, page_title=page_title,
story=story, template=template, order=order)
The build_order()
method generates a page for each order with the correct associated lines but the header/footer is the same across each page. I need to somehow call the PageTemplate for each loop and pass it the new order kwargs.
Upvotes: 1
Views: 618
Reputation: 6107
Here's the solution I arrived at based on the use of chapters in ReportLab's user guide rltemplate.py
class OrderTemplate(PageTemplate):
def __init__(self, id, pagesize=letter):
frame = Frame(0, 0, pagesize[0], pagesize[1], id='main_frame')
PageTemplate.__init__(self, id, [frame])
def afterDrawPage(self, canvas, doc):
canvas.saveState()
canvas.setPageSize(doc.pagesize)
canvas.setTitle(doc.title)
if doc.order:
# title
canvas.setFont('Helvetica', 12)
company_name = doc.order.name.upper()
first_row = canvas.beginText(0, doc.pagesize[1] - 10)
first_row.textLine(company_name)
canvas.drawText(first_row)
canvas.restoreState()
class BatchPrintTemplate(BaseDocTemplate):
def __init__(self, filename, **kwargs):
self.orders = kwargs['orders']
self.order_iter = iter(self.orders)
BaseDocTemplate.__init__(self, filename, **kwargs)
def beforeDocument(self):
self.order = next(self.order_iter)
def afterInit(self):
self.addPageTemplates(OrderTemplate('Order', pagesize=self.pagesize))
def afterFlowable(self, flowable):
if hasattr(flowable, 'new_order'):
try:
self.order = next(self.order_iter)
except StopIteration:
pass
When building your flowables you have to mark each page break with a new_order
attribute. Setting the next order to use in the header is handled by afterFlowable
.
class BatchPrint:
def build_pdf(**kwargs):
buffer = BytesIO()
doc = BatchPrintTemplate(buffer, pagesize=kwargs['pagesize'], title=kwargs['page_title'],
orders=kwargs['orders'])
doc.build(kwargs['story'])
# Get the value of the BytesIO buffer and write it to the response.
pdf = buffer.getvalue()
buffer.close()
return pdf
def build_orders(**kwargs):
story = []
for order in orders:
## build your flowables here ##
story.append(table)
story.append(PageBreak())
story[-1].new_order = True
return BatchPrint.build_pdf(pagesize=pagesize, page_title=page_title, story=story, orders=orders)
Upvotes: 1