Python/ Django- как обновить информацию, отображаемую в сгенерированном PDF
Я работаю над проектом, написанным на Python/ Django, и, в частности, хочу внести небольшое изменение в одну из его функций.
На одной из веб-страниц есть кнопка, которая при нажатии генерирует документ PDF с информацией о проекте в базе данных.
Я просто хочу добавить "дату" в PDF, чтобы при каждом нажатии кнопки и создании PDF-файла он создавался с сегодняшней датой, отображаемой в заголовке, чтобы отслеживать, когда какой PDF-файл был создан (поскольку они могут показывать различную информацию каждый раз, когда пользователь генерирует их).
URL-адрес страницы, на которой отображается кнопка: www.xyz.com/costing/5915/
Числа в URL '/5915/' являются идентификатором проекта, с которым в данный момент работает пользователь. В файле urls.py costing
Приложение, есть следующая строка:
url(r'^(?P<project_id>[0-9]+)/$', views.costing_home, name='home'),
и это view
определяется в views.py с помощью:
def costing_home(request, project_id):
""" Current costing home page in FileMaker with budget reports, CCIs, variations etc """
project = Project.objects.get(id=project_id)
context = {'project': project, 'mini_form':CostingMiniForm(instance=project)}
if project.budget_overview.deposit_budget_saved:
curr_budget = Budget.objects.get_or_create(project=project, version_number=None, current_marker=False)[0]
context['current_budget'] = curr_budget
return render(request, 'costing/costing_post_deposit.html', context)
else:
curr_budget = get_current_budget(project_id) or Budget.objects.get_or_create(project=project, version_number=None, current_marker=False)[0]
context['current_budget'] = curr_budget
versions = project.budget_versions.annotate(item_count=Count('budget_items')).filter(version_number__gte=0,)
saved_versions = versions.filter(item_count__gt=0)
presentations = versions.filter(item_count=0)
if saved_versions: context['version_formset'] = VersionFormSet(queryset=saved_versions)
if presentations: context['presentations'] = presentations
print curr_budget
return render(request, 'costing/costing_pre_deposit.html', context)
Я знаю, что это проекты "post_deposit", к которым я хочу добавить дату (т. Е. "5915" - это идентификатор проекта "post_deposit")
Если я перейду к файлу costing / costing_post_deposit.html в Sublime, я увижу, что он имеет следующую структуру:
{% block costing %}
...
{% endblock costing %}
{% block reports %}
<div class="app-wrap clear">
...
<div class="app sm {{app.color}}">
<a class="filler" id="pdf2_package" data-view-url="{% url 'costing:pdf2_master' project.id %}"></ a>
<strong>Save pdf payment package</strong>
<a id="new_payment_pdf" class="hidden" href="" target="_blank"></a>
</div>
</div>
{% endblock reports %}
...
Класс с текстом "Сохранить пакет платежей в формате PDF" - это кнопка, которую я нажимаю, чтобы сгенерировать PDF (нажатие на кнопку также открывает "новое электронное письмо" с прикрепленным сгенерированным PDF, чтобы отправить его на клиент)
Как я понимаю, мнение перешло к data-view-url
в этом классе (т.е. costing:pdf2_master
) - это представление, которое используется для создания PDF. Это представление определяется с помощью:
def pdf2_master(request, project_id):
project = Project.objects.select_related('budget_overview').prefetch_related('budget_versions', 'payments').get(id=project_id)
""" Save to the current budget (no version number), as versions not used once deposit is received """
budget = get_current_budget(project_id)
# Create a date variable to displays today's date on the PDF when it's generated
date_to_display = datetime.now()
if not budget:
Budget.objects.create(project=project, current_marker=1)
project.payments.first().recalc_balance_payment()
""" Payments """
future = datetime.now()+timedelta(days=9999)
payments = list(project.payments.exclude(is_balance_payment=True).annotate(date_null=Coalesce('date_due', Value(future))).order_by('date_null', 'date_due', 'number'))
try: payments.append(project.payments.get(is_balance_payment=True)) #Ensure balance payment is the last in the list
except ObjectDoesNotExist: pass
def get_payment_details(payment):
payment_amount_exc = payment.amount_exc_vat
payment_amount_inc = inc_vat(payment.amount_exc_vat, project.vat_status)
vat = payment.amount_inc_vat - payment.amount_exc_vat
if payment.requested and not payment.date_paid:
status = 'due'
elif payment.requested and payment.date_paid:
status = 'paid'
else:
status = 'hidden'
return [
['Payment', payment.name],
['Date', payment.date_due],
['Scheduled payment (£)', payment_amount_exc],
['VAT (£)', vat],
['Gross payment (£)', payment_amount_inc, status],
['Date paid', payment.date_paid],
]
payments_details = [get_payment_details(p) for p in payments]
budget_overview = project.budget_overview
payment_made = budget_overview.total_paid_exc
budget_exc_vat = budget_overview.updated_exc_vat
outstanding = budget_overview.outstanding_exc
outstanding_inc = budget_overview.outstanding_inc
net_payment_due = budget_overview.net_payment_due
gross_payment_due = inc_vat(net_payment_due, project.vat_status)
""" Post deposit budget overview styled for pdf """
try:
construction_variations = project.variations.total_construction
client_variations = project.variations.total_client
except ObjectDoesNotExist:
construction_variations = 0
client_variations = 0
grouped_totals = [
['Construction budget', budget.item_total_exc_vat() or 0],
['Construction variations', construction_variations],
['Client variations', client_variations],
['Client choice items', budget.cci_total_exc_vat_final],
]
total_exc = project.budget_overview.updated_exc_vat
total_inc = project.budget_overview.updated_inc_vat
""" CCIs """
cci_grouped_items = budget.cci_items.all().order_by('project_room', 'room', 'name')
""" Agreed client & construction variations """
try:
variations = project.variations.addomit_set.filter(date_agreed__isnull=False).order_by('item__project_room', 'item__room', '-date_agreed').annotate(sca=Case(When(item__notes__icontains='standard cost assumption', then=Value(1)), output_field=IntegerField()) )
# variations.filter(item__notes__icontains='standard cost assumption')
agreed_total = sum([i.item.total_inc_profit for i in variations])
client_variations = variations.filter(variation_type=1).order_by('-date_agreed')
client_agreed_total = sum([i.item.total_inc_profit for i in client_variations])
construction_variations = variations.filter(variation_type=1).order_by('-date_agreed')
construction_agreed_total = sum([i.item.total_inc_profit for i in construction_variations])
unagreed_variations = project.variations.addomit_set.filter(date_agreed__isnull=True, item_reported__isnull=False).order_by('item__project_room', 'item__room', '-date_agreed')
except ObjectDoesNotExist:
variations = ''
agreed_total = ''
client_variations = ''
client_agreed_total = ''
construction_variations = ''
construction_agreed_total = ''
unagreed_variations = ''
pdf_context = {
'project': project,
'date_to_display': date_to_display,
'budget': budget,
'grouped_totals': grouped_totals,
'total_exc': total_exc,
'total_inc': total_inc,
'cci_grouped_items': cci_grouped_items,
'cci_total_exc': budget.cci_total_exc_vat_final,
'variations': variations,
'agreed_total_exc': agreed_total,
'client_variations': client_variations,
'client_agreed_total_exc': client_agreed_total,
'construction_variations': construction_variations,
'construction_agreed_total_exc': construction_agreed_total,
# 'grouped_unagreed_variations': grouped_unagreed_variations,
'unagreed_variations': unagreed_variations,
'payments_details': payments_details,
'latest_total_exc': budget_exc_vat,
'total_paid_exc': payment_made,
'outstanding_exc': outstanding,
'outstanding_inc': outstanding_inc,
'net_payment_due': net_payment_due,
'gross_payment_due': gross_payment_due,
}
template = get_template('pdf2_base.html')
html = template.render(pdf_context)
filename='temp.pdf'
result = open(filename, 'w+b')
pdf = pisa.pisaDocument(src=StringIO.StringIO(
html.encode("UTF-8")), link_callback=fetch_resources, dest=result)
file_to_be_saved = result
if request.GET.get('quick-pdf'):
temp_pdf_obj = Spreadsheet.objects.get_or_create(id=2)[0]
temp_pdf_obj.file.save('temp.pdf', File(file_to_be_saved))
result.close()
file_path = temp_pdf_obj.file.file.name
return open_pdf(file_path)
# Change to save on payment without PDF
payment = project.payments.filter(date_paid__isnull=True, requested=True).order_by('number').last()
if not payment:
payment = project.payments.get(is_balance_payment=True)
# print 'Saving to payment ', payment.number
payment_pdf = payment.pdf_package
payment_pdf.save('{}/Client Payments/{} {} Payment.pdf'.format(CCD, FDS, project.project_code.upper()), File(file_to_be_saved))
result.close()
""" Presentation meeting report. Variables key: Subject: {0: Project name, 1: date DD.MM.YY} Body: {0: Email template,} """
template = EmailTemplate.objects.get(code=10)
email_to = template.get_email_addresses(project_id)
# Make variables for subject/body
date_str = datetime.now().strftime('%d.%m.%Y')
subject = template.subject.format(project.project_name, date_str)
body = template.body([payment.email_content])
file = payment_pdf.file
filename = file.name.split('/')[-1]
mailto = 'mailto:{}?subject={}&body={}&cc={}'.format(email_to, subject, '%0D%0A'.join(body.split('\n')), accounts_email)
pdf_url = reverse('costing:pdf_open', args=[payment.id])+"?pdf=payments"
return JsonResponse({'success':'Made pdf version', 'mailto': mailto, 'pdf_url': pdf_url})
Я пытался добавить переменную date_to_display
к виду, и установив его datetime.now()
, чтобы получить текущую дату, а затем добавил эту переменную в pdf_context
с линией:
'date_to_display': date_to_display,
Однако, когда я нажимаю кнопку "Сохранить пакет оплаты PDF" по этому URL-адресу, хотя файл PDF создается, когда я его открываю, я не вижу нигде отображаемой даты, добавленной к нему... почему это? Как я могу добавить сегодняшнюю дату в PDF при ее создании?
редактировать
На самом деле, я думаю, что проблема может заключаться в том, что если PDF-файл уже создан на сегодняшний день, то новый файл не сохраняется, поскольку, когда я смотрю на созданный PDF-файл, он говорит, что он был последний раз изменен пару часов назад., хотя я нажал кнопку "Сохранить PDF-пакет" только 5 минут назад.
Как я могу проверить, существует ли уже созданный мной PDF-файл, и если это так, то переименовать тот, который я генерирую, чтобы включить текущую дату / время, чтобы у него было уникальное имя файла?
Кроме того, кажется, что сегодняшняя дата добавляется в PDF - в заголовке страницы для каждой страницы, кроме первой страницы... почему она не отображается на первой странице, и как я могу заставить ее отображаться там?
1 ответ
Вместо прохождения в datetime
объект, попробуйте передать строковое представление этого:
date_to_display = str(datetime.now())
В идеале, лучший способ состоит в том, чтобы иметь временную метку имени файла (а не содержимое), поскольку таким образом любой, кто загружает, может также проверить, является ли файл, который он получил, самым последним или нет, и это можно сделать, не открывая файл.
Для этого просто добавьте метку времени к имени файла, используя:
filename='temp_%s.pdf' % datetime.now().strftime("%Y%m%d_%H%M%S")
result = open(filename, 'w+b')