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 %}