Document Report URLs, Designer & Rendering

Developer guide for the Enezaframework reporting engine. Part of the Add Report series.

Table of Contents

  1. Overview
  2. URL Auto-Registration
  3. URL Naming
  4. Designer Integration
  5. Rendering Paths
  6. Reference
  7. Related Guides

Overview

DocumentReportView classes are auto-registered by the framework and can render either through the standard document template or through a designer-created layout. This guide covers URL generation, designer integration, the two rendering paths, and the reference tables.


URL Auto-Registration

get_registered_urls() in enezaframework/utils.py dispatches on view_type. A class with view_type = 'document_report' and a model generates:

python
elif view_type == 'document_report':
    report_name = getattr(view_class, 'report_name', None)
    if report_name:
        url_name = f'{report_name}_report'
        url_pattern = f'{report_name}_report/<str:pk>/'
    else:
        url_name = f'{model_name}_report'
        url_pattern = f'{model_name}_report/<str:pk>/'
    urlpatterns.append(
        path(url_pattern, view_class.as_view(), name=url_name)
    )

DocumentReportPageView sets view_type = 'document_report' (enezareporting/views.py), and DocumentReportView inherits it through BaseDocumentReport.

flowchart TD
    CLASS["@auto_url_register<br/>DocumentReportView subclass"] --> UTILS["get_registered_urls(app_name)"]
    UTILS --> TYPE{"view_type ==<br/>'document_report'?"}
    TYPE -->|Yes| NAME{"report_name set?"}
    NAME -->|Yes| RN["{report_name}_report"]
    NAME -->|No| MN["{model_name}_report"]
    RN --> PATTERN["path: {name}/&lt;str:pk&gt;/"]
    MN --> PATTERN
    PATTERN --> URLS["urlpatterns = [*dynamic_urls]"]

URL Naming

report_name set? URL pattern URL name
Yes, "invoice" /myapp/invoice_report/<str:pk>/ myapp:invoice_report
Yes, "tax_invoice" /myapp/tax_invoice_report/<str:pk>/ myapp:tax_invoice_report
No (falls back to model_name) /myapp/invoice_report/<str:pk>/ myapp:invoice_report

The pattern uses <str:pk>, not <int:pk> (enezareporting/views.py).


Designer Integration

Set design_slug on the view class to use a designer-created report layout:

python
from enezareporting.report_base import DocumentReportView
from enezaframework.utils import auto_url_register
from myapp.models import Invoice


@auto_url_register
class InvoiceDesignReportView(DocumentReportView):
    model = Invoice
    report_name = "invoice_design"
    header_model = Invoice
    header_fields = ["invoice_number", "customer", "date", "due_date", "status"]
    lines_related_name = "lines"
    line_columns = ["item_code", "description", "quantity", "unit_price", "line_total"]
    line_total_fields = ["line_total", "quantity"]
    design_slug = "invoice-pdf-layout"

DocumentReportView inherits DesignTemplateMixin (via BaseDocumentReport and DocumentReportPageView), which provides:

  • get_design() — returns the ReportDesign by slug, or None if not found/not set.
  • render_to_response() — when a design exists, compiles via ReportDesignCompiler, paginates, and renders through design_report.html.
  • get_template_names() — returns the compiled template string or falls back to defaults.

DocumentReportPageView.get_design() also falls back to a ReportDesign matched by the report class name (enezareporting/views.py).

DocumentReportPageView.render_to_response() extracts the lines list from the document data dict before calling the designer compiler, which expects data as a flat list for pagination.

When using DesignTemplateMixin directly, it must appear first in the MRO so its get() and render_to_response() win (enezareporting/views.py). DocumentReportPageView already handles this through its own inheritance.


Rendering Paths

flowchart TD
    GET["get(pk) → _get_document_report_results()"] --> CHECK{"design_slug resolves<br/>to a ReportDesign?"}
    CHECK -->|No| STD["Standard path:<br/>report.html → document_report.html"]
    CHECK -->|Yes| DESIGN["Designer path:<br/>ReportDesignCompiler.compile_paginated_report()<br/>→ design_report.html (A4 pages)"]

1. Without a Designer Template (default)

When design_slug is not set, DesignTemplateMixin.get_design() returns None and the standard template renders:

text
report.html → document_report.html

The template receives:

  • document_header — dict of header field values.
  • document_lines — list of line item dicts.
  • document_totals — dict of total field values.
  • is_document_report — True.

2. With a Designer Template

When design_slug resolves to a valid ReportDesign, the designer compiler handles pagination:

text
get() → _get_document_report_results() → render_to_response()
  → DesignTemplateMixin.render_to_response()
    → ReportDesignCompiler.compile_paginated_report(data_rows)
    → design_report.html with paginated A4 pages

Reference

DocumentReportView API

Attribute Required Default Description
model Yes — Django model for URL registration and header.
header_model Yes — Same as model; document header model for the generator.
header_fields Yes — Field name strings for the header grid.
line_columns Yes — Field name strings for lines table columns.
report_name No model.__name__.lower() Custom URL name segment.
lines_related_name No "lines" Related name for the line items FK.
line_total_fields No [] Numeric fields summed in the footer.
design_slug No None ReportDesign.slug for a designer layout.
Key Type Required Description
name str Yes Display name in the Print dropdown.
icon str Yes Icon filename (without extension) under the static icons/ folder.
url str Yes URL name — matches report_name + _report.

URL Registration

report_name set? URL pattern URL name
Yes {report_name}_report/<str:pk>/ {report_name}_report
No {model_name}_report/<str:pk>/ {model_name}_report

Key Constraints

  • model must be set — get_registered_urls() requires issubclass(view_class.model, Model).
  • Use report_name for distinct URLs when one model has multiple report views.
  • lines_related_name must match the related_name on the line model's FK to the header.
  • Print dropdown position: between Delete and Workflow buttons.
  • Print actions use hx-get (no confirmation), unlike action_list, which uses hx-post.
  • Print action URLs are used verbatim — no _action suffix is appended.