python-docx-template icon indicating copy to clipboard operation
python-docx-template copied to clipboard

Nested Templates?

Open exfiltrata opened this issue 9 years ago • 8 comments

Is it possible for you to implement functionality to do nested templates, maybe with subdocs?

Basically, I want to have a main template with several jinja tags. Then I would like to define tables in a few other template files, each containing jinja tags. Finally, I would like to pull records from a database, and then loop through them, creating a table in the main doc for each record, based on a table template. I can do this with the existing code base, but I have to build the tables in my script as opposed to defining them in a Word template. I hope that makes sense.

exfiltrata avatar Jan 18 '17 17:01 exfiltrata

I have a function to use word files as snipplets:

def __ext_snipplet(self, name):
    """
    Render another docx body into my document
    """
    document = DocxTemplate(self.getSnippletPath(name))
    document.render(self.jinjaContext)
    # Remove the sectPr element ("SecrionProperties")
    # "This element defines the section properties for the final section of the document." (Source: https://msdn.microsoft.com/en-us/library/documentformat.openxml.wordprocessing.sectionproperties(v=office.14).aspx)
    # This is not needed in case of a snipplet
    if document._element.body.sectPr is not None:
        document._element.body.remove(document._element.body.sectPr)
    # Remove the body xml tag
    xml = re.sub(r'</?w:body[^>]*>', '', document.get_xml())
    return xml

You have to add it to the jinja context: self.jinjaContext.update({'snipplet': self.__ext_snipplet})

Then, in your template you can call other word files. They will be rendered too: {{p snipplet('SNIP1.docx') }}

But this function has some problems:

  1. Different formats (color and tables and so on) of the sinipplet files are not copied.
  2. Images of the snipplets are not copied. The code should copy the image files (and other attachements) into the base document and assign a new, free id in the xml.

snipplet.docx snipplet_snip.docx snipplet_tpl.docx

snipplet.py:

from ls.docxtpl import DocxTemplate

tpl = DocxTemplate('test_files/snipplet_tpl.docx')
tpl.render({'a': 'abc'})
tpl.save('test_files/snipplet.docx')

Maybe there are some other problems. Use it with care. Feel free to improve the code.

DennyWeinberg avatar Jan 18 '17 18:01 DennyWeinberg

Hello,

The main template stores all images and styles, creating sub-templates can import some texts or tables, but I do not know yet how to merge sub template images and styles into the main template : this step is part of the library "python-docx" I am using.

Using subdoc can help a little : you have to build your table from scratch : if your tables are simple, it could do the job (have a look to python-docx documentation and the docxtpl/tests/subdoc.py)

But the real sub-templating is not fully possible now : one have to merge maindoc._part with subtamplatedoc._part, thing I do not kown how to proceed.

elapouya avatar Jan 19 '17 08:01 elapouya

Thanks for the suggestions. elapouya - I've been generating tables from scratch via subdocs. It works well, but it's not as flexible as I'd like.

Denny - your function is working quite well for me so far. I'm able to create separate template files, each with tables containing tags, and then pull them into the main document.

exfiltrata avatar Jan 24 '17 23:01 exfiltrata

Denny - do you have any examples of how you're copying images from a snipplet into the base document? EDIT Nevermind on this last question - I figured it out.

exfiltrata avatar Jan 25 '17 20:01 exfiltrata

@fixautomator Could you please share how you got the images into the base document? I am having the same problem.

dalai4git avatar May 17 '17 06:05 dalai4git

@dalai4git I basically loop through the context before I render, looking for images and replacing them with InlineImage. Here's kind of a hacky example: for item in context['screenshots']: if type(item.image) != InlineImage and '<wp:inline' not in item.image: item.image = InlineImage(document, item.image)

exfiltrata avatar May 18 '17 15:05 exfiltrata

@DennyWeinberg

Could you pls. update your example to have a full functional code ? I don't understand how should I update the jinja context (how do I obtain the instance of the jinja context to be more precise)

dk766 avatar Jan 09 '18 12:01 dk766

@exfiltrata ,hi, have you worked out inserting with not constant number of subdocs? thanks a lot

wentgithub avatar Jul 22 '19 00:07 wentgithub