Use Celery to make pdf from HTML page
Why Use Celery in Django? A Complete Guide to Installing and Using Celery 🚀
Django is a powerful web framework for building scalable applications, but what if you need to run background tasks, send emails asynchronously, or process large amounts of data without slowing down your app? This is where Celery comes in!
In this blog, we’ll explore why Celery is essential for Django, how to install it, and how to use it effectively in your projects.
Why Use Celery in Django?
Django handles user requests synchronously, meaning if a task takes too long, the user has to wait. This is problematic for:
✅ Sending Emails – A user shouldn’t have to wait for an email confirmation to be sent.
✅ Generating Reports – Large PDF or Excel reports can take time.
✅ Processing Payments – Payment processing should happen in the background.
✅ Database Backups – Running backups without affecting performance.
✅ Scraping & API Calls – Making API requests without blocking the app.
Celery solves this problem by handling tasks asynchronously in the background using worker processes.
Installing Celery in Django
Before using Celery, ensure you have Redis or RabbitMQ and for windows you can use Memurai installed as a message broker. Redis is the most commonly used and easy to set up.
1️⃣ Install Redis
For Linux/macOS, install Redis using:
sudo apt install redis # Ubuntu/Debian
brew install redis # macOS (Homebrew)
Configuring Celery in Django
1️⃣ Update Django Settings
Open your settings.py
and add the following configuration for Redis/Memurai:
CELERY_BROKER_URL = 'redis://localhost:6379/0'
CELERY_RESULT_BACKEND = 'redis://localhost:6379'
CELERY_ACCEPT_CONTENT = ['application/json']
CELERY_RESULT_SERIALIZER = 'json'
CELERY_TASK_SERIALIZER = 'json'
INSTALLED_APPS = [
# Other apps...
'django_celery_beat',
]
2️⃣ Create a Celery Configuration File
Inside your Django project folder, create a new file celery.py
:
app.conf.beat_schedule = {
"generate_pdf_daily": {
"task": "weatherapp.tasks.generate_pdf",
"schedule": crontab(minute="*"), # Runs after every five minutes
'args': ('weather.html', {"title": "Generated PDF", "content": "This PDF was created using Jinja2."}, 'generated_report.pdf'),
},
}
3️⃣ Modify __init__.py
to Load Celery Automatically
Open __init__.py
inside your project folder and add:
from .celery import app as celery_app
__all__ = ("celery_app",)
Using Celery: Creating Your First Task
Let’s create a simple Celery task inside a Django app (e.g., tasks.py
).
1️⃣ Create a Celery Task
Inside one of your Django apps (e.g., myapp
), create a tasks.py
file:
write code for generating pdf
import json
import os
from celery import shared_task
import pdfkit
from jinja2 import Environment, FileSystemLoader
from PyPDF2 import PdfMerger
from django.conf import settings
@shared_task
def generate_pdf(json_data, filename):
"""Generate PDF from a Jinja2 template"""
template_data = json.loads(json_data)
# Load Jinja2 template
template_dir = os.path.join(settings.BASE_DIR, "weatherapp/templates")
env = Environment(loader=FileSystemLoader(template_dir))
pdf_files = []
try:
for i, item in enumerate(template_data):
template_name = item["template"]
context = item["context"]
# Render template
template = env.get_template(template_name)
html_content = template.render(context)
# Convert HTML to PDF
pdf_options = {
'page-size': 'A4',
'encoding': 'UTF-8',
'no-outline': None, # Prevents missing characters in some cases
'enable-local-file-access': '', # Needed if using local fonts
'no-stop-slow-scripts': '', # Allows JavaScript to load images before rendering
'javascript-delay': 10000, # Waits 10 seconds to load all resources (adjust as needed)
'enable-local-file-access': '', # Allow access to local and external image
}
pdf_data = pdfkit.from_string(html_content, False, options=pdf_options)
# Save each PDF separately
pdf_path = os.path.join(settings.MEDIA_ROOT, f"pdfs/{filename}_part_{i}.pdf")
with open(pdf_path, "wb") as f:
f.write(pdf_data)
pdf_files.append(pdf_path)
# Merge all PDFs into one
merger = PdfMerger()
for pdf in pdf_files:
merger.append(pdf)
merged_pdf_path = os.path.join(settings.MEDIA_ROOT, f"pdfs/{filename}.pdf")
merger.write(merged_pdf_path)
merger.close()
return merged_pdf_path
except Exception as e:
return {"error": str(e)}
2️⃣ Calling Celery Tasks in Django
You can call this Celery task in Django views or models:
from django.shortcuts import render
from .weather_utils import fetch_weather_data
from django.http import JsonResponse
from .tasks import generate_pdf
import json
def weather_view(request):
weather_details = fetch_weather_data()
return render(request, "weather.html", {"weather_details": weather_details})
def page_copy(request):
return render(request, 'page_copy.html')
def hidden_widget(request):
return render(request, 'hidden_search_widget.html')
def generate_pdf_view(request):
"""Fetch weather data and trigger PDF generation asynchronously"""
weather_details = fetch_weather_data() # Ensure data is available
template_data = [
{"template": "weather.html", "context": {"weather_details": weather_details}},
{"template": "hidden_search_widget.html", "context": {"page_details": "This is some text for page copy"}},
]
#template_name = "weather.html"
#context = {"weather_details": weather_details} # Pass actual data
filename = "generated_weather_report"
json_data = json.dumps(template_data)
task = generate_pdf.delay(json_data, filename)
return JsonResponse({"task_id": task.id, "status": "Processing"})
3️⃣ Start Celery Worker
Run the Celery worker in a new terminal window:
celery -A your_project worker --loglevel=info
Post a Comment