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')
Другие вопросы по тегам