4.4. Разработка «кастомного» функционала

В случае, если задача не решается описанным выше образом, требуется разработка представления или набора представлений. Они могут быть размещены как в отдельном приложении, так и внутри какого-то из существующих приложений конфигурации. Ни в коем случае не редактируйте ничего внутри директории /sphere/, так как она является директорией ядра и вы можете потерять возможность получения обновлений, добавляющие в систему исправления ошибок и новый функционал и возможности.

Рассмотрим выгрузку карточки корреспонденции из нашего приложения в pdf.

Для этого, добавим файл resources/my_project/sphere_conf/bps/projects/correspondence/views.py с представлением, которое будет получать запросы и возвращать файл pdf.

from io import BytesIO

from flask import render_template, send_file
from weasyprint import HTML

from sphere import conf
from sphere.bps.mixin_views import ProjectView
from sphere.lib.utils_view import AbstractView


class ToPdfView(ProjectView, AbstractView):
    """ Сформировать pdf для печати """

    def get(self):
        pdf = HTML(
            string=render_template('correspondence/to_pdf.html', document=self.document),
            base_url=conf.SITE_URL
        ).render()

        return send_file(
            BytesIO(pdf.write_pdf()),
            attachment_filename='Act.pdf',
            as_attachment=True
        )

Любое кастомное представление необходимо унаследовать от класса AbstractView. Представление может содержать методы get, post, put, delete, принимающие пользовательские запросы соответствующих http-методов.

В представлении мы делаем сразу несколько вещей. С помощью метода render_template мы обрабатываем шаблон в контексте со значением переменной document, содержащей текущий документ, который выгружается из системы. Доступность переменной self.document делает примесь ProjectView, для чего ей требуются параметры project_name и document_id, чтобы определить конкретный документ в конкретном проекте.

Функция render_template возвращает html-строку, которая передается в конструктор класса weasyprint.HTML. Вызов метода «render» из экземпляра этого класса возвращает представление pdf-файла, из которого мы получаем байты самого файла вызовом «pdf.write_pdf()» и передаем их в обертке BytesIO, эмулирующего файловый объект в метод send_file, который возвращает ответ с файлом для скачивания в виде вложения

Далее, свяжем это представление с url с параметром document_id и project_name, на который будут поступать запросы. Причем, переменная project_name будет определена по умолчанию для этого url как текущее название проекта «correspondence», а переменная document_id зависит от адреса url.

from flask import Blueprint
from sphere_conf.bps.projects.correspondence import views


blueprint = Blueprint('correspondence', __name__,
                    static_folder='static',
                    template_folder='templates')


# Подключаем наше представление
blueprint.add_url_rule(
    '/to_pdf/<int:document_id>/',
    view_func=views.ToPdfView.as_view('to_pdf'),
    defaults={'project_name': 'correspondence'},
)

Вызовом «blueprint.add_url_rule» мы связываем внутреннее представление данных с ресурсами этого приложения, которое (приложение) далее будет загружено сервером и встроено в список url-ов сервера с выбранным префиксом (или без него), выбранным в файле конфигурации с описанием приложений конфигурации /config.py.

# Подключенные приложения
APPS = (
    # Ядро
    ('sphere.lib', '/lib'),
    ('sphere.auth', '/auth'),
    ('sphere.logs', '/logs'),
    ('sphere.email', '/email'),
    ('sphere.sms', '/sms'),
    ('sphere.reports', '/reports'),
    ('sphere.bps', '/bps'),
    ('sphere.bps.plugins.files', '/bps_files'),
    ('sphere.bps.plugins.tasks', '/bps_tasks'),

    ('sphere_conf', None),
    ('sphere_conf.bps.projects.correspondence', '/correspondence'),
)

В данном случае, наше приложение подключено с использованием url-префикса correspondence и все внутренние ресурсы будут доступны под этим префиксом, то есть, наше представление получает полный url следующего вида: correspondence//to_pdf/<int:document_id>/

Далее, нам нужно в карточке документа разместить ссылку на скачивание. Для этого, встроим ссылку в один из блоков шаблона документа в файле /sphere_conf/bps/projects/correspondence/templates/correspondence/document_form.html

{% extends 'bps/document_form.html' %}


{% block content_plugins_info %}
    {{super()}}

    {# Ссылка на скачивание #}
    <a href="{{ url('correspondence.to_pdf', document_id=document.id) }}">Сформировать pdf</a>
{% endblock content_plugins_info %}