1

I wish to construct a Microsoft Word table using Python and then place it into a location marked by a placeholder in the Word template. Is this possible to do using docx and docx template?

The code below is a basic example of what I'm trying to do, but I'm unsure of how to place the table into the word template.

I know I can create a basic table using Jinja2 tags and add rows to the table using the render method, but the table creation logic is a bit involved (merging of certain cells, and empty spaces to separate related rows), so I'd prefer to construct the table using Python, and not have to use Jinja2 for that. Any help is appreciated!

from docxtpl import DocxTemplate
from docx.table import Table

doc = DocxTemplate('template.docx')

items = [
    {'column1': 'Item 1-1', 'column2': 'Item 1-2', 'column3': 'Item 1-3'},
    {'column1': 'Item 2-1', 'column2': 'Item 2-2', 'column3': 'Item 2-3'},
    {'column1': 'Item 3-1', 'column2': 'Item 3-2', 'column3': 'Item 3-3'}
]

rows = []
for item in items:
    row = [item['column1'], item['column2'], item['column3']]
    rows.append(row)

table = Table(len(rows)+1, 3)  # Create a new table with the correct number of rows and columns
table.cell(0, 0).text = 'Column 1'  # Add the column headers to the first row
table.cell(0, 1).text = 'Column 2'
table.cell(0, 2).text = 'Column 3'
for i, row in enumerate(rows):
    table.cell(i+1, 0).text = row[0]  # Add the row data to the table
    table.cell(i+1, 1).text = row[1]
    table.cell(i+1, 2).text = row[2]

# Code required here to place the table in the word template at a specific placeholder location.

doc.save('output.docx')
Greenjoy
  • 59
  • 2
  • 12
  • 1
    can you please provide an example output you may want? Also, I am not able to recreate due to various errors, such as `AttributeError: 'int' object has no attribute 'col_count'` for your `table.cell(0, 0).text = 'Column 1'` line. Thanks – hlin03 Apr 29 '23 at 07:33
  • The code is an example of what I'm trying to achieve. It's clearly not working, so looking for help to see if it's possible. The output of the program should be a new Microsoft Word document, 'output.docx'. The output is based on an MS Word document that serves as a template. The template is read in by the program. Within the template, there would be a static placeholder such as '{{ my_place_holder }}'. In the output Word document, the placeholder would be replaced with a Word table - the rows and columns of the table should be constructed within the Python program. Hope it's clear. Thanks! – Greenjoy Apr 30 '23 at 05:16

1 Answers1

1

I'm sorry.

This is an example of constructing a table by Python docx package and placing it into a specific location in a Word template.

Inserting table does via saving temporary file named demo.docx.

"""Tesing subdoc."""

from docx import Document

from docxtpl import DocxTemplate


def make_table(name):
    """Make table document."""
    sd = Document()

    items = [
        {"column1": "Item 1-1", "column2": "Item 1-2", "column3": "Item 1-3"},
        {"column1": "Item 2-1", "column2": "Item 2-2", "column3": "Item 2-3"},
        {"column1": "Item 3-1", "column2": "Item 3-2", "column3": "Item 3-3"},
    ]

    rows = []
    for item in items:
        row = [item["column1"], item["column2"], item["column3"]]
        rows.append(row)

    table = sd.add_table(
        rows=4, cols=3
    )  # Create a new table with the correct number of rows and columns
    table.cell(0, 0).text = "Column 1"  # Add the column headers to the first row
    table.cell(0, 1).text = "Column 2"
    table.cell(0, 2).text = "Column 3"
    for i, row in enumerate(rows):
        table.cell(i + 1, 0).text = row[0]  # Add the row data to the table
        table.cell(i + 1, 1).text = row[1]
        table.cell(i + 1, 2).text = row[2]

    sd.save(name)


make_table("demo.docx")  # create document with tricks table
doc = DocxTemplate("template.docx")  # do docxtpl template for main document
sd = doc.new_subdoc("demo.docx")  # do subdocument
# place the table in the word main document at a specific placeholder location.
context = {
    "mysubdoc": sd,
}

doc.render(context)

doc.save("output.docx")

Sourses and docs:

Sergey Zaykov
  • 523
  • 2
  • 9
  • Thanks for this, but as I mentioned in my original question, I'm aware I can use jinja2 tags and populate the table using the render method. But this doesn't give me much control of the table - for instance, I want to merge particular cells together based on their content, which I don't think is as straightforward to do with Jinja2. My question was about constructing the table using Python coding constructs to create a docx Table object and then replacing a placeholder with the table. But it appears this approach may not be possible. – Greenjoy Apr 30 '23 at 09:55
  • If I understood you you need Sub-documents. https://docxtpl.readthedocs.io/en/latest/#sub-documents Code is here https://github.com/elapouya/python-docx-template/blob/master/tests/merge_docx.py and templates are there https://github.com/elapouya/python-docx-template/blob/master/tests/templates/merge_docx_subdoc.docx https://github.com/elapouya/python-docx-template/blob/master/tests/templates/merge_docx_master_tpl.docx – Sergey Zaykov Apr 30 '23 at 21:02
  • 1
    Well done Sergey. This method achieves what I was after. I'm very pleased. Thanks mate! :) – Greenjoy May 01 '23 at 09:57
  • 1
    As a slight alternative to Sergey's code, I found that you can call the new_sub() method without any argument - this will create a new blank sub-document which you can add paragraphs/pictures/tables to using docx methods. Then you can use the render method to place that sub-document at a pre-defined placeholder in the template. Check out the example at https://github.com/elapouya/python-docx-template/blob/master/tests/subdoc.py – Greenjoy May 01 '23 at 11:12