Блог Синявского
  • Разделы
  • Метки
  • Все статьи

Django выгрузка в формате .docx

1

Введение

.docx открытый формат документов от корпорации зла, в ответ на формат .odf.

Если решили использовать .docx, то нужно знать, что это на самом деле .zip архив. В котором содержится вся инфомация о документе - текст, изображения, стили, оформление. Сам текст содержится в xml (файл в архиве /word/document.xml)

Для Django существует несколько различных способов создать документ в формате .docx. Во-первых python-docx позволяет создать документ, используя дружественный интерфейс для работы с объектом (только через этот интерфейс, мы не можем работать с xml). Во-вторых есть библиотека docxtpl, которая зависит от python-docx и шаблонизатора jinja2. Мы можем использовать синтаксис шаблонизатора непосредственно в документе. В-третьих вариант распаковать zip архив и изменять xml при помощи библиотеки lxml. Поддержка zip встроена в python.

Все эти способы имеют преимущества и недостатки и каждый решит сам для себя что использовать.

Мой алгоритм

Для себя я выбрал следующий алгоритм создания отчетов в формате .docx. Я руководствовался соотношением необходимых-лишних библиотек в проекте, простотой работы, использования всех возможностей шаблонизатора, таких как собственные тэги, наследование.

  1. Создаем документ document.docx (содержащий все стили, и текст). Извлекаем из него document.xml.
  2. Из document.xml создаем шаблон, используя теги django шаблонизатора
  3. Рендерим шаблон document.xml наполняя его контекстом, получаем xml текст
  4. Парсим этот текст с помощью lxml получаем блок документа
  5. Открываем документ document.docx используя python-docx
  6. Подменяем body документа на наш блок
  7. Сохраняем документ с помощью python-docx с именем test.docx

Листинг программы open_docx_test.py

# encoding: utf-8
from django.template import Template, Context
from docx import Document
from lxml import etree
from django.conf import settings
settings.configure()

destination_document = Document('document.docx')
with open('document.xml') as f:
    template_text = f.read()
template = Template(template_text)
template_xml = template.render(Context({'title': 'Тестовый заголовок'}))
target_xml_tree = etree.fromstring(template_xml.encode('utf-8'))
target_body = target_xml_tree[0]
root = destination_document._element
root.replace(root.body, target_body)
destination_document.save('test.docx')

Для интеграции с Django необходимо вернуть документ в виде HTTP ответа, обернув его корректными заголовками. В папке для шаблонов я создал папку docx_report скопировал туда документ и полученный из него для рекдактирования xml шаблон document.docx и document.xml

# encoding: utf-8
from StringIO import StringIO
import os
from django.template import Template, Context
from docx import Document
from lxml import etree
from django.views.generic import View
from django.http import HttpResponse

def create_docx_document(document_folder, document_context):
    destination_document_file = StringIO()
    destination_document = Document(os.path.join(document_folder, 'document.docx'))
    with open(os.path.join(document_folder, 'document.xml')) as xml_file:
        template_text = xml_file.read()
    template = Template(template_text)
    template_xml = template.render(Context(document_context))
    target_xml_tree = etree.fromstring(template_xml.encode('utf-8'))
    target_body = target_xml_tree[0]
    root = destination_document._element
    root.replace(root.body, target_body)
    destination_document.save(destination_document_file)
    return destination_document_file

class ReportView(View):
    def get(self, request, *args, **kwargs):
        context = {'title': 'Тестовый заголовок'}
        template_dir = os.path.join(settings.BASE_DIR, 'templates', 'docx_report')
        docx = create_docx_document(template_dir, context)
        response = HttpResponse(
            docx.getvalue(),
            content_type='application/vnd.openxmlformats-officedocument.wordprocessingml.document'
        )
        response['Content-Disposition'] = 'attachment; filename=my_report.docx'
        return response

P.S. Посвещаю эту статью своей любимой женщине, спасибо тебе, родная, за терпение и твою нежность!



  • ← сюда
  • туда →

comments powered by Disqus

Опубликовано

25.05.2016

Обновление

05.05.2022

Категории

django

Тэги

  • django 12
  • docx 1
  • example 16
  • python 30

Всегда на связи

  • Блог Синявского - Ничего не переносить на завтра, это тоже проблема с прокастинацией?
  • © Алексей Синявский, по лицензии CC BY-SA если не указано иное.
  • С использованием Pelican. Тема: Elegant от Talha Mansoor