Загрузка файла после сохранения модели в Django
Я пытаюсь сгенерировать PDF на основе ModelForm с помощью Weasyprint. Идея заключается в том, что когда пользователь нажимает кнопку сохранения, PDF генерируется после сохранения модели (для этого я использую сигнал post_save
). Но когда я нажимаю кнопку сохранения, модель сохраняется, но запрос на загрузку не показывается пользователю, хотя у меня правильная конфигурация Content-Disposition
в моем HttpResponse.
Мне также нужно передать некоторые параметры сигналу post_save
, потому что некоторые поля, которые нужно добавить в PDF, находятся не в Model, а в ModelForm, и я не смог найти, как это сделать.
admin.py
def save_model(self, request: HttpRequest, obj: Contract, form: ContractForm, change: Any) -> None:
if not change and not request.user.is_superuser:
obj = MythLabsMultiTenancy.assign_organization_to_obj(
obj, request.user
)
return super().save_model(request, obj, form, change)
forms.py
from dal.autocomplete import ModelSelect2
from django import forms
from django.core.validators import RegexValidator
class ContractForm(forms.ModelForm):
class Meta:
widgets = {
'handover_vehicle': ModelSelect2(
url='vehicle_autocomplete', forward=('organization',),
),
'lease_holder': ModelSelect2(
url='client_autocomplete', forward=('organization',),
),
'replacement_vehicle': ModelSelect2(
url='replacement_vehicle_autocomplete', forward=('organization',),
),
}
warranty_card_company = forms.CharField(required=False, label='Tarjeta')
warranty_card_number = forms.CharField(
max_length=19, required=False, label='N° de tarjeta'
)
warranty_card_expiration_date = forms.CharField(
max_length=5, required=False, label='Fecha de expiración',
help_text='Formato: MM/YY',
validators=[RegexValidator("(0[1-9]|1[0-2])\/[0-9]{2}")]
)
warranty_card_auth_code = forms.IntegerField(
max_value=9999, min_value=0, required=False,
label='Código de autorización'
)
signals.py
from datetime import datetime
from django.db.models import QuerySet
from django.db.models.signals import post_save
from django.dispatch import receiver
from django.http import HttpResponse
from django.template.loader import render_to_string
from weasyprint import HTML
from .models import Charge, Contract, Control, Extension
@receiver(post_save, sender=Contract, dispatch_uid='download_contract_pdf')
def download_contract_pdf(sender, instance, **kwargs) -> HttpResponse:
file_name = f"contrato-{datetime.now()}.pdf"
key_list = [
'warranty_card_company', 'warranty_card_number',
'warranty_card_expiration_date', 'warranty_card_auth_code'
]
credit_card_data = []
for key, value in instance.items:
if key in key_list:
credit_card_data.append(value)
charge_qs: QuerySet = Charge.objects.filter(contract=instance.pk)
control_qs: QuerySet = Control.objects.filter(contract=instance.pk)
extension_qs: QuerySet = Extension.objects.filter(contract=instance.pk)
html_string = render_to_string(
'pdf_template.html', {
'charge_obj': charge_qs,
'contract_obj': instance,
'control_obj': control_qs,
'extension_obj': extension_qs,
'credit_card_data': credit_card_data
}
)
html = HTML(string=html_string)
pdf = html.write_pdf(
stylesheets=[
"https://cdn.jsdelivr.net/npm/bootstrap@5.2.0-beta1/dist/css/bootstrap.min.css"
]
)
response = HttpResponse(pdf, content_type="application/pdf",)
response['Content-Disposition'] = f'attachment; filename={file_name}'
return response
Если вам нужна дополнительная информация, не стесняйтесь спрашивать! Спасибо заранее!
Вам необходимо заключить имя файла в двойные кавычки:
response['Content-Disposition'] = f'attachment; filename="{file_name}"'
Я решил эту проблему! Все, что мне нужно было сделать, это использовать response_add
и response_change
в моей ModelAdmin.
def response_add(self, request: HttpRequest, obj: Contract, post_url_continue: None) -> HttpResponse:
super().response_add(request, obj, post_url_continue)
return generate_contract_pdf(request, obj, None)
def response_change(self, request: HttpRequest, obj: Contract) -> HttpResponse:
super().response_change(request, obj)
return generate_contract_pdf(request, obj, None)
Документация: