如何在Django后台实现可下载的采购发票PDF功能?
在Django后台添加发票PDF下载功能
1. 安装依赖
推荐使用WeasyPrint,它支持将HTML模板转换为PDF,便于自定义发票样式:
pip install weasyprint
2. 创建发票HTML模板
在你的app目录下新建templates/invoice_template.html,编写带样式的发票结构,填充MainPurchases字段:
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>采购发票</title> <style> .invoice-container { width: 800px; margin: 0 auto; font-family: Arial, sans-serif; } .invoice-header { text-align: center; border-bottom: 2px solid #000; padding-bottom: 10px; } .invoice-details { margin-top: 20px; display: flex; justify-content: space-between; } .invoice-items { margin-top: 30px; width: 100%; border-collapse: collapse; } .invoice-items th, .invoice-items td { border: 1px solid #000; padding: 8px; text-align: left; } .total-section { margin-top: 20px; text-align: right; font-weight: bold; } </style> </head> <body> <div class="invoice-container"> <div class="invoice-header"> <h1>采购发票</h1> </div> <div class="invoice-details"> <div> <p><strong>供应商:</strong> {{ purchase.supplier.name }}</p> <p><strong>采购日期:</strong> {{ purchase.date_created|date:"Y-m-d H:i" }}</p> </div> <div> <p><strong>支付方式:</strong> {{ purchase.get_payment_method_display }}</p> <p><strong>已付金额:</strong> ${{ purchase.paid_amount }}</p> </div> </div> <table class="invoice-items"> <thead> <tr> <th>商品名称</th> <th>数量</th> <th>单价</th> <th>小计</th> </tr> </thead> <tbody> <tr> <td>{{ purchase.product.name }}</td> <td>{{ purchase.quantity }}</td> <td>${{ purchase.purchase_price }}</td> <td>${{ purchase.quantity|mul:purchase.purchase_price }}</td> </tr> </tbody> </table> <div class="total-section"> <p>总金额: ${{ purchase.quantity|mul:purchase.purchase_price }}</p> <p>未付金额: ${{ purchase.quantity|mul:purchase.purchase_price|sub:purchase.paid_amount }}</p> </div> </div> </body> </html>
需要自定义模板标签处理数值计算,在app目录下创建templatetags/invoice_tags.py:
from django import template register = template.Library() @register.filter def mul(value, arg): return value * arg @register.filter def sub(value, arg): return value - arg
3. 编写PDF生成函数
在app目录下创建utils.py,实现PDF生成逻辑:
from django.http import HttpResponse from django.template.loader import render_to_string from weasyprint import HTML def generate_invoice_pdf(purchase): # 渲染HTML模板 html_content = render_to_string('invoice_template.html', {'purchase': purchase}) # 生成PDF文件 pdf_file = HTML(string=html_content).write_pdf() # 构造下载响应 response = HttpResponse(content_type='application/pdf') response['Content-Disposition'] = f'attachment; filename="invoice_{purchase.id}.pdf"' response.write(pdf_file) return response
4. 在Django Admin中添加下载按钮
修改app目录下的admin.py,自定义Admin类并添加下载入口:
from django.contrib import admin from django.urls import reverse from django.utils.html import format_html from .models import MainPurchases from .utils import generate_invoice_pdf class MainPurchasesAdmin(admin.ModelAdmin): list_display = ('product', 'quantity', 'supplier', 'date_created', 'download_invoice') def download_invoice(self, obj): # 生成下载链接 url = reverse('admin:download_invoice', args=[obj.id]) return format_html('<a href="{}" class="button">下载发票PDF</a>', url) download_invoice.short_description = '操作' # 注册自定义URL def get_urls(self): from django.urls import path urls = super().get_urls() custom_urls = [ path('download-invoice/<int:pk>/', self.admin_site.admin_view(self.download_invoice_view), name='download_invoice'), ] return custom_urls + urls def download_invoice_view(self, request, pk): purchase = MainPurchases.objects.get(pk=pk) return generate_invoice_pdf(purchase) admin.site.register(MainPurchases, MainPurchasesAdmin)
5. 测试功能
启动Django服务器,进入Admin后台的MainPurchases列表页,每条数据后会显示「下载发票PDF」按钮,点击即可生成并下载包含指定字段的PDF发票。
如果不想用HTML模板,也可以用reportlab直接绘制PDF,示例如下:
from django.http import HttpResponse from reportlab.pdfgen import canvas from reportlab.lib.pagesizes import letter def generate_invoice_pdf(purchase): response = HttpResponse(content_type='application/pdf') response['Content-Disposition'] = f'attachment; filename="invoice_{purchase.id}.pdf"' p = canvas.Canvas(response, pagesize=letter) width, height = letter # 绘制发票内容 p.drawString(100, height-100, "采购发票") p.drawString(100, height-130, f"供应商: {purchase.supplier.name}") p.drawString(100, height-160, f"商品: {purchase.product.name}") p.drawString(100, height-190, f"数量: {purchase.quantity}") p.drawString(100, height-220, f"单价: ${purchase.purchase_price}") p.drawString(100, height-250, f"总金额: ${purchase.quantity * purchase.purchase_price}") p.drawString(100, height-280, f"已付金额: ${purchase.paid_amount}") p.drawString(100, height-310, f"采购日期: {purchase.date_created.strftime('%Y-%m-%d %H:%M')}") p.showPage() p.save() return response
内容的提问来源于stack exchange,提问作者Sesshōmaru
相关产品推荐
相关产品推荐

