API Reference
This section provides detailed documentation for all classes, methods, and functions in django-cloudflareimages-toolkit.
CloudflareImagesService Class
- class django_cloudflareimages_toolkit.services.CloudflareImagesService[source]
Bases:
objectService class for Cloudflare Images API operations.
- REQUEST_TIMEOUT = 30
- property session: Session
- get_direct_upload_url(user=None, custom_id: str | None = None, metadata: dict[str, Any] | None = None, require_signed_urls: bool | None = None, expiry_minutes: int | None = None, creator: str | None = None) dict[str, str][source]
Get a one-time upload URL for direct creator upload.
This is an alias for create_direct_upload_url that returns a dict to match the documentation examples.
- create_direct_upload_url(user=None, custom_id: str | None = None, metadata: dict[str, Any] | None = None, require_signed_urls: bool | None = None, expiry_minutes: int | None = None, creator: str | None = None) CloudflareImage[source]
Create a one-time upload URL for direct creator upload.
Settings-backed defaults are applied for any argument left as
None:require_signed_urlsandexpiry_minutesfrom their respective settings,creatorfromDEFAULT_CREATOR, andmetadatais merged on top ofDEFAULT_METADATA(per-request keys win).- Parameters:
user – Django user instance (optional)
custom_id – Custom ID for the image (optional)
metadata – Additional metadata to store with the image
require_signed_urls – Whether to require signed URLs
expiry_minutes – Minutes until the upload URL expires
creator – Cloudflare
creatorvalue to associate with the image
- Returns:
CloudflareImage instance with upload URL
- Raises:
CloudflareImagesError – If the API request fails
- check_image_status(image: CloudflareImage) dict[str, Any][source]
Check the status of an image upload.
- Parameters:
image – CloudflareImage instance
- Returns:
Dictionary containing the image status data
- Raises:
CloudflareImagesError – If the API request fails
- list_images(page: int = 1, per_page: int = 1000) dict[str, Any][source]
List images from Cloudflare Images.
- Parameters:
page – Page number for pagination (default: 1)
per_page – Number of images per page (default: 1000; clamped to the Cloudflare maximum,
MAX_LIST_PER_PAGE)
- Returns:
Dictionary with pagination info and list of images
- Raises:
CloudflareImagesError – If the API request fails
- get_image(image_id: str) dict[str, Any][source]
Get details for a specific image.
- Parameters:
image_id – Cloudflare image ID
- Returns:
Dictionary with image details
- Raises:
CloudflareImagesError – If the API request fails
- register_uploaded_image(cloudflare_id: str, user=None, expected_creator: str | None = None) CloudflareImage[source]
Verify an uploaded image against Cloudflare and persist it locally.
This is the safe alternative to
CloudflareImage.objects.get_or_create( cloudflare_id=<client-supplied id>): it fetches the image details from Cloudflare, confirms the image exists and that its draft state is cleared (bytes were actually uploaded), and only then creates/returns the local record with status, variants, and metadata populated from the Cloudflare response.- Parameters:
cloudflare_id – The Cloudflare image ID reported by the client.
user – Django user to associate with the image (optional).
expected_creator – If given, the Cloudflare
creatoron the image must equal this value orImageOwnershipErroris raised before any local row is created. Use it (e.g. with the uploader’s id) to stop a caller registering another user’s image by submitting an arbitrary id from the same Cloudflare account.
- Returns:
The created or updated CloudflareImage instance.
- Raises:
ImageNotFoundError – If the image does not exist in Cloudflare.
ImageNotReadyError – If the image exists but is still a draft.
ImageOwnershipError – If
expected_creatordoes not match.CloudflareImagesError – For other Cloudflare API failures.
- update_image(image_id: str, metadata: dict[str, Any] | None = None, require_signed_urls: bool | None = None) dict[str, Any][source]
Update image metadata and settings.
- Parameters:
image_id – Cloudflare image ID
metadata – New metadata for the image
require_signed_urls – Whether to require signed URLs
- Returns:
Dictionary with updated image details
- Raises:
CloudflareImagesError – If the API request fails
- delete_image(image: CloudflareImage, *, missing_ok: bool = False) bool[source]
Delete an image from Cloudflare Images.
A Cloudflare
404(the image is already absent) is surfaced as the typedImageNotFoundError, mirroringget_image(). BecauseImageNotFoundErrorsubclassesCloudflareImagesError, existingexcept CloudflareImagesErrorcallers keep matching it.- Parameters:
image – CloudflareImage instance
missing_ok – When
True, a Cloudflare404(image already gone) is treated as a successful delete and returnsTrueinstead of raising. The desired end state for a delete is “not in Cloudflare”, so callers whose job is to converge on that state (orphan cleanup, the admin delete action, the viewset delete) passmissing_ok=Trueand remain idempotent across repeated or partially-failed runs.
- Returns:
True if deletion was successful (or the image was already absent and
missing_okis set).- Raises:
ImageNotFoundError – Cloudflare returned 404 and
missing_okis False.CloudflareImagesError – For any other API/transport failure.
- validate_webhook_signature(payload: bytes, signature: str) bool[source]
Validate webhook signature from Cloudflare.
- Parameters:
payload – Raw webhook payload
signature – Signature from webhook headers (should be in format ‘sha256=…’)
- Returns:
True if signature is valid
- process_webhook(payload: dict[str, Any]) CloudflareImage | None[source]
Process webhook payload from Cloudflare.
Returns the updated
CloudflareImagewhen the payload matches a known image, orNoneonly for the genuine unknown image case — a missing id or no matching local row. The caller (WebhookView) mapsNoneto a 404.Crucially, this does NOT swallow unexpected errors. A transient failure (e.g. a DB hiccup, or a save error inside
update_from_cloudflare_response) is allowed to propagate so the view’s existing 500 path runs and Cloudflare retries delivery. The previous catch-allexcept Exception: return Nonereported such recoverable errors as a 404 (“no such image, don’t retry”) — the opposite of what idempotent webhook delivery needs, and it made the view’s 500 branch unreachable.- Parameters:
payload – Webhook payload data
- Returns:
Updated CloudflareImage instance, or None if the image is unknown.
- Raises:
Exception – Any unexpected error while processing a known image is propagated to the caller (surfaced as a 500 so Cloudflare retries).
The main service class for interacting with Cloudflare Images API.
get_direct_upload_url Method
- CloudflareImagesService.get_direct_upload_url(user=None, custom_id: str | None = None, metadata: dict[str, Any] | None = None, require_signed_urls: bool | None = None, expiry_minutes: int | None = None, creator: str | None = None) dict[str, str][source]
Get a one-time upload URL for direct creator upload.
This is an alias for create_direct_upload_url that returns a dict to match the documentation examples.
Parameters:
metadata(dict, optional): Custom metadata to attach to the imagerequire_signed_urls(bool, optional): Whether to require signed URLs for accesscreator(str, optional): Cloudflare “creator” value to associate with the upload
Any argument left as None falls back to its settings default
(DEFAULT_METADATA, REQUIRE_SIGNED_URLS, DEFAULT_CREATOR).
Returns: dict with ‘id’ and ‘uploadURL’ keys
Raises:
CloudflareImagesError: When the API request fails, or the Cloudflare response reportssuccess: falseValueError: When a required setting (ACCOUNT_ID,API_TOKEN) is missing
Example:
from django_cloudflareimages_toolkit.services import CloudflareImagesService
service = CloudflareImagesService()
upload_data = service.get_direct_upload_url(
metadata={'category': 'profile', 'user_id': '123'}
)
print(f"Upload URL: {upload_data['uploadURL']}")
list_images Method
- CloudflareImagesService.list_images(page: int = 1, per_page: int = 1000) dict[str, Any][source]
List images from Cloudflare Images.
- Parameters:
page – Page number for pagination (default: 1)
per_page – Number of images per page (default: 1000; clamped to the Cloudflare maximum,
MAX_LIST_PER_PAGE)
- Returns:
Dictionary with pagination info and list of images
- Raises:
CloudflareImagesError – If the API request fails
Parameters:
page(int, optional): Page number for pagination (default: 1)per_page(int, optional): Number of images per page (default: 1000, max: 10000)
Returns: dict with pagination info and list of images
Example:
images = service.list_images(page=1, per_page=20)
for image in images['result']['images']:
print(f"Image ID: {image['id']}")
get_image Method
- CloudflareImagesService.get_image(image_id: str) dict[str, Any][source]
Get details for a specific image.
- Parameters:
image_id – Cloudflare image ID
- Returns:
Dictionary with image details
- Raises:
CloudflareImagesError – If the API request fails
Parameters:
image_id(str, required): Cloudflare image ID
Returns: dict with image details
Raises:
ImageNotFoundError: When Cloudflare returns a 404 for the image ID
Example:
image_details = service.get_image('your-image-id')
print(f"Image URL: {image_details['result']['variants'][0]}")
delete_image Method
- CloudflareImagesService.delete_image(image: CloudflareImage, *, missing_ok: bool = False) bool[source]
Delete an image from Cloudflare Images.
A Cloudflare
404(the image is already absent) is surfaced as the typedImageNotFoundError, mirroringget_image(). BecauseImageNotFoundErrorsubclassesCloudflareImagesError, existingexcept CloudflareImagesErrorcallers keep matching it.- Parameters:
image – CloudflareImage instance
missing_ok – When
True, a Cloudflare404(image already gone) is treated as a successful delete and returnsTrueinstead of raising. The desired end state for a delete is “not in Cloudflare”, so callers whose job is to converge on that state (orphan cleanup, the admin delete action, the viewset delete) passmissing_ok=Trueand remain idempotent across repeated or partially-failed runs.
- Returns:
True if deletion was successful (or the image was already absent and
missing_okis set).- Raises:
ImageNotFoundError – Cloudflare returned 404 and
missing_okis False.CloudflareImagesError – For any other API/transport failure.
Parameters:
image_id(str, required): Cloudflare image ID
Returns: dict with success status
Example:
result = service.delete_image('your-image-id')
if result['success']:
print("Image deleted successfully")
update_image Method
- CloudflareImagesService.update_image(image_id: str, metadata: dict[str, Any] | None = None, require_signed_urls: bool | None = None) dict[str, Any][source]
Update image metadata and settings.
- Parameters:
image_id – Cloudflare image ID
metadata – New metadata for the image
require_signed_urls – Whether to require signed URLs
- Returns:
Dictionary with updated image details
- Raises:
CloudflareImagesError – If the API request fails
Parameters:
image_id(str, required): Cloudflare image IDmetadata(dict, optional): New metadata for the imagerequire_signed_urls(bool, optional): Whether to require signed URLs
Returns: dict with updated image details
Example:
updated = service.update_image(
'your-image-id',
metadata={'updated': True, 'category': 'featured'}
)
CloudflareImage Model
- class django_cloudflareimages_toolkit.models.CloudflareImage(*args, **kwargs)[source]
Bases:
ModelModel to track Cloudflare image uploads.
- objects = <django_cloudflareimages_toolkit.models.CloudflareImageManager object>
- id
A wrapper for a deferred-loading field. When the value is read from this object the first time, the query is executed.
- cloudflare_id
A wrapper for a deferred-loading field. When the value is read from this object the first time, the query is executed.
- user
Accessor to the related object on the forward side of a many-to-one or one-to-one (via ForwardOneToOneDescriptor subclass) relation.
In the example:
class Child(Model): parent = ForeignKey(Parent, related_name='children')
Child.parentis aForwardManyToOneDescriptorinstance.
- filename
A wrapper for a deferred-loading field. When the value is read from this object the first time, the query is executed.
- original_filename
A wrapper for a deferred-loading field. When the value is read from this object the first time, the query is executed.
- content_type
A wrapper for a deferred-loading field. When the value is read from this object the first time, the query is executed.
- file_size
A wrapper for a deferred-loading field. When the value is read from this object the first time, the query is executed.
- upload_url
A wrapper for a deferred-loading field. When the value is read from this object the first time, the query is executed.
- status
A wrapper for a deferred-loading field. When the value is read from this object the first time, the query is executed.
- require_signed_urls
A wrapper for a deferred-loading field. When the value is read from this object the first time, the query is executed.
- metadata
A wrapper for a deferred-loading field. When the value is read from this object the first time, the query is executed.
- creator
A wrapper for a deferred-loading field. When the value is read from this object the first time, the query is executed.
- created_at
A wrapper for a deferred-loading field. When the value is read from this object the first time, the query is executed.
- updated_at
A wrapper for a deferred-loading field. When the value is read from this object the first time, the query is executed.
- uploaded_at
A wrapper for a deferred-loading field. When the value is read from this object the first time, the query is executed.
- expires_at
A wrapper for a deferred-loading field. When the value is read from this object the first time, the query is executed.
- width
A wrapper for a deferred-loading field. When the value is read from this object the first time, the query is executed.
- height
A wrapper for a deferred-loading field. When the value is read from this object the first time, the query is executed.
- format
A wrapper for a deferred-loading field. When the value is read from this object the first time, the query is executed.
- variants
A wrapper for a deferred-loading field. When the value is read from this object the first time, the query is executed.
- cloudflare_metadata
A wrapper for a deferred-loading field. When the value is read from this object the first time, the query is executed.
- last_referenced_at
A wrapper for a deferred-loading field. When the value is read from this object the first time, the query is executed.
- get_variant_url(variant_name: str) str | None[source]
Get the URL for a specific variant by name.
Cloudflare returns variants as full URLs like: https://imagedelivery.net/<hash>/<id>/<variant_name>
- Parameters:
variant_name – The variant name to look for (e.g., ‘public’, ‘thumbnail’)
- Returns:
The full variant URL if found, None otherwise
- get_url(variant: str = 'public') str | None[source]
Get the URL for a specific variant of the image.
- Parameters:
variant – The variant name (e.g., ‘public’, ‘thumbnail’, ‘avatar’)
- Returns:
The URL for the specified variant, or None if not found
- get_signed_url(variant: str = 'public', expiry: int = 3600) str | None[source]
Get a signed URL for a specific variant of the image.
- Parameters:
variant – The variant name (e.g., ‘public’, ‘thumbnail’, ‘avatar’)
expiry – Expiry time in seconds (default: 3600 = 1 hour)
- Returns:
A signed URL for the specified variant, or None if not available
Note
This method requires the image to have require_signed_urls=True and proper Cloudflare API integration for signing URLs.
- update_from_cloudflare_response(response_data: dict[str, Any]) None[source]
Update model fields from Cloudflare API response.
- exception DoesNotExist
Bases:
ObjectDoesNotExist
- exception MultipleObjectsReturned
Bases:
MultipleObjectsReturned
- exception NotUpdated
Bases:
ObjectNotUpdated,DatabaseError
- get_next_by_created_at(*, field=<django.db.models.fields.DateTimeField: created_at>, is_next=True, **kwargs)
- get_next_by_expires_at(*, field=<django.db.models.fields.DateTimeField: expires_at>, is_next=True, **kwargs)
- get_next_by_updated_at(*, field=<django.db.models.fields.DateTimeField: updated_at>, is_next=True, **kwargs)
- get_previous_by_created_at(*, field=<django.db.models.fields.DateTimeField: created_at>, is_next=False, **kwargs)
- get_previous_by_expires_at(*, field=<django.db.models.fields.DateTimeField: expires_at>, is_next=False, **kwargs)
- get_previous_by_updated_at(*, field=<django.db.models.fields.DateTimeField: updated_at>, is_next=False, **kwargs)
- get_status_display(*, field=<django.db.models.fields.CharField: status>)
- logs
Accessor to the related objects manager on the reverse side of a many-to-one relation.
In the example:
class Child(Model): parent = ForeignKey(Parent, related_name='children')
Parent.childrenis aReverseManyToOneDescriptorinstance.Most of the implementation is delegated to a dynamically defined manager class built by
create_reverse_many_to_one_manager()defined below.
- usages
Accessor to the related objects manager on the reverse side of a many-to-one relation.
In the example:
class Child(Model): parent = ForeignKey(Parent, related_name='children')
Parent.childrenis aReverseManyToOneDescriptorinstance.Most of the implementation is delegated to a dynamically defined manager class built by
create_reverse_many_to_one_manager()defined below.
- user_id
Django model for tracking Cloudflare Images.
Fields:
cloudflare_id(CharField): Unique Cloudflare image ID (max 255 characters)filename(CharField): Original filename (max 255 characters)uploaded_at(DateTimeField): Timestamp when image was uploadedfile_size(PositiveIntegerField): File size in bytes (optional)width(PositiveIntegerField): Image width in pixels (optional)height(PositiveIntegerField): Image height in pixels (optional)format(CharField): Image format (jpeg, png, gif, webp) (max 10 characters)variants(JSONField): Available image variantsmetadata(JSONField): Custom metadatacreator(CharField): Cloudflare “creator” value, indexed and queryable (max 255 characters, blank allowed)status(CharField): Upload status — one ofpending,draft,uploaded,failed,expiredupload_url(URLField): Direct upload URL (temporary)expires_at(DateTimeField): When the upload URL expires
is_ready, is_uploaded, is_expired, public_url, and
thumbnail_url are read-only properties, not stored fields.
Methods:
get_url Method
- CloudflareImage.get_url(variant: str = 'public') str | None[source]
Get the URL for a specific variant of the image.
- Parameters:
variant – The variant name (e.g., ‘public’, ‘thumbnail’, ‘avatar’)
- Returns:
The URL for the specified variant, or None if not found
Parameters:
variant(str, optional): Image variant name (default: ‘public’)
Returns: str - Full image URL
Example:
image = CloudflareImage.objects.get(cloudflare_id='your-id')
original_url = image.get_url()
thumbnail_url = image.get_url('thumbnail')
get_variant_url Method
- CloudflareImage.get_variant_url(variant_name: str) str | None[source]
Get the URL for a specific variant by name.
Cloudflare returns variants as full URLs like: https://imagedelivery.net/<hash>/<id>/<variant_name>
- Parameters:
variant_name – The variant name to look for (e.g., ‘public’, ‘thumbnail’)
- Returns:
The full variant URL if found, None otherwise
Parameters:
variant_name(str, required): Name of the variant to retrieve
Returns: str or None - Full variant URL if found, None otherwise
Retrieves a specific variant URL from the image’s variants list. This method searches through the stored variants (which may be a list of URLs or a dict) to find and return the URL matching the requested variant name.
Example:
image = CloudflareImage.objects.get(cloudflare_id='your-id')
# Get specific variant URLs
public_url = image.get_variant_url('public')
thumbnail_url = image.get_variant_url('thumbnail')
# Returns None if variant doesn't exist
custom_url = image.get_variant_url('nonexistent') # None
get_signed_url Method
- CloudflareImage.get_signed_url(variant: str = 'public', expiry: int = 3600) str | None[source]
Get a signed URL for a specific variant of the image.
- Parameters:
variant – The variant name (e.g., ‘public’, ‘thumbnail’, ‘avatar’)
expiry – Expiry time in seconds (default: 3600 = 1 hour)
- Returns:
A signed URL for the specified variant, or None if not available
Note
This method requires the image to have require_signed_urls=True and proper Cloudflare API integration for signing URLs.
Parameters:
variant(str, optional): Image variant nameexpiry(int, optional): URL expiry time in seconds
Returns: str - Signed image URL
Example:
# Get signed URL that expires in 1 hour
signed_url = image.get_signed_url('thumbnail', expiry=3600)
is_expired Property
- CloudflareImage.is_expired
Check if the upload URL has expired.
Returns: bool - True if upload URL has expired
Example:
if image.is_expired:
print("Upload URL has expired")
Manager Methods
register_uploaded Method
- objects.register_uploaded(cloudflare_id: str, user=None, expected_creator: str | None = None) CloudflareImage
Safely register an already-uploaded image by its
cloudflare_id.Unlike
get_or_create(cloudflare_id=...)with a client-supplied ID, this verifies the image against Cloudflare first: it confirms the image exists and that its draft state is cleared (bytes actually uploaded) before creating/returning the local record, then populates status, variants, and metadata from the Cloudflare response.- Parameters:
cloudflare_id – The Cloudflare image ID reported by the client.
user – Django user to associate with the image (optional).
expected_creator – If given, the Cloudflare
creatoron the image must equal this value (otherwiseImageOwnershipErroris raised before any local row is created). Pass the uploader’s id here when you setcreatorat upload time to enforce that a caller can only register their own image.
- Returns:
The created or updated CloudflareImage instance.
- Raises:
ImageNotFoundError – If the image does not exist in Cloudflare.
ImageNotReadyError – If the image exists but is still a draft.
ImageOwnershipError – If
expected_creatordoes not match.CloudflareImagesError – For other Cloudflare API failures.
Safely registers a client-supplied cloudflare_id. The manager fetches
the image from Cloudflare, confirms it exists and that its draft state is
cleared, then creates (or returns) the local record populated with status,
variants, metadata, and creator. No local row is created on failure.
Parameters:
cloudflare_id(str, required): Cloudflare image ID reported by the clientuser(optional): User to associate with the local recordexpected_creator(str, optional): When given, the Cloudflarecreatormust equal it orImageOwnershipErroris raised before any row is created
Returns: CloudflareImage - The created or existing local record
Raises:
ImageNotFoundError: When the image does not exist in CloudflareImageNotReadyError: When the image exists but is still a draftImageOwnershipError: Whenexpected_creatordoes not match the image’s creator, or when thecloudflare_idis already registered locally to a differentuserCloudflareImagesError: When thecloudflare_idis longer than the local column (255), or for other Cloudflare API failures
Example:
from django_cloudflareimages_toolkit import (
CloudflareImage, ImageNotFoundError, ImageNotReadyError, ImageOwnershipError,
)
try:
image = CloudflareImage.objects.register_uploaded(
cloudflare_id,
user=request.user,
expected_creator=str(request.user.pk), # optional ownership gate
)
except ImageNotFoundError:
... # id does not exist in Cloudflare
except ImageNotReadyError:
... # exists but upload not completed (still a draft)
except ImageOwnershipError:
... # image belongs to a different creator
Warning
Calling CloudflareImage.objects.get_or_create(cloudflare_id=<client value>)
directly is unsafe — the id may not exist, may be a draft, or may belong to
another user, and it leaves a bare local row. Use register_uploaded
instead.
ImageUsage Model
- class django_cloudflareimages_toolkit.models.ImageUsage(*args, **kwargs)[source]
Bases:
ModelReverse index: which content object references which Cloudflare image.
This is the missing half of the toolkit’s source of truth.
CloudflareImagerecords what has been uploaded;ImageUsagerecords where each image is used — the model instance and field that point at a givencloudflare_id.It is a derived index, not an independent source of truth: it is maintained automatically by signals on host models (see
registry/signals) and can always be rebuilt from those models with thereconcile_image_usagemanagement command.Two reverse-lookup helpers fall out of the schema:
orphaned images —
CloudflareImage.objects.filter(usages__isnull=True)(uploaded but referenced by no content), andunregistered references —
ImageUsage.objects.filter(image__isnull=True)(content points at an image the toolkit has noCloudflareImagerow for).
- content_type
Accessor to the related object on the forward side of a many-to-one or one-to-one (via ForwardOneToOneDescriptor subclass) relation.
In the example:
class Child(Model): parent = ForeignKey(Parent, related_name='children')
Child.parentis aForwardManyToOneDescriptorinstance.
- object_id
A wrapper for a deferred-loading field. When the value is read from this object the first time, the query is executed.
- content_object
Provide a generic many-to-one relation through the
content_typeandobject_idfields.This class also doubles as an accessor to the related object (similar to ForwardManyToOneDescriptor) by adding itself as a model attribute.
- field_name
A wrapper for a deferred-loading field. When the value is read from this object the first time, the query is executed.
- SOURCE_AUTO = 'auto'
- SOURCE_MANUAL = 'manual'
- SOURCE_CHOICES = [('auto', 'Auto'), ('manual', 'Manual')]
- source
A wrapper for a deferred-loading field. When the value is read from this object the first time, the query is executed.
- cloudflare_id
A wrapper for a deferred-loading field. When the value is read from this object the first time, the query is executed.
- image
Accessor to the related object on the forward side of a many-to-one or one-to-one (via ForwardOneToOneDescriptor subclass) relation.
In the example:
class Child(Model): parent = ForeignKey(Parent, related_name='children')
Child.parentis aForwardManyToOneDescriptorinstance.
- created_at
A wrapper for a deferred-loading field. When the value is read from this object the first time, the query is executed.
- updated_at
A wrapper for a deferred-loading field. When the value is read from this object the first time, the query is executed.
- exception DoesNotExist
Bases:
ObjectDoesNotExist
- exception MultipleObjectsReturned
Bases:
MultipleObjectsReturned
- exception NotUpdated
Bases:
ObjectNotUpdated,DatabaseError
- content_type_id
- get_next_by_created_at(*, field=<django.db.models.fields.DateTimeField: created_at>, is_next=True, **kwargs)
- get_next_by_updated_at(*, field=<django.db.models.fields.DateTimeField: updated_at>, is_next=True, **kwargs)
- get_previous_by_created_at(*, field=<django.db.models.fields.DateTimeField: created_at>, is_next=False, **kwargs)
- get_previous_by_updated_at(*, field=<django.db.models.fields.DateTimeField: updated_at>, is_next=False, **kwargs)
- get_source_display(*, field=<django.db.models.fields.CharField: source>)
- id
A wrapper for a deferred-loading field. When the value is read from this object the first time, the query is executed.
- image_id
- objects = <django.db.models.manager.Manager object>
Reverse index recording which content references which Cloudflare image. It is a
derived index maintained by signals and rebuildable with
reconcile_image_usage. See Usage for the full registry workflow.
Key fields:
content_type/object_id/content_object: the referencing model instancefield_name: the field holding the reference (e.g.avatar) ormanualcloudflare_id: the referenced Cloudflare image ID (source of truth)image: resolvedCloudflareImage(Nonemarks an unregistered reference)
Reverse lookups:
image.usages.all() # what references this image
CloudflareImage.objects.filter(usages__isnull=True) # orphaned (unused) images
ImageUsage.objects.filter(image__isnull=True) # referenced but unregistered
Image Usage Registry
- django_cloudflareimages_toolkit.registry.get_models_with_image_fields(refresh: bool = False) dict[source]
Return
{model_class: [field_name, ...]}for every CloudflareImageField.This is the single source of truth for where images can live, derived from the model definitions themselves so it never drifts from the code. Results are cached; pass
refresh=Trueto rebuild (useful in tests that declare models after the app registry is first inspected).
- django_cloudflareimages_toolkit.registry.register_usage(obj, cloudflare_id: str, field_name: str = 'manual')[source]
Manually record that
objreferencescloudflare_id.Use this for references the toolkit cannot discover automatically — an image ID kept in a
JSONField, fetched from another service, or derived at runtime rather than stored in aCloudflareImageField. Idempotent: calling again for the same(obj, field_name)updates the row in place.- Parameters:
obj – Any saved model instance that “owns” the reference.
cloudflare_id – The Cloudflare image ID being referenced.
field_name – A label distinguishing this reference from others on the same object (defaults to
"manual"). It must NOT match the name of aCloudflareImageFielddeclared onobj’s model: usage rows are unique on(content_type, object_id, field_name), so a colliding label would share a row with the auto-tracked field and the two references would overwrite each other.
- Returns:
The created or updated
ImageUsagerow.- Raises:
ValueError – If
field_namecollides with a tracked field on the model.
- django_cloudflareimages_toolkit.registry.unregister_usage(obj, field_name: str = 'manual') None[source]
Remove a manually-registered usage for
obj(idempotent).
register_usage / unregister_usage are also importable from the package
root. Use them for references the toolkit cannot discover automatically:
from django_cloudflareimages_toolkit import register_usage, unregister_usage
register_usage(obj, "cloudflare-image-id") # field_name="manual" by default
unregister_usage(obj)
ImageMetadataFactory
- class django_cloudflareimages_toolkit.ImageMetadataFactory[source]
Bases:
objectBase class for programmatically generating upload metadata.
Subclass and override
get_metadata(). Instances are callable, so the service layer can treat a factory instance and a plain callable uniformly.Example:
from django_cloudflareimages_toolkit.metadata import ImageMetadataFactory class TenantMetadataFactory(ImageMetadataFactory): def get_metadata(self, *, metadata, user=None, **context): if user is not None: metadata["uploaded_by"] = str(user.pk) metadata["source"] = "web" return metadata # settings.py CLOUDFLARE_IMAGES = { # ... "METADATA_FACTORY": "myapp.factories.TenantMetadataFactory", }
- get_metadata(*, metadata: dict[str, Any], user: Any = None, custom_id: str | None = None, creator: str | None = None, **context: Any) dict[str, Any][source]
Return the final metadata dict for an upload.
- Parameters:
metadata – The resolved metadata so far (
DEFAULT_METADATAmerged with the per-request metadata).user – The Django user associated with the upload, if any.
custom_id – The custom Cloudflare image ID, if provided.
creator – The resolved Cloudflare
creatorvalue, if any.**context – Forward-compatible additional context.
- Returns:
The metadata dict to send to Cloudflare. The base implementation returns
metadataunchanged.
Pluggable factory for computing the metadata attached to each upload.
Subclass it and override get_metadata; instances are callable.
Configure via the METADATA_FACTORY setting.
Method to override:
def get_metadata(self, *, metadata, user=None, custom_id=None,
creator=None, **context) -> dict
The factory receives the already-resolved metadata (DEFAULT_METADATA
merged with per-request metadata) plus upload context, and returns the final
metadata dict that is sent to Cloudflare and persisted. Merge precedence,
lowest to highest, is DEFAULT_METADATA < per-request metadata < factory
output.
Example:
from django_cloudflareimages_toolkit import ImageMetadataFactory
class TenantMetadataFactory(ImageMetadataFactory):
def get_metadata(self, *, metadata, user=None, **context):
if user is not None:
metadata['uploaded_by'] = str(user.pk)
metadata['source'] = 'web'
return metadata
CloudflareImageField
- class django_cloudflareimages_toolkit.fields.CloudflareImageField(variants: list[str] | None = None, metadata: dict[str, Any] | None = None, require_signed_urls: bool = False, max_file_size: int | None = None, allowed_formats: list[str] | None = None, **kwargs)[source]
Bases:
FieldA Django model field for storing Cloudflare Images.
This field stores the Cloudflare image ID and provides easy access to image URLs, variants, and metadata. It integrates with the CloudflareImage model and service layer.
- description = 'Cloudflare Image'
- __init__(variants: list[str] | None = None, metadata: dict[str, Any] | None = None, require_signed_urls: bool = False, max_file_size: int | None = None, allowed_formats: list[str] | None = None, **kwargs)[source]
Initialize the CloudflareImageField.
- Parameters:
variants – List of variant names to create for uploaded images
metadata – Default metadata to attach to uploaded images
require_signed_urls – Whether to require signed URLs for image access
max_file_size – Maximum file size in bytes (None for no limit)
allowed_formats – List of allowed image formats (jpeg, png, gif, webp)
**kwargs – Additional field options
- to_python(value: Any) CloudflareImageFieldValue | None[source]
Convert the database value to a Python object.
- Parameters:
value – The value from the database (Cloudflare image ID)
- Returns:
CloudflareImageFieldValue instance or None
- from_db_value(value: Any, expression, connection) CloudflareImageFieldValue | None[source]
Convert database value to Python object.
Django model field for Cloudflare Images integration.
Parameters:
variants(list, optional): List of variant names to createmetadata(dict, optional): Default metadata for uploadsrequire_signed_urls(bool, optional): Whether to require signed URLsmax_file_size(int, optional): Maximum file size in bytesallowed_formats(list, optional): List of allowed image formats
Example:
from django.db import models
from django_cloudflareimages_toolkit.fields import CloudflareImageField
class Product(models.Model):
name = models.CharField(max_length=100)
image = CloudflareImageField(
variants=['thumbnail', 'large'],
metadata={'category': 'product'},
max_file_size=5 * 1024 * 1024, # 5MB
allowed_formats=['jpeg', 'png']
)
CloudflareImageWidget
- class django_cloudflareimages_toolkit.widgets.CloudflareImageWidget(variants: list[str] | None = None, metadata: dict[str, Any] | None = None, require_signed_urls: bool = False, max_file_size: int | None = None, allowed_formats: list[str] | None = None, attrs: dict[str, Any] | None = None)[source]
Bases:
TextInputA widget for handling Cloudflare image uploads.
This widget renders a hidden input (which stores the Cloudflare image id) alongside a file input that drives the direct-upload flow implemented in the accompanying static JavaScript. The behaviour is loaded via the widget’s
Media; this class only builds the configuration the template and script consume.- template_name = 'django_cloudflareimages_toolkit/widgets/cloudflare_image_widget.html'
- __init__(variants: list[str] | None = None, metadata: dict[str, Any] | None = None, require_signed_urls: bool = False, max_file_size: int | None = None, allowed_formats: list[str] | None = None, attrs: dict[str, Any] | None = None)[source]
Initialize the widget.
- Parameters:
variants – List of image variants to create
metadata – Default metadata for uploads
require_signed_urls – Whether to require signed URLs
max_file_size – Maximum file size in bytes
allowed_formats – List of allowed image formats
attrs – Additional HTML attributes
- get_context(name: str, value: Any, attrs: dict[str, Any] | None = None) dict[str, Any][source]
Build the template context, assembling the config payload once.
- render(name: str, value: Any, attrs: dict[str, Any] | None = None, renderer=None) SafeString[source]
Render the widget HTML from the template.
Falls back to a minimal, equivalent markup block only if the package’s templates aren’t reachable on the loader path.
- class Media[source]
Bases:
objectDefine media files for the widget.
- css = {'all': ('django_cloudflareimages_toolkit/css/cloudflare_image_widget.css',)}
- js = ('django_cloudflareimages_toolkit/js/cloudflare_image_widget.js',)
- property media
Django form widget for handling Cloudflare image uploads with JavaScript-based upload functionality.
Django Admin Integration
- class django_cloudflareimages_toolkit.admin.CloudflareImageAdmin(model, admin_site)[source]
Bases:
ModelAdminAdmin interface for CloudflareImage model.
- change_list_template = 'admin/django_cloudflareimages_toolkit/cloudflareimage/change_list.html'
- list_display = ('cloudflare_id_display', 'user_display', 'status_display', 'filename_display', 'file_size_display', 'usage_count_display', 'created_at', 'expires_at', 'is_expired_display', 'thumbnail_preview', 'actions_display')
- list_filter = ('status', <class 'django_cloudflareimages_toolkit.admin.OrphanedImageFilter'>, 'require_signed_urls', 'created_at', 'uploaded_at', 'expires_at', ('user', <class 'django.contrib.admin.filters.RelatedOnlyFieldListFilter'>))
- search_fields = ('cloudflare_id', 'filename', 'original_filename', 'user__username', 'user__email')
- readonly_fields = ('id', 'cloudflare_id', 'upload_url_display', 'status', 'created_at', 'updated_at', 'uploaded_at', 'expires_at', 'width', 'height', 'format', 'variants_display', 'cloudflare_metadata_display', 'is_expired_display', 'is_uploaded_display', 'is_ready_display', 'public_url_display', 'thumbnail_url_display', 'image_preview', 'transformation_examples')
- fields = ('id', 'cloudflare_id', 'user', 'filename', 'original_filename', 'content_type', 'file_size', 'width', 'height', 'format', 'upload_url_display', 'status', 'require_signed_urls', 'metadata', 'created_at', 'updated_at', 'uploaded_at', 'expires_at', 'variants_display', 'cloudflare_metadata_display', 'is_expired_display', 'is_uploaded_display', 'is_ready_display', 'public_url_display', 'thumbnail_url_display', 'image_preview', 'transformation_examples')
- inlines = [<class 'django_cloudflareimages_toolkit.admin.ImageUsageInline'>, <class 'django_cloudflareimages_toolkit.admin.ImageUploadLogInline'>]
- actions = ['check_status_action', 'mark_as_expired', 'delete_from_cloudflare_action', 'refresh_all_status']
- list_per_page = 25
- date_hierarchy = 'created_at'
- changelist_view(request, extra_context=None)[source]
Expose the gallery/table view mode and a clean querystring.
- actions_display(obj)[source]
Display action buttons.
A per-row “Check status” link used to live here, but it called a client-side
checkStatus()that posted to a non-existent admin route (wrong app label, unregistered URL) and always 404’d. Per-image status checks are available via the bulk “Check status from Cloudflare” action instead, so the dead link was removed (see issue #22).
- class Media[source]
Bases:
object- js = ('admin/js/cloudflare_images_admin.js',)
- css = {'all': ('admin/css/cloudflare_images_admin.css',)}
- property media
Django admin interface for managing Cloudflare Images.
Features:
List view with image previews and metadata
Search functionality by filename and Cloudflare ID
Filtering by upload status, format, and date
Bulk delete operations
Image detail view with full metadata
Admin Actions:
delete_selected_images: Delete images from both Django and Cloudflarerefresh_image_metadata: Refresh metadata from Cloudflare APIgenerate_upload_urls: Generate new upload URLs for failed uploads
Example Customization:
from django.contrib import admin
from django_cloudflareimages_toolkit.admin import CloudflareImageAdmin
from django_cloudflareimages_toolkit.models import CloudflareImage
@admin.register(CloudflareImage)
class CustomCloudflareImageAdmin(CloudflareImageAdmin):
list_display = ['filename', 'uploaded_at', 'file_size', 'is_ready', 'image_preview']
list_filter = ['is_ready', 'format', 'uploaded_at']
Webhook Views
- class django_cloudflareimages_toolkit.views.WebhookView(**kwargs)[source]
Bases:
APIViewAPI view for handling Cloudflare webhooks.
- permission_classes = []
- post(request: HttpRequest) HttpResponse[source]
Handle incoming webhook from Cloudflare.
- Status codes returned:
200 — payload processed and matched an existing image
400 — payload failed JSON parse OR schema validation
- 401 — webhook_secret is configured but the request was
unauthenticated (missing or invalid signature)
404 — payload was valid but referenced an unknown image
500 — unexpected error while processing a validated payload
Note that 401 is only emitted when a
CLOUDFLARE_IMAGES.WEBHOOK_SECRETis configured. Deployments without a secret accept any well-formed payload — callers that want enforcement MUST set the secret.
- dispatch(request, *args, **kwargs)
.dispatch() is pretty much the same as Django’s regular dispatch, but with extra hooks for startup, finalize, and exception handling.
Handles Cloudflare Images webhook notifications. CSRF-exempt at the
view layer; security is enforced via HMAC signature verification when
CLOUDFLARE_IMAGES["WEBHOOK_SECRET"] is configured. See
Webhook Configuration for the full status-code matrix.
URL Pattern:
from django_cloudflareimages_toolkit.views import WebhookView
path('webhook/', WebhookView.as_view(), name='webhook')
Webhook Events:
upload.complete: Image upload and processing completedupload.failed: Image upload failedimage.deleted: Image was deleted
Example:
# urls.py
from django.urls import path, include
urlpatterns = [
path('cloudflare-images/', include('django_cloudflareimages_toolkit.urls')),
]
Management Commands
cleanup_expired_images Command
- class django_cloudflareimages_toolkit.management.commands.cleanup_expired_images.Command(stdout=None, stderr=None, no_color=False, force_color=False)[source]
Command to clean up expired image upload URLs.
- help = 'Clean up expired image upload URLs and mark them as expired'
Cleans up expired upload URLs and unused images.
Options:
--dry-run: Show what would be cleaned up without making changes--delete: Delete old expired images instead of only marking them expired--days(int): Delete images expired for at least this many days (default: 7)--delete-orphans: Delete uploaded images referenced by no content (orphans)--orphan-days(int): Only delete orphans older than this many days (default: 30)
Example:
# 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
# Delete orphaned (unreferenced) images older than 30 days
python manage.py cleanup_expired_images --delete-orphans --orphan-days 30
reconcile_image_usage Command
- class django_cloudflareimages_toolkit.management.commands.reconcile_image_usage.Command(stdout=None, stderr=None, no_color=False, force_color=False)[source]
Rebuild the image usage registry and report orphans/unregistered refs.
- help = 'Rebuild the image usage registry from host models and report orphans.'
Rebuilds the image usage registry from host models — the fix for bulk operations that bypass the sync signals — and reports orphans and unregistered references. Idempotent and safe to schedule.
Options:
--dry-run: Report what would change without writing.
python manage.py reconcile_image_usage
python manage.py reconcile_image_usage --dry-run
App Configuration
- class django_cloudflareimages_toolkit.apps.CloudflareImagesConfig(app_name, app_module)[source]
Bases:
AppConfigApp configuration for django_cloudflareimages_toolkit.
- default_auto_field = 'django.db.models.BigAutoField'
- name = 'django_cloudflareimages_toolkit'
- verbose_name = 'Cloudflare Images Toolkit'
Django app configuration class.
Attributes:
default_auto_field: Specifies BigAutoField as default primary keyname: App name for Django’s app registryverbose_name: Human-readable app name
Image URL Factory Class
- class django_cloudflareimages_toolkit.url_factory.CloudflareImageURLFactory(settings: CloudflareImagesSettings | None = None)[source]
Bases:
objectSingle source of truth for Cloudflare Images delivery URLs.
The factory builds delivery URLs from their components, recognizes whether an arbitrary URL is a delivery URL, extracts the image id from one, and rewrites Cloudflare’s
imagedelivery.netURLs into the configured shape.All behavior is driven by the
DELIVERY_URL,DELIVERY_PATH_PREFIX, andDELIVERY_INCLUDE_ACCOUNT_HASHsettings. WhenDELIVERY_URLis not configured, every method preserves the historicalimagedelivery.netbehavior.- DEFAULT_HOST = 'imagedelivery.net'
- DEFAULT_SCHEME = 'https'
- __init__(settings: CloudflareImagesSettings | None = None) None[source]
Initialize the factory.
- Parameters:
settings – A settings object exposing
delivery_url,delivery_path_prefix,delivery_include_account_hash, andaccount_hash. Defaults to the globalcloudflare_settingssingleton.
- property uses_custom_domain: bool
Whether an alternate
DELIVERY_URLis configured.
- property path_prefix: str
Normalized path prefix for custom-domain URLs (
""when default).
- property include_account_hash: bool
Whether the account hash appears in the path of built URLs.
- base_url(account_hash: str | None = None) str[source]
Return the delivery URL prefix up to (but excluding) the image id.
- Parameters:
account_hash – Override for the account hash. Defaults to
cloudflare_settings.account_hashwhen the configured shape includes the hash.
- build_url(image_id: str, variant: str = 'public', account_hash: str | None = None) str[source]
Build a full delivery URL honoring the configured shape.
- Parameters:
image_id – The Cloudflare image id (may contain
/for custom paths).variant – The variant name or flexible-variant options. Pass an empty value to omit the trailing segment entirely. Note that a multi-segment
image_idcombined with an emptyvariantdoes not round-trip throughextract_image_id(), which treats the last segment as the variant; pair custom-path ids with a variant.account_hash – Optional account-hash override.
- Returns:
The fully-qualified delivery URL.
- is_delivery_url(url: str) bool[source]
Return
Trueifurlis a Cloudflare Images delivery URL.Recognizes the shared
imagedelivery.nethost and the configured custom domain (whenDELIVERY_URLis set). The host must match exactly — a URL that merely containsimagedelivery.netelsewhere in the path does not qualify — and for a custom domain with a path prefix the URL must use that prefix.
- extract_image_id(url: str) str | None[source]
Extract the image id from a delivery URL, or
None.Handles the shared host, the native custom-domain prefix, and the Worker shape, accounting for whether the account hash is present. Multi-segment (custom-path) image ids are preserved.
Note
The final path segment is assumed to be the variant. A custom-path image id used without a variant (e.g. a URL built via
build_url("folder/sub/abc", variant="")) cannot be distinguished from “id + variant” and will lose its last segment here. Pair custom-path ids with a variant for reliable round-tripping.
- split_variant(url: str) tuple[str, str | None][source]
Split a delivery URL into
(base_without_last_segment, last_segment).Useful for swapping a named variant for flexible-variant options. The query string and fragment are dropped from the returned base.
- with_options(url: str, options: str) str[source]
Return a flexible-variant URL applying
optionstourl.When the delivery URL carries a variant segment, that segment is replaced by
options. When it has no variant (e.g. a no-variant custom URL), the options are appended so the image id is preserved rather than overwritten. Query string and fragment are preserved.
- rewrite_url(url: str) str[source]
Rewrite a Cloudflare
imagedelivery.netURL into the configured shape.Cloudflare always returns variant URLs on the shared
imagedelivery.nethost; this rewrites them to the configured custom domain. The query string and fragment (e.g. signed-URL parameters) are preserved.Returns the URL unchanged when no
DELIVERY_URLis configured or whenurlis not a shared-host delivery URL.
Single source of truth for building, recognizing, extracting from, and rewriting
Cloudflare Images delivery URLs. The image_url_factory singleton is exported
from the package root. See Image URL Factory for a usage guide and the
DELIVERY_URL configuration.
Exception Classes
CloudflareImagesAPIError
- exception django_cloudflareimages_toolkit.exceptions.CloudflareImagesAPIError(message: str, status_code: int | None = None, response_data: dict | None = None)[source]
Exception raised when the Cloudflare Images API request fails.
This exception is raised when: - API returns an error status code - Network request fails - Invalid API response format - Authentication failures
Raised when Cloudflare Images API requests fail.
Common Causes:
Network connectivity issues
Invalid API credentials
Rate limiting
Server errors
Invalid image data
Example:
try:
service = CloudflareImagesService()
upload_data = service.get_direct_upload_url()
except CloudflareImagesAPIError as e:
print(f"API error: {e}")
ConfigurationError
- exception django_cloudflareimages_toolkit.exceptions.ConfigurationError(message: str, status_code: int | None = None, response_data: dict | None = None)[source]
Exception raised when configuration is missing or invalid.
This exception is raised when: - Required settings are missing (CLOUDFLARE_ACCOUNT_ID, CLOUDFLARE_API_TOKEN) - Invalid configuration values - Missing environment variables
Raised when configuration is missing or invalid.
Common Causes:
Missing required settings
Invalid account credentials
Malformed configuration data
Example:
try:
service = CloudflareImagesService()
except ConfigurationError as e:
print(f"Configuration error: {e}")
ValidationError
- exception django_cloudflareimages_toolkit.exceptions.ValidationError(message: str, status_code: int | None = None, response_data: dict | None = None)[source]
Exception raised when validation fails.
This exception is raised when: - Invalid file types or sizes - Invalid metadata format - Field validation errors - Image processing validation failures
Raised when image data validation fails.
Common Causes:
Invalid image format
File size exceeds limits
Missing required metadata
Example:
try:
field = CloudflareImageField(allowed_formats=['jpeg'])
# Validation occurs during model save
except ValidationError as e:
print(f"Validation error: {e}")
ImageNotFoundError
- exception django_cloudflareimages_toolkit.exceptions.ImageNotFoundError(message: str, status_code: int | None = None, response_data: dict | None = None)[source]
Exception raised when an image is not found.
This exception is raised when: - Image ID doesn’t exist in Cloudflare - Image has been deleted - Access denied to image
Raised when an image does not exist in Cloudflare. This is raised by
get_image() on a Cloudflare 404 and by
CloudflareImage.objects.register_uploaded() when the supplied
cloudflare_id does not exist. Importable from both
django_cloudflareimages_toolkit and
django_cloudflareimages_toolkit.exceptions.
Example:
from django_cloudflareimages_toolkit import ImageNotFoundError
try:
service.get_image('missing-id')
except ImageNotFoundError as e:
print(f"Image not found: {e}")
ImageNotReadyError
- exception django_cloudflareimages_toolkit.exceptions.ImageNotReadyError(message: str, status_code: int | None = None, response_data: dict | None = None)[source]
Exception raised when an image exists but is not ready to be registered.
This is raised when the image is still a draft – the upload URL was created but no bytes have been uploaded yet, so Cloudflare still reports
draftasTrue. It signals that thecloudflare_idis real but the upload has not completed, so it must not be trusted as finished.
Subclass of CloudflareImagesError. Raised by
CloudflareImage.objects.register_uploaded() when the image exists in
Cloudflare but is still a draft (the upload has not completed). No local row
is created in this case. Importable from both
django_cloudflareimages_toolkit and
django_cloudflareimages_toolkit.exceptions.
Example:
from django_cloudflareimages_toolkit import ImageNotReadyError
try:
CloudflareImage.objects.register_uploaded(cloudflare_id)
except ImageNotReadyError as e:
print(f"Upload not completed yet: {e}")
ImageOwnershipError
- exception django_cloudflareimages_toolkit.exceptions.ImageOwnershipError(message: str, status_code: int | None = None, response_data: dict | None = None)[source]
Exception raised when a registered image’s creator does not match.
This is raised by
register_uploadedwhen anexpected_creatoris supplied and the Cloudflarecreatoron the image does not equal it. It guards against a caller registering another user’s completed image by submitting an arbitrarycloudflare_idfrom the same account.
Subclass of CloudflareImagesError. Raised by
CloudflareImage.objects.register_uploaded() when an expected_creator is
supplied and the Cloudflare creator on the image does not match it. The
check runs before any local row is created, so it prevents a caller from
registering another user’s completed image. Importable from both
django_cloudflareimages_toolkit and
django_cloudflareimages_toolkit.exceptions.
Example:
from django_cloudflareimages_toolkit import ImageOwnershipError
try:
CloudflareImage.objects.register_uploaded(
cloudflare_id, user=request.user, expected_creator=str(request.user.pk)
)
except ImageOwnershipError as e:
print(f"Not your image: {e}")
Utility APIs
There is no separate utils module. The utility-style operations
live as methods on the service singleton and the CloudflareImage
model, plus the standalone CloudflareImageTransform class for URL
construction.
Image URL Generation
URL generation is a method on the model, not a freestanding function.
Use get_url()
for the default variant or
get_variant_url()
to select one.
image = CloudflareImage.objects.get(cloudflare_id='abc123')
url = image.get_variant_url('thumbnail')
For transformation URLs (resize, crop, format, watermark) without a
CloudflareImage instance, use the standalone builder
CloudflareImageTransform:
from django_cloudflareimages_toolkit.transformations import (
CloudflareImageTransform,
)
# Pass an existing delivery URL to the constructor, chain the
# transforms, then call .build().
url = (
CloudflareImageTransform("https://imagedelivery.net/your-hash/abc123/public")
.width(400)
.height(300)
.format("auto")
.quality(85)
.build()
)
See Patterns & Recipes for a watermarking recipe based on Cloudflare dashboard
variants. CloudflareImageTransform builds resize/format URLs; it does not
expose Cloudflare’s draw overlay parameter.
Signed URL Generation
Signed URLs are produced by
get_signed_url()
when require_signed_urls=True on the model row:
image = CloudflareImage.objects.get(cloudflare_id='abc123')
signed_url = image.get_signed_url(variant="public", expiry=3600)
Returns None if the image isn’t uploaded yet or signed URLs are
disabled for that row.
Configuration Helpers
Read settings via the cloudflare_settings proxy instead of
indexing settings.CLOUDFLARE_IMAGES directly. The proxy validates
required keys (ACCOUNT_ID, API_TOKEN) on access and exposes
documented defaults for everything else.
from django_cloudflareimages_toolkit.settings import cloudflare_settings
print(cloudflare_settings.account_id)
print(cloudflare_settings.default_expiry_minutes)
secret = cloudflare_settings.webhook_secret # may be None
Webhook Signature Verification
Signature verification is wired into WebhookView and runs
automatically when a secret is configured. If you need to verify a
signature outside the bundled view (e.g. inside a Celery task or a
custom endpoint), call the service directly:
from django_cloudflareimages_toolkit.services import cloudflare_service
ok = cloudflare_service.validate_webhook_signature(
request.body, signature_header
)
Returns True only when the HMAC matches. Callers that need strict
enforcement should check the secret first or use WebhookView,
which encodes the right policy.
Constants and Settings
Default Settings:
# CLOUDFLARE_IMAGES keys and their defaults. Set these under
# settings.CLOUDFLARE_IMAGES; ACCOUNT_ID, ACCOUNT_HASH, and API_TOKEN are required.
CLOUDFLARE_IMAGES_DEFAULTS = {
'BASE_URL': 'https://api.cloudflare.com/client/v4',
'DEFAULT_EXPIRY_MINUTES': 30, # 2-360
'REQUIRE_SIGNED_URLS': True,
'DEFAULT_METADATA': {},
'DEFAULT_CREATOR': None,
'METADATA_FACTORY': None,
'WEBHOOK_SECRET': None,
'MAX_FILE_SIZE_MB': 10,
'DELIVERY_URL': None, # e.g. 'images.example.com'
'DELIVERY_PATH_PREFIX': 'cdn-cgi/imagedelivery',
'DELIVERY_INCLUDE_ACCOUNT_HASH': True,
}
API Endpoints:
CLOUDFLARE_IMAGES_API_BASE = 'https://api.cloudflare.com/client/v4'
CLOUDFLARE_IMAGES_DELIVERY_BASE = 'https://imagedelivery.net'
Migration Support
The package includes Django migrations for database schema management:
Initial Migration (0001_initial.py):
Creates CloudflareImage model table
Sets up indexes for performance
Creates constraints for data integrity
Migration Commands:
# Apply migrations
python manage.py migrate django_cloudflareimages_toolkit
# Create new migration (if you modify models)
python manage.py makemigrations django_cloudflareimages_toolkit
Testing Utilities
Mock Service
For testing purposes, you can mock the CloudflareImagesService:
from unittest.mock import patch, MagicMock
from django.test import TestCase
class MyTestCase(TestCase):
@patch('django_cloudflareimages_toolkit.services.CloudflareImagesService')
def test_image_upload(self, mock_service):
mock_instance = MagicMock()
mock_service.return_value = mock_instance
mock_instance.get_direct_upload_url.return_value = {
'id': 'test-id',
'uploadURL': 'https://test-upload-url.com'
}
# Your test code here
Test Image Factory
Create test images for testing:
from django_cloudflareimages_toolkit.models import CloudflareImage
def create_test_image(**kwargs):
defaults = {
'cloudflare_id': 'test-image-id',
'filename': 'test.jpg',
'is_ready': True,
'file_size': 1024,
'width': 800,
'height': 600,
'format': 'jpeg'
}
defaults.update(kwargs)
return CloudflareImage.objects.create(**defaults)
Version Information
- django_cloudflareimages_toolkit.__version__ = '1.1.4'
str(object=’’) -> str str(bytes_or_buffer[, encoding[, errors]]) -> str
Create a new string object from the given object. If encoding or errors is specified, then the object must expose a data buffer that will be decoded using the given encoding and error handler. Otherwise, returns the result of object.__str__() (if defined) or repr(object). encoding defaults to sys.getdefaultencoding(). errors defaults to ‘strict’.
Current package version string.
Example:
import django_cloudflareimages_toolkit
print(f"Package version: {django_cloudflareimages_toolkit.__version__}")
Logging
The package uses Python’s standard logging module with the logger name django_cloudflareimages_toolkit.
Log Levels:
DEBUG: Detailed API request/response informationINFO: Successful operations and image processing updatesWARNING: Recoverable errors and fallback usageERROR: Failed operations and API errorsCRITICAL: System-level failures
Example Configuration:
import logging
# Configure logging for the package
logging.getLogger('django_cloudflareimages_toolkit').setLevel(logging.INFO)
# Example log output
logger = logging.getLogger('django_cloudflareimages_toolkit')
logger.info("Image uploaded successfully: %s", image_id)
Type Hints
The package includes comprehensive type hints for better IDE support and type checking:
from typing import Dict, List, Optional, Union
from django_cloudflareimages_toolkit.services import CloudflareImagesService
service: CloudflareImagesService = CloudflareImagesService()
upload_data: Dict[str, str] = service.get_direct_upload_url()
images: Dict[str, Union[List, Dict]] = service.list_images()