This repository contains the code for Step 1 of a 10-part YouTube tutorial series aimed at a technical Uzbek-speaking audience. The series demonstrates how to build and optimize a Django Rest Framework (DRF) API that generates PDF contract files in real-time after a user submits an order. In this step, we create a basic DRF API, use weasyprint
to generate styled PDFs from HTML templates, save the PDFs to the media
folder, and perform a baseline performance test using Locust.
- Objective: Build a DRF API that allows users to create orders and generates a styled PDF contract, saved to the
media
folder, with a URL returned in the response. - Tech Stack:
- Django 4.x
- Django Rest Framework
- WeasyPrint (for HTML-to-PDF conversion)
- SQLite (default database)
- Locust (for performance testing)
- Features:
- POST endpoint (
/api/orders/create/
) to create an order and generate a PDF. - Styled PDF generated from an HTML template with CSS.
- PDF saved to
media/contracts/
folder. - JSON response with the PDF URL.
- Baseline performance test with Locust.
- POST endpoint (
- Python 3.8+
- pip and virtualenv
- System dependencies for WeasyPrint (e.g.,
libcairo2
,libpango-1.0-0
):-
On Ubuntu:
sudo apt-get install python3-dev python3-pip python3-setuptools python3-wheel libcairo2 libpango-1.0-0 libpangocairo-1.0-0 libgdk-pixbuf2.0-0 libffi-dev shared-mime-info
-
On macOS:
brew install python3 cairo pango libffi gdk-pixbuf
-
-
Clone the repository and checkout the
step-1-html-to-pdf
branch:git clone <repository-url> cd <repository> git checkout step-1-html-to-pdf
-
Create and activate a virtual environment:
python -m venv venv source venv/bin/activate # On Windows: venv\Scripts\activate
-
Install dependencies:
pip install -r requirements.txt
Note: If
requirements.txt
is not present, install manually:pip install django djangorestframework weasyprint locust
-
Apply database migrations:
python manage.py makemigrations python manage.py migrate
-
Create the
media
folder in the project root:mkdir media
-
Run the development server:
python manage.py runserver
- POST /api/orders/create/
- Creates an order and generates a PDF contract.
- Saves the PDF to
media/contracts/
. - Returns a JSON response with the order details and PDF URL.
curl -X POST http://127.0.0.1:8000/api/orders/create/ \
-H "Content-Type: application/json" \
-d '{"customer_name": "Ali Valiyev", "amount": 500.00}'
{
"id": 1,
"customer_name": "Ali Valiyev",
"amount": "500.00",
"created_at": "2025-04-19T12:00:00Z",
"contract_pdf": "/media/contracts/contract_1.pdf"
}
The PDF can be accessed at http://127.0.0.1:8000/media/contracts/contract_1.pdf
.
-
Ensure Locust is installed:
pip install locust
-
Run Locust:
locust
-
Open
http://localhost:8089
in a browser and configure:- Number of users: 50
- Spawn rate: 5 users/second
- Test duration: 60 seconds
-
Environment: Local machine (results may vary).
-
Results:
Metric Value O'rtacha javob vaqti 15ms Median 13ms 95%ile 28ms 99%ile 50ms So'rovlar/sekund 14 Average size (bytes) ~150 Xatolar 0%
- Response Time: ~15ms average due to HTML rendering and PDF generation with
weasyprint
. - Throughput: ~14 requests/second, limited by CPU-intensive PDF generation.
- Comparison with Previous (Step 1 with
reportlab
):- Previous: 11.39ms average, 16.8 req/s (faster but less styled).
- Current: Slower (~32%) but supports complex, styled PDFs via HTML/CSS.
- Trade-offs:
- Pros: Easier to create and maintain complex PDF designs using HTML/CSS.
- Cons: Higher CPU usage due to
weasyprint
processing.
pdf_api/
├── config/ # Django project settings
├── orders/ # Orders app
│ ├── templates/orders/ # HTML templates for PDF
│ │ └── contract.html
│ ├── __init__.py
│ ├── admin.py
│ ├── apps.py
│ ├── migrations/
│ ├── models.py # Order model with contract_pdf field
│ ├── serializers.py # DRF serializer
│ ├── urls.py # App URLs
│ ├── utils.py # PDF generation logic
│ └── views.py # API view
├── media/ # Folder for saved PDFs
├── locustfile.py # Locust test script
├── manage.py
└── requirements.txt # Dependencies
- HTML-to-PDF: Using
weasyprint
simplifies the creation of styled PDFs (e.g., tables, colors, fonts) compared toreportlab
, which requires manual coordinate-based design. - Media Storage: Saving PDFs to the
media
folder allows users to download them later, improving user experience and reducing network bandwidth (JSON response vs. streaming large PDFs). - Baseline Testing: Locust provides a clear starting point for performance optimization in later steps.
- Step 2: Switch to PostgreSQL and optimize database queries.
- Future Optimizations:
- Asynchronous PDF generation with Celery.
- Caching and database indexing.
- Infrastructure scaling with Docker, Gunicorn, Nginx, and load balancing.