Package: cloudflare-images
cloudflare-imagesWrapper around Cloudflare Images API, with instructions to create a usable custom Django storage class such wrapper.DevelopmentSeedocumentation.Runpoetry installRunpoetry shellRunpytestNote:pytestwill work only if no.envfile exists with the included values. See docstrings.ChangesRemoved:Djangoas a dependencyAdded: Instructions to createDjangocustom storage classAdded:.enable_batch()Added:.list_images()Added:.get_batch_token()Added:.get_usage_statistics()Added:.update_image()Added:.v2Renamed:.base_apitov1Renamed:.get()to.get_image_details()Renamed:.post()to.upload_image()Renamed:.delete()to.delete_image()Renamed:.upsert()to.delete_then_upload_image()Renamed:CloudflareImagesAPIv1toCloudflareImagesAPIDjango InstructionsStarting withDjango4.2, add a CustomStorageclass to theSTORAGESsetting 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 shouldresemblethe following:fromhttpimportHTTPStatusimporthttpxfromdjango.core.files.baseimportFilefromdjango.core.files.storageimportStoragefromdjango.utils.deconstructimportdeconstructiblefromcloudflare_imagesimportCloudflareImagesAPI@deconstructibleclassLimitedStorageCloudflareImages(Storage):def__init__(self):super().__init__()self.api=CloudflareImagesAPI()def__repr__(self):return"<LimitedToImagesStorageClassCloudflare>"def_open(self,name:str,mode="rb")->File:returnFile(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)returnself.api.url(img_id=res.json()["result"]["id"])defget_valid_name(self,name):returnnamedefget_available_name(self,name,max_length=None):returnself.generate_filename(name)defgenerate_filename(self,filename):returnfilenamedefdelete(self,name)->httpx.Response:returnself.api.delete(name)defexists(self,name:str)->bool:res=self.api.get(name)ifres.status_code==HTTPStatus.NOT_FOUND:returnFalseelifres.status_code==HTTPStatus.OK:returnTrueraiseException("Image name found but http status code is not OK.")deflistdir(self,path):raiseNotImplementedError("subclasses of Storage must provide a listdir() method")defsize(self,name:str):returnlen(self.api.get(name).content)defurl(self,name:str):returnself.api.url(name)defurl_variant(self,name:str,variant:str):returnself.api.url(name,variant)defget_accessed_time(self,name):raiseNotImplementedError("subclasses of Storage must provide a get_accessed_time() method")defget_created_time(self,name):raiseNotImplementedError("subclasses of Storage must provide a get_created_time() method")defget_modified_time(self,name):raiseNotImplementedError("subclasses of Storage must provide a get_modified_time() method")Can then define acallablelikeso:fromdjango.core.files.storageimportstoragesdefselect_storage(is_remote_env:bool):returnstorages["cloudflare_images"]ifis_remote_envelsestorages["default"]classMyModel(models.Model):my_img=models.ImageField(storage=select_storage)Can also refer to it via:fromdjango.core.files.storageimportstoragescf=storages["cloudflare_images"]# assume previous upload doneid=<image-id-uploaded># get image url, defaults to 'public' variantcf.url(id)# specified 'avatar' variant, assuming it was created in the Cloudflare Images dashboard / APIcf.url_variant(id,'avatar')