xhtml2pdf - 使用 django forloop 标记显示 table 行时出现问题

xhtml2pdf - Problem with displaying table rows using django forloop tag

我想做的是创建一个包含多个 table 行的发票 pdf 文件。 Table 行将在 Django 中使用 for 循环创建。问题是 for 循环标记内的数据在 pdf 文件中不可见。 您可以查看下面的屏幕截图。 Django 正确呈现 invoice.html 模板,因此代码有效,但 pdf 文件包含没有任何 table 行的空帧。要从 html 渲染 pdf,我正在使用 xhtml2pdf.

how django render the invoice.html template

how pdf file looks like

invoice.html

<html>
    <head>
        {% load static %}
        <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
        <meta charset="UTF-8">
      <style>
        @font-face {
            font-family: Roboto;
            src: url('{% static 'fonts/Roboto-Regular.ttf' %}');
        }

        @font-face {
            font-family: Roboto-bold;
            src: url('{% static 'fonts/Roboto-Bold.ttf' %}');
            font-weight: bold;
        }

        @page {
            size: a4 portrait;
            @frame header_frame {           /* Static Frame */
                -pdf-frame-content: frame_header_left;
                left: 50pt; width: 245pt; top: 30pt; height: 150pt;
            }
            @frame header_frame {           /* Static Frame */
                -pdf-frame-content: frame_header_right;
                left: 300pt; width: 245pt; top: 50pt; height: 150pt;
            }

            @frame content_frame {          /* Content Frame */
                -pdf-frame-content: frame_invoice_number;
                left: 50pt; width: 512pt; top: 170pt; height: 30pt;
            }

            @frame col1 {
                -pdf-frame-content: frame_col1;
                left: 50pt; width: 245pt; top: 220pt; height: 130pt;
            }

            @frame col2 {
                -pdf-frame-content: frame_col2;
                left: 300pt; width: 245pt; top: 220pt; height: 130pt;
            }

            @frame frame_services {
                -pdf-frame-content: frame_services;
                left: 50pt; width: 512pt; top: 380pt; height: 250pt;
                -pdf-frame-border: 1;
            }

            @frame content_frame {
                -pdf-frame-content: frame_summary;
                left: 465pt; width: 100pt; top: 590pt; height: 50pt;
            }

            @frame content_frame {
                -pdf-frame-content: frame_vat;
                left: 50pt; width: 512pt; top: 590pt; height: 150pt;
            }

            @frame content_frame {
                -pdf-frame-content: frame_signatures_left;
                left: 50pt; width: 140pt; top: 725pt; height: 70pt;
            }

            @frame content_frame {
                -pdf-frame-content: frame_signatures_right;
                left: 400pt; width: 140pt; top: 725pt; height: 70pt;
                left: 400pt; width: 140pt; top: 725pt; height: 70pt;
            }

            @frame content_frame {
                -pdf-frame-content: footer_content;
                left: 50pt; width: 512pt; top: 775pt; height: 50pt;
            }
    }

        body {
          background-color: white;
          font-family: "Roboto", sans-serif;
        }

        .right{
            font-size: 10px;
            text-align: right;

        }
        .left{
            text-align: left;
        }

        .invoice-number {
            font-size: 18px;
            font-family: "Roboto-bold", sans-serif;
        }

        .col-titles {
            font-size: 16px;
            text-decoration: underline;
            font-family: "Roboto-bold", sans-serif;
         }

         .footer {
            font-size: 8px;
            text-align: center;
         }

         p{
            font-size: 10px;
            line-height: 0;
         }

         table {
            border-bottom: 1px solid #ddd;
            text-align: center;
        }

        td, td {
            border-bottom: 1px solid #ddd;
            vertical-align: middle;
        }

        .summary{
             border-bottom: 1px solid #ddd;
        }

        .signatures {
            border-top: 1px solid black;
            font-size: 8px;
            text-align: center;
        }

         th {
            height: 36px;

         }
         td {
            height: 25px;
         }


      </style>
    </head>
    <body>
        <div>
            <div>
                <div id="frame_header_left" class="left">
                    <img src="{% static 'invoices/logo.png' %}" alt="logo" width="150" height="112">
                </div>
                <div id="frame_header_right" class="right">
                    <p>Miejsce wystawienia: Żabno</p>
                    <p>Data badania: {{ invoice.data_badania }}</p>
                    <p>Data wystawienia: {{ invoice.data_wystawienia_faktury }}</p>
                </div>
            </div>
            <div id="frame_invoice_number">
                <div class="invoice-number">
                    <h2>Faktura nr: {{ invoice.numer }}</h2>
                </div>
            </div>
            <div id="frame_col1">
                <p class="col-titles">Sprzedawca</p>
                <div>
                    <p>MEDIKAP</p>
                    <p>ul. Plac Grunwaldzki 15B, 33-240 Żabno</p>
                    <p>NIP: 999999999</p>
                    <p>REGON: 9999999</p>
                    <p>Bank: ING Bank Śląski</p>
                    <p>Nr konta: 12 1234 1234 1243 1243 214 1244</p>
                </div>
            </div>

            <div id="frame_col2">
                <p class="col-titles">Nabywca</p>
                <div>
                    <p> {{ invoice.firma}} </p>
                    <p> ul. {{ invoice.firma.ulica }} </p>
                    <p> {{ invoice.firma.kod_pocztowy }} {{ invoice.firma.miasto}}</p>
                    <p> NIP: {{ invoice.firma.nip }}</p>
                    <p> REGON: {{ invoice.firma.regon }}</p>
                    <p> forma płatności: {{ invoice.get_forma_platnosci_display}}</p>
                </div>
            </div>

            <div id="frame_services">
                <table>
                    <tr>
                        <th style="width: 50px;"> # </th>
                        <th style="width: 600px;"> Nazwa usługi</th>
                        <th style="width: 100px;"> Ilość</th>
                        <th style="width: 100px;"> Rabat[%]</th>
                        <th style="width: 100px;"> Cena usługi</th>
                        <th style="width: 100px;"> Wartość</th>
                        <th style="width: 100px;"> Wartość z rabatem:</th>
                    </tr>

                {% for service in services_items %}
                    <tr>
                        <td> test </td>
                        <td> test </td>
                    </tr>
                {% endfor %}
                </table>
            </div>
            <div id="frame_summary" class="summary">
                <p> : {{ service.get_total_value }} PLN</p>
                <p> Wartość z uwzględnieniem {{ invoice.rabat}}% rabatu: {{ discounted_value|floatformat:"-2" }} PLN</p>
            </div>

            <div id="frame_vat">
                <p> Podstawa zwolnienia z VAT: </p>
                <p> Zwolnienie ze względu na zakres wykonywanych czynności (art. 43 ust.1) pkt 19 Ustawy o VAT</p>
            </div>

            <div id="frame_signatures_left">
                <p class="signatures"> podpis osoby upoważnionej do odbioru faktury</p>
            </div>

            <div id="frame_signatures_right">
                <p class="signatures"> podpis osoby upoważnionej do wystawienia faktury</p>
            </div>

            <div id="footer_content" >
                <p class="footer">MEDIKAP Maria K.</p>
                <p class="footer">Plac Grunwaldzki 15B, 33-240 Żabno</p>
                <p class="footer">e-mail: gabinet.medikap@gmail.com tel: 539 993 332</p>
                <p class="footer">NIP: 9930212793 REGON: 852441210</p>
            </div>

        </div>
    </body>
