GroupDocs.Assembly for Python via .NET generates documents from the results of your database queries. Python’s database drivers return rows; turn each row into a dict keyed by column name, and the template reads the columns by name, as it would read the columns of a .NET DataTable. The examples use Python’s built-in sqlite3 module with an in-memory database they create; any DB-API driver (PostgreSQL, MySQL, SQL Server, Oracle) returns rows the same way.
Python’s sqlite3 module (or your database’s DB-API driver): connections, cursors and rows
Here are the steps to generate documents from databases:
Connect to your database with its Python driver
Execute SQL queries and turn the rows into a list of dicts
Create a template document with expression tags referencing table and column names
Use DocumentAssembler to assemble the template with the rows
Save the assembled document
Note
The ADO.NET classes a .NET application passes to the engine (DataSet, DataTable, DataView, IDataReader) have no Python form. In Python, a table is a list of dicts, several tables are a dict of such lists, and related rows are nested lists. A column’s Python type decides what the template sees: sqlite3 returns int, float, str, bytes and None.
Generate Document from Query Rows
The simplest approach is a list of dicts, one per row:
importsqlite3fromgroupdocs.assemblyimportDataSourceInfo,DocumentAssemblerdefcreate_database():# An in-memory database standing in for your ownconnection=sqlite3.connect(":memory:")connection.executescript("""
CREATE TABLE Customers (CustomerId INTEGER PRIMARY KEY, CustomerName TEXT, Email TEXT, Balance REAL);
INSERT INTO Customers VALUES (1, 'Quayside Bistro', 'orders@quaysidebistro.example', 1250.75);
INSERT INTO Customers VALUES (2, 'Lantern Street Café', 'orders@lanternstreetcafe.example', 2480.00);
INSERT INTO Customers VALUES (3, 'Saltmarsh Deli', 'orders@saltmarshdeli.example', 640.50);
INSERT INTO Customers VALUES (4, 'Pier Nine Kitchen', 'orders@piernine.example', 1890.20);
""")returnconnectiondefquery(connection,sql):# Each row becomes a dict keyed by column name, as the template reads itconnection.row_factory=sqlite3.Rowreturn[dict(row)forrowinconnection.execute(sql)]connection=create_database()try:customers=query(connection,"SELECT CustomerName, Email, Balance FROM Customers")finally:connection.close()# Assemble documentassembler=DocumentAssembler()assembler.assemble_document("customer-accounts.docx","customer-accounts-report.docx",DataSourceInfo(customers,"customers"))print("Document generated from query rows successfully.")
A .NET application can hand the engine an open IDataReader and let it read the rows one by one. The engine cannot read from a Python cursor: read the rows into a list first, as above, and keep large results small by selecting only the columns and rows the report shows.
Filtering and Sorting Rows
Where .NET filters and sorts a DataView, Python lets the database do it in the query:
importsqlite3fromgroupdocs.assemblyimportDataSourceInfo,DocumentAssemblerdefcreate_database():# An in-memory database standing in for your ownconnection=sqlite3.connect(":memory:")connection.executescript("""
CREATE TABLE Customers (CustomerId INTEGER PRIMARY KEY, CustomerName TEXT, Email TEXT, Balance REAL);
INSERT INTO Customers VALUES (1, 'Quayside Bistro', 'orders@quaysidebistro.example', 1250.75);
INSERT INTO Customers VALUES (2, 'Lantern Street Café', 'orders@lanternstreetcafe.example', 2480.00);
INSERT INTO Customers VALUES (3, 'Saltmarsh Deli', 'orders@saltmarshdeli.example', 640.50);
INSERT INTO Customers VALUES (4, 'Pier Nine Kitchen', 'orders@piernine.example', 1890.20);
""")returnconnectiondefquery(connection,sql,parameters=()):# Each row becomes a dict keyed by column name, as the template reads itconnection.row_factory=sqlite3.Rowreturn[dict(row)forrowinconnection.execute(sql,parameters)]connection=create_database()try:# Filtered and sorted by the databasecustomers=query(connection,"SELECT * FROM Customers WHERE Balance > ? ORDER BY CustomerName ASC",(1000,))finally:connection.close()assembler=DocumentAssembler()assembler.assemble_document("customer-accounts.docx","customer-accounts-filtered.docx",DataSourceInfo(customers,"customers"))print("Document generated from filtered rows successfully.")
A .NET DataSet relation lets a template call GetChildRows("CustomerOrders") on a customer row. Python rows have no such method: nest each customer’s orders under the customer instead, and the template iterates them as a member:
importsqlite3fromgroupdocs.assemblyimportDataSourceInfo,DocumentAssemblerdefcreate_database():# An in-memory database standing in for your ownconnection=sqlite3.connect(":memory:")connection.executescript("""
CREATE TABLE Customers (CustomerId INTEGER PRIMARY KEY, CustomerName TEXT, Email TEXT, Balance REAL);
INSERT INTO Customers VALUES (1, 'Quayside Bistro', 'orders@quaysidebistro.example', 1250.75);
INSERT INTO Customers VALUES (2, 'Lantern Street Café', 'orders@lanternstreetcafe.example', 2480.00);
INSERT INTO Customers VALUES (3, 'Saltmarsh Deli', 'orders@saltmarshdeli.example', 640.50);
INSERT INTO Customers VALUES (4, 'Pier Nine Kitchen', 'orders@piernine.example', 1890.20);
CREATE TABLE Orders (OrderId INTEGER PRIMARY KEY, CustomerId INTEGER, Amount REAL);
INSERT INTO Orders VALUES (10001, 1, 552.00);
INSERT INTO Orders VALUES (10002, 2, 522.00);
INSERT INTO Orders VALUES (10003, 3, 281.00);
INSERT INTO Orders VALUES (10004, 1, 132.00);
INSERT INTO Orders VALUES (10005, 2, 192.00);
INSERT INTO Orders VALUES (10006, 1, 516.00);
INSERT INTO Orders VALUES (10007, 4, 198.00);
INSERT INTO Orders VALUES (10008, 3, 456.00);
""")returnconnectiondefquery(connection,sql,parameters=()):# Each row becomes a dict keyed by column name, as the template reads itconnection.row_factory=sqlite3.Rowreturn[dict(row)forrowinconnection.execute(sql,parameters)]connection=create_database()try:customers=query(connection,"SELECT * FROM Customers ORDER BY CustomerId")# Relate the tables: each customer gets the list of its own ordersforcustomerincustomers:customer["Orders"]=query(connection,"SELECT OrderId, Amount FROM Orders WHERE CustomerId = ? ORDER BY OrderId",(customer["CustomerId"],))finally:connection.close()assembler=DocumentAssembler()assembler.assemble_document("customer-orders.docx","customer-orders-report.docx",DataSourceInfo(customers,"customers"))print("Document generated with related rows successfully.")
When using database connections, always release them. Close the connection in a finally block, as above, or with contextlib.closing. A customer with no orders gets an empty list, which works as long as another customer has orders: an empty list on its own carries no column names, and the template then fails with “Can not get the value of member … on type ‘System.Object’”.
Advanced Usage Topics
To learn more about working with complex data, several data sources, and performance optimization for large datasets, please refer to the advanced usage section.
More resources
GitHub Examples
You may easily run the code above and see the feature in action in our GitHub examples: