A report often needs data that is related to its root object at several levels: a customer, its account, its account manager, the other customers of that manager. The .NET version of this page builds such data with a custom C# type whose indexers fetch each property, related entity and list of child entities lazily, when the template first asks for it.
In Python, the same data is a structure of nested dicts and lists, and the template reaches every level by member access: root.Entities.AccountManager.Name. The structure may also refer back to itself. A dict that appears in several places, such as an account manager that each of its customers points to, is one object for the engine, so a template can follow the references round as far as it needs. See Using Data Sources for how the engine reads Python data.
Creating Template
The template reads the properties of the root entity, its related entities, its child entities, and then goes from the root to its account manager and back to the manager’s customers:
Using properties of the root entity
<<[root["CustomerName"]]>>
<<[root.ShippingAddress]>>
Using related entities of the root entity
<<[root.Entities.Account.Login]>>
<<[root.Entities.AccountManager.Name]>>
Using child entities of the root entity
<<foreach [order in root.Children.Orders]>><<[order.OrderNumber]>>: <<[order.Product.ProductName]>>
<</foreach>>
Invoice | Amount due
<<foreach [invoice in root.Children.Invoices]>><<[invoice.Number]>> | <<[invoice.Amount]>><</foreach>>
Following the references back
<<foreach [customer in root.Entities.AccountManager.Customers]>><<[customer.CustomerName]>> (<<[customer.Entities.Account.Login]>>) is managed by <<[customer.Entities.AccountManager.Name]>>, who manages <<[customer.Entities.AccountManager.Customers.Count()]>> customers
<</foreach>>
The invoices are a table-row data band in the template.
The code loads Quayside Bistro and Saltmarsh Deli from customers.json, gives each its related and child entities as nested dicts, and puts both in the list of customers of their account manager, Mei Tanaka. Each customer’s AccountManager is that same manager dict, so the data is circular: manager, customer, manager again.
importjsonfromgroupdocs.assemblyimportDataSourceInfo,DocumentAssemblerwithopen("customers.json",encoding="utf-8")asf:customers={c["CustomerName"]:cforcinjson.load(f)}manager={"Name":"Mei Tanaka","Customers":[]}defadd_entities(customer,login):# Related entities and child entities of a customer, as nested dicts and listscustomer["Entities"]={"Account":{"Login":login},"AccountManager":manager}customer["Children"]={"Orders":customer["Order"],"Invoices":[{"Number":"INV-%d"%o["OrderNumber"],"Amount":o["Price"]}foroincustomer["Order"]],}# The manager refers back to the customer: the structure is circularmanager["Customers"].append(customer)returncustomerroot=add_entities(customers["Quayside Bistro"],"quayside-bistro")add_entities(customers["Saltmarsh Deli"],"saltmarsh-deli")assembler=DocumentAssembler()assembler.assemble_document("recursive-customer-data.docx","recursive-customer-data-report.docx",DataSourceInfo(root,"root"))
A dict passed to the engine also has an indexer, so <<[root["CustomerName"]]>> reads a value by its key, as in the template above. Use it for a key that is not an identifier, such as <<[row["First Name"]]>>. The indexer returns the value as object, however, which the engine cannot index again or read members of. The indexer chain of the .NET template, root.Entities["Account"]["Login"], fails in Python with:
Can not resolve an indexer on type 'System.Object'.
Write such a chain with member access instead, root.Entities.Account.Login, as the template above does.
When the Engine Reads the Data
Python data is read eagerly. DataSourceInfo(root, "root") copies the whole structure into .NET objects when it is created, so:
The data must be complete before DataSourceInfo is created. A change made to a dict afterwards does not reach the report.
A value cannot be computed on demand. The engine reads only dicts, lists, tuples and sets, and the values str, int, float, decimal.Decimal, bool, None, datetime, date, time and bytes. Another Python object, such as an instance of a class with properties, raises a TypeError, and the __getitem__ of a dict subclass is not called.
A list cannot contain itself: that raises a ValueError. A dict can be referred to from any number of places, itself included.
To keep a large report fast, load only the data its template reads before you call assemble_document.
Was this page helpful?
Any additional feedback you'd like to share with us?
Please tell us how we can improve this page.
Thank you for your feedback!
We value your opinion. Your feedback will help us improve our documentation.
Analyzing your prompt, please hold on...
An error occurred while retrieving the results. Please refresh the page and try again.