cloudflare-images
Wrapper around Cloudflare Images API, with instructions to create a usable custom Django storage class such wrapper.
Development
See documentation.
- Run
just start - Run
just dumpenv - Run
pytest
Note: pytest will work only if no .env file exists with the included values. See docstrings.
Changes
Dec. 2, 2023
- Compatibility: python 3.12
- Compatibility: pydantic 2.5
Initial
- Removed: Django as a dependency
- Added: Instructions to create Django custom storage class
- Added:
.enable_batch() - Added:
.list_images() - Added:
.get_batch_token() - Added:
.get_usage_statistics() - Added:
.update_image() - Added:
.v2 - Renamed:
.base_apitov1 - Renamed:
.get()to.get_image_details() - Renamed:
.post()to.upload_image() - Renamed:
.delete()to.delete_image() - Renamed:
.upsert()to.delete_then_upload_image() - Renamed:
CloudflareImagesAPIv1toCloudflareImagesAPI
Django Instructions
Starting with Django 4.2, add a Custom Storage class to the STORAGES setting like so:
STORAGES = { # django 4.2 and above
"default": { # default
"BACKEND": "django.core.files.storage.FileSystemStorage",
},
"staticfiles": { # default
"BACKEND": "django.contrib.staticfiles.storage.StaticFilesStorage",
},
"cloudflare_images": { # add location of custom storage class
"BACKEND": "path.to.storageclass",
},
}
The path to the custom storage class should resemble the following:
from http import HTTPStatus
import httpx
from django.core.files.base import File
from django.core.files.storage import Storage
from django.utils.deconstruct import deconstructible
from cloudflare_images import CloudflareImagesAPI
@deconstructible
class LimitedStorageCloudflareImages(Storage):
def __init__(self):
super().__init__()
self.api = CloudflareImagesAPI()
def __repr__(self):
return "<LimitedToImagesStorageClassCloudflare>"
def _open(self, name: str, mode="rb") -> File:
return File(self.api.get(img_id=name), name=name)
def _save(self, name: str, content: bytes) -> str:
res = self.api.delete_then_upload_image(name, content)
return self.api.url(img_id=res.json()["result"]["id"])
def get_valid_name(self, name):
return name
def get_available_name(self, name, max_length=None):
return self.generate_filename(name)
def generate_filename(self, filename):
return filename
def delete(self, name) -> httpx.Response:
return self.api.delete(name)
def exists(self, name: str) -> bool:
res = self.api.get(name)
if res.status_code == HTTPStatus.NOT_FOUND:
return False
elif res.status_code == HTTPStatus.OK:
return True
raise Exception("Image name found but http status code is not OK.")
def listdir(self, path):
raise NotImplementedError(
"subclasses of Storage must provide a listdir() method"
)
def size(self, name: str):
return len(self.api.get(name).content)
def url(self, name: str):
return self.api.url(name)
def url_variant(self, name: str, variant: str):
return self.api.url(name, variant)
def get_accessed_time(self, name):
raise NotImplementedError(
"subclasses of Storage must provide a get_accessed_time() method"
)
def get_created_time(self, name):
raise NotImplementedError(
"subclasses of Storage must provide a get_created_time() method"
)
def get_modified_time(self, name):
raise NotImplementedError(
"subclasses of Storage must provide a get_modified_time() method"
)
Can then define a callable likeso:
from django.core.files.storage import storages
def select_storage(is_remote_env: bool):
return storages["cloudflare_images"] if is_remote_env else storages["default"]
class MyModel(models.Model):
my_img = models.ImageField(storage=select_storage)
Can also refer to it via:
from django.core.files.storage import storages
cf = storages["cloudflare_images"]
# assume previous upload done
id = <image-id-uploaded>
# get image url, defaults to 'public' variant
cf.url(id)
# specified 'avatar' variant, assuming it was created in the Cloudflare Images dashboard / API
cf.url_variant(id, 'avatar')
Metadata
Release files for cloudflare-images 0.1.8
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | Actions |
|---|---|---|---|---|
| cloudflare_images-0.1.8.tar.gz | 9.3 kB | Without provenance | Details for cloudflare_images-0.1.8.tar.gz |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Actions Reset |
|---|---|---|---|---|
| cloudflare_images-0.1.8-py3-none-any.whl | Python 3 | none | any | Details for cloudflare_images-0.1.8-py3-none-any.whl |
Total release size: 17.4 kB
Release files / cloudflare_images-0.1.8.tar.gz
| Download URL | cloudflare_images-0.1.8.tar.gz |
|---|---|
| Size | 9.3 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
95426031ed818c052ccae47e77b614740e8b38a12fb71aa7b3f3cbb00550d143
|
|
BLAKE2b-256 checksum How to use checksums |
e52ac03b66eeef353cba305d3d1e8118137b95a48519769baa0522826be7be87
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/5.1.1 CPython/3.12.3
|
Release files / cloudflare_images-0.1.8-py3-none-any.whl
| Download URL | cloudflare_images-0.1.8-py3-none-any.whl |
|---|---|
| Size | 8.0 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
6a34d2d907fd19692d4db5396d56384b0d96ad0bdc834fa2d4e044e6347e5449
|
|
BLAKE2b-256 checksum How to use checksums |
d25b2e874f56c557d9d1ad34de1c910e4555d83f83fd0d1c2dcb4ad6c7298420
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/5.1.1 CPython/3.12.3
|
Security for cloudflare-images 0.1.8
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
No provenance found
None of the files in this release were uploaded with provenance metadata.
A note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.