#python #django #django-templates #weasyprint
Вопрос:
Я работаю с weasyprint после переноса с xhtml2pdf и нахожу некоторые проблемы с получением статических файлов. Я получаю следующую ошибку:
2021-12-03 14:45:50,198 [ERROR] Failed to load image at "http://api.dashboard.localhost:8000/static/logos/logo.png" (URLError: lt;urlopen error [Errno -2] Name or service not knowngt;)
но когда я получаю доступ к тому же URL-адресу, который weasyprint не смог ни в моем браузере, ни в curl, я могу просматривать/ получать доступ к файлу.
Вот мой код:
from io import BytesIO import mimetypes from pathlib import Path from urllib.parse import urlparse import logging from django.conf import settings from django.contrib.staticfiles.finders import find from django.core.files.storage import default_storage from django.urls import get_script_prefix from django.template.loader import render_to_string import weasyprint from weasyprint import HTML logging.basicConfig( level=logging.INFO, format="%(asctime)s [%(levelname)s] %(message)s", handlers=[ logging.FileHandler("debug.log"), logging.StreamHandler() ] ) # https://github.com/fdemmer/django-weasyprint/blob/main/django_weasyprint/utils.py def url_fetcher(url, *args, **kwargs): # load file:// paths directly from disk if url.startswith('file:'): mime_type, encoding = mimetypes.guess_type(url) url_path = urlparse(url).path data = { 'mime_type': mime_type, 'encoding': encoding, 'filename': Path(url_path).name, } default_media_url = settings.MEDIA_URL in ('', get_script_prefix()) if not default_media_url and url_path.startswith(settings.MEDIA_URL): media_root = settings.MEDIA_ROOT if isinstance(settings.MEDIA_ROOT, Path): media_root = f'{settings.MEDIA_ROOT}/' path = url_path.replace(settings.MEDIA_URL, media_root, 1) data['file_obj'] = default_storage.open(path) return data elif settings.STATIC_URL and url_path.startswith(settings.STATIC_URL): path = url_path.replace(settings.STATIC_URL, '', 1) data['file_obj'] = open(find(path), 'rb') return data # fall back to weasyprint default fetcher return weasyprint.default_url_fetcher(url, *args, **kwargs) def render_template_to_pdf(template_path, request, context): results = BytesIO() template_string = render_to_string( template_name=template_path, context=context, ) # create the pdf report HTML(string=template_string, base_url=request.build_absolute_uri("/"), url_fetcher=url_fetcher).write_pdf(results) return results.getbuffer()
Приведенный выше код генерирует pdf-файл, но без изображений, так как вышеупомянутая ошибка продолжает отображаться в моих журналах.
Мои настройки для мультимедийных/ статических файлов:
DEFAULT_FILE_STORAGE = "utils.storages.CustomFileSystemStorage" STATIC_URL = "/static/" STATIC_ROOT = os.path.realpath(env.str("STATIC_FILES_ROOT", default=os.path.join(BASE_DIR, "staticfiles") "/")) MEDIA_URL = "/media/" MEDIA_ROOT = os.path.realpath(env.str("MEDIA_FILES_ROOT", default=os.path.join(BASE_DIR, "mediafiles") "/")) STATICFILES_DIRS = [ os.path.join(BASE_DIR, "static"), ]
В моем шаблоне:
{% load static %} lt;div style="float: right;"gt; lt;img src="{% static 'logos/logo.jpg' %}" alt="logo" width="140" height="40"/gt; lt;/divgt;
Я запускаю это в docker, но я думаю, что это может быть неуместно, так как я мог бы получить доступ к файлам вне приложения (браузер/ curl), но не с помощью weasyprint.
Я проверял ответы на stackoverflow/github/etc, Но, к сожалению, не смог найти ничего, объясняющего, почему это происходит или как это обойти. Любые идеи о том, почему это происходит, очень ценятся!
Ответ №1:
Докер-это не проблема, потому что я также получаю аналогичную ошибку при использовании статики так же, как вы делали без докера. Я получаю ошибку, показанную ниже:
[weasyprint:137] ОШИБКА: Относительная ссылка URI без базового URI:
Итак, что я сделал, так это использовал urlsplit для получения URL-адреса моего приложения и передал его в шаблон, чтобы я мог использовать полный URL-адрес.
from django.utils.six.moves.urllib.parse import urlsplit def test(request): scheme = urlsplit(request.build_absolute_uri(None)) context = { 'host_url': f"{scheme.scheme}://{scheme.netloc}" } return render(request, 'pdf.html', context)
шаблон
lt;img src="{{ host_url }}/logos/logo.jpg" alt="" /gt;
Вместо передачи host_url вы можете напрямую использовать свой домен, но использование host_url делает приложение динамичным, и вам не нужно менять домен, находясь в другом домене или используя локальный сервер.
Комментарии:
1. Эй, спасибо тебе за твой ответ. Однако это не решает проблему. У меня есть базовый URI, и окончательный URL-адрес, созданный
host_url
таким же, как и тот, который используетсяstatic
. Также просто хочу отметить, что модуль django.utils.six был удален из django-3.0. Я ценю вашу помощь.
Ответ №2:
Я не уверен, почему, но установка base_url
на "."
решение проблемы, и weasyprint теперь может разрешать как локальные, так и внешние статические файлы.
Изменения вступают в силу в:
HTML(string=template_string, base_url=".", url_fetcher=url_fetcher).write_pdf(results)
Это заняло у меня целый день, и я изучил исходный код как weasyprint, так и django-weasyprint, прежде чем пытаться "."
. Я надеюсь, что это спасет кого-нибудь с такой же проблемой в будущем.