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
{% csrf_token %} {{ form.name }}
{{ form.avatar }}
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 Profile Avatar Avatar Thumbnail {% if profile.avatar %} Profile Avatar {% else %} Default Avatar {% endif %} Advanced Template Usage ~~~~~~~~~~~~~~~~~~~~~~~ .. code-block:: html 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