</html>

观看次数。py/DetailsInvoice

class DetailsInvoice(generic.View):
    template_name = 'invoices/invoice_detail.html'
    form_class = DetailInvoiceForm
    success_url = reverse_lazy("invoices:list")

    def get(self, request, invoice_id):
        current_invoice = get_object_or_404(Invoice, id=invoice_id)
        form = self.form_class(instance=current_invoice)
        request.session['invoice_id'] = current_invoice.id

        services = Service.objects.all()
        all_service_items = ServiceItem.objects.all().filter(faktura = current_invoice).order_by('usluga')

        context = {
            'invoice': current_invoice,
            'form' : form,
            'services' : services,
            'services_items' : all_service_items,
        }

        return render(request, self.template_name, context)

    def post(self, request, invoice_id):

        current_invoice = get_object_or_404(Invoice, id=invoice_id)
        form = self.form_class(request.POST, instance=current_invoice)

        all_service_items = ServiceItem.objects.all().filter(faktura=current_invoice)

        context = {
            'invoice' : current_invoice,
        }

        pdf = render_to_pdf('invoices/invoice.html', context)
        services_assigned_to_invoice = current_invoice.uslugi.all()

        if 'update-data' in request.POST and form.is_valid():
            for service in all_service_items:
                service_item = get_object_or_404(ServiceItem, id=service.id)
                quantity_input = request.POST.get('quantity-' + str(service.id))
                discount_input = request.POST.get('discount-' + str(service.id))

                service_item.ilosc = int(quantity_input)
                service_item.rabat = int(discount_input)
                service_item.save()

            form.save()

            for service_item in all_service_items:
                if service_item.usluga not in services_assigned_to_invoice:
                    service_item.delete()

            for single_service in services_assigned_to_invoice:
                new_service_item, created = ServiceItem.objects.get_or_create(usluga=single_service, faktura=current_invoice)

            messages.success(request, 'Pomyślnie zaktualizowane dane')
            return HttpResponseRedirect(self.request.META.get('HTTP_REFERER'))

        if 'view-pdf' in request.POST:
            return HttpResponse(pdf, content_type='application/pdf')

        if 'download-pdf' in request.POST:
            response = HttpResponse(pdf, content_type='application/pdf')
            filename = f"Faktura {current_invoice.numer}.pdf"
            content = "attachment; filename={}".format(filename)
            response['Content-Disposition'] = content
            return response
        else:
            return redirect('invoices:list')

渲染函数

def render_to_pdf(template_src, context_dict={}):
    template = get_template(template_src)
    html = template.render(context_dict)
    result = BytesIO()
    pdf = pisa.pisaDocument(BytesIO(html.encode("utf-8")), result, link_callback=link_callback)
    if not pdf.err:
        return HttpResponse(result.getvalue(), content_type='application/pdf')
    return None

您向 PDF 传递的上下文变量与传递给 HTML 模板的上下文变量不同。您要传递给您的 HTML 模板:

    context = {
        'invoice': current_invoice,
        'form' : form,
        'services' : services,
        'services_items' : all_service_items,
    }

而您的 PDF 只是传递:

    context = {
        'invoice' : current_invoice,
    }

services_items 是您的 PDF 模板中似乎缺少的那个。因此,因为 for service in services_items 是 PDF 模板中的 empty/non-existent 列表,所以它不会呈现任何行。将来您可以通过在 for 循环中添加 {% empty %} 部分来检查这一点:

            {% for service in services_items %}
                <tr>
                    <td> test </td>
                    <td> test </td>
                </tr>
            {% empty %}
                No items!
            {% endfor %}