In my Django application, in a Batch, there are multiple Inquiries. Each Inquiry has a related Candidate. A candidate has multiple Addresses. I want to generate a PDF that will include all Candidates and their related Addresses. I am using Weasyprint to generate the PDF.
Models.py
class Batch(models.Model):
batch = models.CharField(verbose_name='Reference Number', max_length=200)
class Candidate(models.Model):
name = models.CharField(help_text='', max_length=200)
nationality = models.CharField(max_length=200)
class Address(models.Model):
DEFAULT_COUNTRY_ID = 1
PERA= 'Permanant Address'
PREA= 'Present Address'
ADDRESS_TYPE_CHOICES = [
(PERA, 'Permanant Address'),
(PREA, 'Present Address'),
]
candidate = models.ForeignKey(Candidate, on_delete=models.CASCADE, verbose_name='Candidate')
nid = models.CharField(verbose_name='NID', max_length = 15, unique = True, null=True, blank=False)
address_type = models.CharField(max_length=200, choices=ADDRESS_TYPE_CHOICES)
address_line_1 = models.CharField('Address Line 1', max_length=1000, null=True, blank=True)
state = models.ForeignKey(State, on_delete=models.CASCADE,)
city = models.ForeignKey(City, on_delete=models.CASCADE,)
country = models.ForeignKey(Country, on_delete=models.CASCADE, default=DEFAULT_COUNTRY_ID)
class Inquiry(models.Model):
batch = models.ForeignKey(Batch, on_delete=models.CASCADE, null=True,)
candidate = models.ForeignKey(Candidate, on_delete=models.SET_NULL, null=True, blank=True)
name = models.CharField(verbose_name='Name', help_text='', max_length=200)
nid = models.CharField(verbose_name='NID', max_length = 15, null=True, blank=False)
Here is what I tried. But it just generates multiple instances of the first Candidate. How can I generate a PDF with all Candidates and their relevant Addresses?
views.py
def download_batch_report(request, pk):
inquiries = Inquiry.objects.filter(batch=pk)
batch = get_object_or_404(Batch, id=pk)
for inquiry in inquiries:
inquiry = get_object_or_404(Inquiry, id=inquiry.id)
candidate = get_object_or_404(Candidate, nid=inquiry.nid)
addresses = Address.objects.filter(candidate=candidate)
print_time = timezone.now()
font_config = FontConfiguration()
context = {'batch': batch, 'inquiries': inquiries, 'inquiry': inquiry, 'candidate': candidate,'addresses': addresses, 'print_time': print_time}
html_string = render_to_string('ssvr/batch/report.html', context).encode(encoding="UTF-8")
css = CSS(string='''
@font-face {
font-family: kalpurush;
src: url(file:///mnt/d/djangoprojects/dj-boot-modal/librarydb/static/librarydb/fonts/kalpurush.ttf);
}''', font_config=font_config)
response = HttpResponse(content_type='application/pdf')
response['Content-Disposition'] = 'inline; filename=' str(batch.batch) '_' str(timezone.now()) '.pdf'
HTML(string=html_string).write_pdf(response, stylesheets=[css], presentational_hints=True, font_config=font_config)
return response
report.html
<!DOCTYPE html>
<html lang="en">
{% load static %}
{% load tz %}
<head>
<meta charset="UTF-16">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>{{ batch.batch }}</title>
</head>
<body>
{% for inquiry in inquiries %}
<div id="content" >
<div >
<div >
1. Full Name: {{ candidate.name }} <br>
2. Nationality: {{ candidate.nationality }} <br>
<hr>
</div>
<div >
{% for address in addresses %}
{% if address.address_type == 'Permanant Address' %}
3. Permanant Address: {{ address.address_line_1 }}, {{ address.city }}, {{ address.state }}, {{ address.country }}
{% endif %}
{% endfor %} <br>
{% for address in addresses %}
{% if address.address_type == 'Present Address' %}
4. Present Address: {{ address.address_line_1 }}, {{ address.city }}, {{ address.state }}, {{ address.country }}
{% endif %}
{% endfor %}
</div> <br>
<hr>
<br>
<div>
<div>
<p>
Report Generated at:
{% localtime on %}
{{ print_time|timezone:"Asia/Dhaka" }}
{% endlocaltime %}
</p>
</div>
</div>
<div ></div>
{% endfor %}
</body>
</html>
CodePudding user response:
There is no need to pass Candidate
object in the context. Use inquiry.candidate
to access the candidate attached to each inquiry. Same for addresses. Use inquiry.candidate.address_set.all
to loop through those.
Also, there is no need for the for loop in your view since you're doing it in the report. In any case, on the first run of the loop, the code will reach return and exit the function.