Usage
=====
This guide covers the various ways to use django-cloudflareimages-toolkit in your Django applications.
Model Field Usage
-----------------
The simplest way to use Cloudflare Images is with the ``CloudflareImageField``:
.. code-block:: python
from django.db import models
from django_cloudflareimages_toolkit.fields import CloudflareImageField
class Profile(models.Model):
name = models.CharField(max_length=100)
avatar = CloudflareImageField()
class Product(models.Model):
name = models.CharField(max_length=100)
description = models.TextField()
image = CloudflareImageField()
Direct Upload Service
---------------------
For programmatic image uploads, use ``CloudflareImagesService``. The
service exposes two related operations: generating a one-time upload
URL (for handing to a browser or another service) and the full
end-to-end flow of uploading a local file from disk straight to
Cloudflare.
Generating a direct upload URL
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.. code-block:: python
from django_cloudflareimages_toolkit.services import cloudflare_service
# Returns a CloudflareImage row with the upload_url populated and
# status=PENDING. The same row will move to UPLOADED once Cloudflare
# finishes processing the upload (you can poll via check_image_status
# or wire up the WebhookView — see docs/webhooks.rst).
image = cloudflare_service.create_direct_upload_url(
user=request.user,
metadata={"category": "profile", "user_id": str(request.user.pk)},
require_signed_urls=False,
expiry_minutes=30,
)
print(image.cloudflare_id) # e.g. "2cdc28f0-017a-49c4-9ed7-..."
print(image.upload_url) # one-time URL — POST the file here
Server-side: uploading a local file from disk
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
A common pattern is to upload an image stored on the Django server
(generated thumbnail, imported asset, content-management workflow)
straight to Cloudflare without involving a browser. The toolkit drives
this through the same Direct Creator Upload primitive — get an upload
URL from Cloudflare, then ``POST`` the file bytes to it.
.. code-block:: python
import requests
from django_cloudflareimages_toolkit.services import cloudflare_service
from django_cloudflareimages_toolkit.exceptions import CloudflareImagesError
def upload_local_image(path: str, *, user=None, metadata=None):
"""Upload a file from disk to Cloudflare Images.
Returns the populated CloudflareImage row.
"""
# 1. Reserve a one-time upload slot. The returned CloudflareImage
# is already persisted with status=PENDING, so a webhook or
# a later check_image_status() call has a row to update.
image = cloudflare_service.create_direct_upload_url(
user=user,
metadata=metadata or {},
expiry_minutes=30,
)
# 2. POST the bytes. Cloudflare's Direct Creator endpoint expects
# multipart/form-data with the file under the field name "file".
with open(path, "rb") as fh:
response = requests.post(
image.upload_url,
files={"file": (image.cloudflare_id, fh, "application/octet-stream")},
timeout=60,
)
if not response.ok:
raise CloudflareImagesError(
f"Cloudflare upload POST failed: {response.status_code} {response.text[:200]}"
)
# 3. Sync the local row with the now-uploaded state. This is
# idempotent — the webhook (if configured) will also drive
# this transition.
cloudflare_service.check_image_status(image)
image.refresh_from_db()
return image
# Usage
image = upload_local_image(
"/srv/media/incoming/banner.jpg",
user=request.user,
metadata={"source": "cms_import"},
)
print(image.status) # "uploaded"
print(image.public_url) # delivery URL
print(image.get_variant_url("thumbnail"))
The same pattern works for ``BytesIO`` and ``InMemoryUploadedFile`` —
swap the ``open(path, "rb")`` line for the file-like object you already
have.
.. note::
The upload URL is single-use and expires per ``expiry_minutes`` (2–360
minutes; default 30). If you batch hundreds of uploads, request one
URL per file rather than reusing — Cloudflare rejects reuse.
For production-grade retry, circuit-breaking, and graceful degradation
around this flow, see :doc:`patterns`.
Bulk uploads
~~~~~~~~~~~~
For batch jobs, push each ``upload_local_image`` call through a task
queue rather than running them inline. A Celery example:
.. code-block:: python
from celery import shared_task
from django_cloudflareimages_toolkit.exceptions import CloudflareImagesError
@shared_task(
bind=True,
autoretry_for=(CloudflareImagesError, requests.RequestException),
retry_backoff=True,
retry_backoff_max=300,
retry_jitter=True,
max_retries=8,
)
def upload_local_image_async(self, path: str, *, user_id: int | None = None):
from django.contrib.auth import get_user_model
user = get_user_model().objects.get(pk=user_id) if user_id else None
return upload_local_image(path, user=user).cloudflare_id
This pattern is covered in more detail (with circuit-breaker + cache
fallback) in :doc:`patterns`.
Frontend Integration
--------------------
Use the direct upload URLs for secure client-side uploads:
JavaScript Upload Example
~~~~~~~~~~~~~~~~~~~~~~~~~~
.. code-block:: html
.. code-block:: javascript
// JavaScript upload handler
document.getElementById('upload-form').addEventListener('submit', async (e) => {
e.preventDefault();
const fileInput = document.getElementById('image-input');
const file = fileInput.files[0];
if (!file) return;
try {
// Get upload URL from your Django backend
const response = await fetch('/api/get-upload-url/');
const uploadData = await response.json();
// Upload directly to Cloudflare
const formData = new FormData();
formData.append('file', file);
const uploadResponse = await fetch(uploadData.uploadURL, {
method: 'POST',
body: formData
});
if (uploadResponse.ok) {
const result = await uploadResponse.json();
console.log('Upload successful:', result);
// Save image reference in your Django app
await fetch('/api/save-image/', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-CSRFToken': getCookie('csrftoken')
},
body: JSON.stringify({
cloudflare_id: result.result.id,
filename: file.name
})
});
}
} catch (error) {
console.error('Upload failed:', error);
}
});
Django Views for Upload
-----------------------
Create views to handle upload URL generation and image saving:
Upload URL Generation
~~~~~~~~~~~~~~~~~~~~~
.. code-block:: python
from django.http import JsonResponse
from django.views.decorators.csrf import csrf_exempt
from django.contrib.auth.decorators import login_required
from django_cloudflareimages_toolkit.services import CloudflareImagesService
@login_required
def get_upload_url(request):
try:
service = CloudflareImagesService()
upload_data = service.get_direct_upload_url()
return JsonResponse({
'uploadURL': upload_data['uploadURL'],
'id': upload_data['id']
})
except Exception as e:
return JsonResponse({'error': str(e)}, status=500)
Image Saving
~~~~~~~~~~~~
.. code-block:: python
import json
from django.http import JsonResponse
from django.views.decorators.csrf import csrf_exempt
from django.contrib.auth.decorators import login_required
from django_cloudflareimages_toolkit.models import CloudflareImage
@csrf_exempt
@login_required
def save_image(request):
if request.method == 'POST':
try:
data = json.loads(request.body)
image = CloudflareImage.objects.create(
cloudflare_id=data['cloudflare_id'],
filename=data['filename'],
uploaded_by=request.user # If you have this field
)
return JsonResponse({
'success': True,
'image_id': image.id,
'url': image.get_url()
})
except Exception as e:
return JsonResponse({'error': str(e)}, status=500)
return JsonResponse({'error': 'Method not allowed'}, status=405)
Using in Django Forms
---------------------
Integrate Cloudflare Images with Django forms:
Form Definition
~~~~~~~~~~~~~~~
.. code-block:: python
from django import forms
from django_cloudflareimages_toolkit.fields import CloudflareImageField
class ProfileForm(forms.ModelForm):
class Meta:
model = Profile
fields = ['name', 'avatar']
widgets = {
'avatar': forms.HiddenInput(), # Hidden field for image ID
}
class ProductForm(forms.Form):
name = forms.CharField(max_length=100)
description = forms.CharField(widget=forms.Textarea)
image = forms.CharField(widget=forms.HiddenInput()) # Store Cloudflare ID
Form Template
~~~~~~~~~~~~~
.. code-block:: html
Image Transformations
---------------------
The toolkit provides powerful image transformation capabilities using Cloudflare's
flexible variants and Image Resizing features.
CloudflareImageTransform
~~~~~~~~~~~~~~~~~~~~~~~~
Use ``CloudflareImageTransform`` to build transformation URLs:
.. code-block:: python
from django_cloudflareimages_toolkit.transformations import CloudflareImageTransform
# For Cloudflare Images (imagedelivery.net)
# Transforms are applied as path-based options: width=300,height=200
transform = CloudflareImageTransform(image.public_url)
thumbnail_url = (transform
.width(300)
.height(300)
.fit('cover')
.quality(85)
.build())
# Result: https://imagedelivery.net///width=300,height=300,fit=cover,quality=85
# For Image Resizing on custom domains (cdn-cgi format)
transform = CloudflareImageTransform("/images/photo.jpg", zone="example.com")
resized_url = transform.width(800).quality(85).build()
# Result: https://example.com/cdn-cgi/image/width=800,quality=85/images/photo.jpg
Available Transformations
~~~~~~~~~~~~~~~~~~~~~~~~~
.. code-block:: python
transform = CloudflareImageTransform(base_url)
# Dimensions
transform.width(800) # Width in pixels (1-12000)
transform.height(600) # Height in pixels (1-12000)
# Fit modes
transform.fit('scale-down') # Scale down only, never enlarge
transform.fit('contain') # Fit within dimensions, preserve aspect ratio
transform.fit('cover') # Fill dimensions, crop if needed
transform.fit('crop') # Crop to exact dimensions
transform.fit('pad') # Pad to dimensions with background
# Quality and format
transform.quality(85) # Quality 1-100
transform.format('webp') # Output format: auto, webp, avif, jpeg, json
# Visual adjustments
transform.blur(10) # Blur amount 1-250
transform.sharpen(2.0) # Sharpen 0.0-10.0
transform.brightness(0.1) # Brightness -1.0 to 1.0
transform.contrast(0.1) # Contrast -1.0 to 1.0
transform.gamma(1.2) # Gamma 0.1-9.9
# Cropping and positioning
transform.gravity('auto') # auto, left, right, top, bottom, center
transform.rotate(90) # Rotation: 0, 90, 180, 270
# Borders and background
transform.background('ffffff') # Background color (hex)
transform.border(2, 'cccccc') # Border width and color
# Device pixel ratio
transform.dpr(2.0) # DPR 1.0-3.0 for retina displays
Predefined Variants
~~~~~~~~~~~~~~~~~~~
Use ``CloudflareImageVariants`` for common use cases:
.. code-block:: python
from django_cloudflareimages_toolkit.transformations import CloudflareImageVariants
# Square thumbnail
thumbnail = CloudflareImageVariants.thumbnail(image.public_url, 150)
# Circular avatar (requires CSS border-radius)
avatar = CloudflareImageVariants.avatar(image.public_url, 100)
# Hero/banner image
hero = CloudflareImageVariants.hero_image(image.public_url, 1920, 800)
# Responsive image
responsive = CloudflareImageVariants.responsive_image(image.public_url, 800)
# Product image with white background
product = CloudflareImageVariants.product_image(image.public_url, 400)
# Mobile-optimized WebP
mobile = CloudflareImageVariants.mobile_optimized(image.public_url, 400)
Responsive Images
~~~~~~~~~~~~~~~~~
Generate srcset for responsive images:
.. code-block:: python
from django_cloudflareimages_toolkit.transformations import CloudflareImageUtils
# Generate srcset attribute
srcset = CloudflareImageUtils.get_srcset(
image.public_url,
widths=[320, 640, 1024, 1920],
quality=85
)
# Result: "url 320w, url 640w, url 1024w, url 1920w"
# Generate sizes attribute
sizes = CloudflareImageUtils.get_sizes_attribute({
'max-width: 768px': 100, # 100vw on mobile
'max-width: 1024px': 50, # 50vw on tablet
'default': 800 # 800px on desktop
})
Template Usage
--------------
Display images in your Django templates:
Basic Image Display
~~~~~~~~~~~~~~~~~~~
.. code-block:: html
{% if profile.avatar %}
{% else %}
{% endif %}
Advanced Template Usage
~~~~~~~~~~~~~~~~~~~~~~~
.. code-block:: html
{% for product in products %}
{{ product.name }}
{{ product.description|truncatewords:20 }}
{% endfor %}
Image Management
----------------
Programmatically manage images using the service:
List Images
~~~~~~~~~~~
.. code-block:: python
from django_cloudflareimages_toolkit.services import CloudflareImagesService
service = CloudflareImagesService()
# List all images
images = service.list_images()
for image in images['result']['images']:
print(f"Image ID: {image['id']}")
print(f"Filename: {image['filename']}")
print(f"Uploaded: {image['uploaded']}")
Get Image Details
~~~~~~~~~~~~~~~~~
.. code-block:: python
# Get specific image details
image_id = "your-image-id"
image_details = service.get_image(image_id)
print(f"Image URL: {image_details['result']['variants'][0]}")
print(f"Metadata: {image_details['result']['meta']}")
Delete Images
~~~~~~~~~~~~~
.. code-block:: python
# Delete an image
image_id = "your-image-id"
result = service.delete_image(image_id)
if result['success']:
print("Image deleted successfully")
Webhook Handling
----------------
Handle real-time upload notifications:
Webhook View
~~~~~~~~~~~~
.. code-block:: python
import json
import hmac
import hashlib
from django.http import HttpResponse
from django.views.decorators.csrf import csrf_exempt
from django.conf import settings
from django_cloudflareimages_toolkit.models import CloudflareImage
@csrf_exempt
def cloudflare_webhook(request):
if request.method == 'POST':
# Verify webhook signature
signature = request.headers.get('CF-Webhook-Signature')
if not verify_webhook_signature(request.body, signature):
return HttpResponse(status=401)
try:
data = json.loads(request.body)
# Handle upload completion
if data.get('event') == 'upload.complete':
image_id = data['data']['id']
# Update image status
try:
image = CloudflareImage.objects.get(cloudflare_id=image_id)
image.is_ready = True
image.file_size = data['data'].get('size')
image.width = data['data'].get('width')
image.height = data['data'].get('height')
image.format = data['data'].get('format')
image.save()
except CloudflareImage.DoesNotExist:
# Create new image record if it doesn't exist
CloudflareImage.objects.create(
cloudflare_id=image_id,
filename=data['data'].get('filename', ''),
is_ready=True,
file_size=data['data'].get('size'),
width=data['data'].get('width'),
height=data['data'].get('height'),
format=data['data'].get('format')
)
return HttpResponse(status=200)
except Exception as e:
return HttpResponse(status=500)
return HttpResponse(status=405)
def verify_webhook_signature(payload, signature):
webhook_secret = settings.CLOUDFLARE_IMAGES.get('WEBHOOK_SECRET')
if not webhook_secret:
return False
expected_signature = hmac.new(
webhook_secret.encode(),
payload,
hashlib.sha256
).hexdigest()
return hmac.compare_digest(signature, expected_signature)
Admin Integration
-----------------
The package provides Django admin integration:
Custom Admin Configuration
~~~~~~~~~~~~~~~~~~~~~~~~~~
.. code-block:: python
from django.contrib import admin
from django_cloudflareimages_toolkit.admin import CloudflareImageAdmin
from django_cloudflareimages_toolkit.models import CloudflareImage
# Customize the admin interface
@admin.register(CloudflareImage)
class CustomCloudflareImageAdmin(CloudflareImageAdmin):
list_display = ['filename', 'uploaded_at', 'file_size', 'is_ready', 'image_preview']
list_filter = ['is_ready', 'format', 'uploaded_at']
search_fields = ['filename', 'cloudflare_id']
readonly_fields = ['cloudflare_id', 'uploaded_at', 'file_size', 'width', 'height']
def image_preview(self, obj):
if obj.is_ready:
return f'
'
return "Processing..."
image_preview.allow_tags = True
image_preview.short_description = "Preview"
Management Commands
-------------------
Use the provided management commands:
Cleanup Expired Images
~~~~~~~~~~~~~~~~~~~~~~
.. code-block:: bash
# Clean up expired upload URLs
python manage.py cleanup_expired_images
# Clean up images older than 7 days
python manage.py cleanup_expired_images --days 7
# Dry run to see what would be deleted
python manage.py cleanup_expired_images --dry-run
Testing
-------
Test image functionality in your Django tests:
.. code-block:: python
from django.test import TestCase
from unittest.mock import patch, MagicMock
from django_cloudflareimages_toolkit.services import CloudflareImagesService
from django_cloudflareimages_toolkit.models import CloudflareImage
class CloudflareImagesTestCase(TestCase):
@patch('django_cloudflareimages_toolkit.services.requests.post')
def test_get_direct_upload_url(self, mock_post):
# Mock the API response
mock_response = MagicMock()
mock_response.json.return_value = {
'success': True,
'result': {
'id': 'test-image-id',
'uploadURL': 'https://upload.imagedelivery.net/test-url'
}
}
mock_post.return_value = mock_response
service = CloudflareImagesService()
result = service.get_direct_upload_url()
self.assertEqual(result['id'], 'test-image-id')
self.assertIn('uploadURL', result)
def test_cloudflare_image_model(self):
image = CloudflareImage.objects.create(
cloudflare_id='test-id',
filename='test.jpg',
is_ready=True
)
self.assertEqual(str(image), 'test.jpg')
self.assertTrue(image.get_url().startswith('https://imagedelivery.net/'))
Best Practices
--------------
1. **Security First**: Always verify webhook signatures and validate uploads
2. **Error Handling**: Implement proper error handling for upload failures
3. **User Feedback**: Provide clear feedback during upload processes
4. **Image Optimization**: Use appropriate variants for different use cases
5. **Cleanup**: Regularly clean up expired upload URLs and unused images
6. **Testing**: Test upload functionality across different browsers and devices
7. **Monitoring**: Monitor upload success rates and performance
8. **Backup Strategy**: Consider backup strategies for critical images
9. **Rate Limiting**: Implement rate limiting for upload endpoints
10. **Progressive Enhancement**: Ensure your app works without JavaScript for uploads
Performance Tips
----------------
1. **Use Variants**: Create and use appropriate image variants instead of resizing originals
2. **Lazy Loading**: Implement lazy loading for image-heavy pages
3. **CDN Benefits**: Leverage Cloudflare's global CDN for fast image delivery
4. **Async Uploads**: Use asynchronous uploads to improve user experience
5. **Batch Operations**: Batch multiple image operations when possible
6. **Caching**: Cache image URLs and metadata appropriately
7. **Compression**: Use appropriate image formats and compression settings