{"id":13415690,"url":"https://github.com/codingjoe/django-pictures","last_synced_at":"2025-05-15T12:02:39.748Z","repository":{"id":37863313,"uuid":"455480246","full_name":"codingjoe/django-pictures","owner":"codingjoe","description":"Responsive cross-browser image library using modern codes like AVIF \u0026 WebP","archived":false,"fork":false,"pushed_at":"2025-05-04T13:06:25.000Z","size":141,"stargazers_count":259,"open_issues_count":2,"forks_count":23,"subscribers_count":5,"default_branch":"main","last_synced_at":"2025-05-15T12:02:38.527Z","etag":null,"topics":["avif","cdn","django","grid","hacktoberfest","image-processing","picture","pillow","responsive","webp"],"latest_commit_sha":null,"homepage":"","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"bsd-2-clause","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/codingjoe.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":".github/FUNDING.yml","license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":"SECURITY.md","support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null},"funding":{"github":"codingjoe","buy_me_a_coffee":"codingjoe","custom":"https://www.paypal.me/codingjoe"}},"created_at":"2022-02-04T08:58:09.000Z","updated_at":"2025-05-04T13:05:51.000Z","dependencies_parsed_at":"2023-12-18T09:29:26.972Z","dependency_job_id":"a8c590d8-9085-4c26-92cd-3d40c1ac60f1","html_url":"https://github.com/codingjoe/django-pictures","commit_stats":{"total_commits":142,"total_committers":14,"mean_commits":"10.142857142857142","dds":0.4859154929577465,"last_synced_commit":"577412182d0cb36c93d6ffe8f0feeb9275ef49e5"},"previous_names":[],"tags_count":30,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/codingjoe%2Fdjango-pictures","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/codingjoe%2Fdjango-pictures/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/codingjoe%2Fdjango-pictures/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/codingjoe%2Fdjango-pictures/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/codingjoe","download_url":"https://codeload.github.com/codingjoe/django-pictures/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":254337612,"owners_count":22054253,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["avif","cdn","django","grid","hacktoberfest","image-processing","picture","pillow","responsive","webp"],"created_at":"2024-07-30T21:00:51.361Z","updated_at":"2025-05-15T12:02:39.724Z","avatar_url":"https://github.com/codingjoe.png","language":"Python","readme":"![Django Pictures Logo](https://repository-images.githubusercontent.com/455480246/daaa7870-d28c-4fce-8296-d3e3af487a64)\n\n# Django Pictures\n\nResponsive cross-browser image library using modern codes like AVIF \u0026 WebP.\n\n- responsive web images using the [picture](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/picture) tag\n- native grid system support\n- serve files with or without a CDN\n- placeholders for local development\n- migration support\n- async image processing for [Celery], [Dramatiq] or [Django RQ][django-rq]\n- [DRF] support\n\n[![PyPi Version](https://img.shields.io/pypi/v/django-pictures.svg)](https://pypi.python.org/pypi/django-pictures/)\n[![Test Coverage](https://codecov.io/gh/codingjoe/django-pictures/branch/main/graph/badge.svg)](https://codecov.io/gh/codingjoe/django-pictures)\n[![GitHub License](https://img.shields.io/github/license/codingjoe/django-pictures)](https://raw.githubusercontent.com/codingjoe/django-pictures/master/LICENSE)\n\n## Usage\n\nBefore you start, it can be a good idea to understand the fundamentals of\n[responsive images](https://developer.mozilla.org/en-US/docs/Learn/HTML/Multimedia_and_embedding/Responsive_images).\n\nOnce you get a feeling how complicated things can get with all device types,\nyou'll probably find a new appreciation for this package,\nand are ready to adopt in your project :)\n\n```python\n# models.py\nfrom django.db import models\nfrom pictures.models import PictureField\n\nclass Profile(models.Model):\n    title = models.CharField(max_length=255)\n    picture = PictureField(upload_to=\"avatars\")\n```\n\n```html\n\u003c!-- template.html --\u003e\n{% load pictures %}\n{% picture profile.picture img_alt=\"Spiderman\" img_loading=\"lazy\" picture_class=\"my-picture\" m=6 l=4 %}\n```\n\nThe keyword arguments `m=6 l=4` define the columns the image should take up in\na grid at a given breakpoint. So in this example, the image will take up\nsix columns on medium screens and four columns on large screens. You can define\nyour grid and breakpoints as you want, refer to the [grid columns](#grid-columns) and\n[breakpoints](#breakpoints) sections.\n\nThe template above will render into:\n\n```html\n\u003cpicture class=\"my-picture\"\u003e\n  \u003csource type=\"image/webp\"\n          srcset=\"/media/testapp/profile/image/800w.webp 800w, /media/testapp/profile/image/100w.webp 100w, /media/testapp/profile/image/200w.webp 200w, /media/testapp/profile/image/300w.webp 300w, /media/testapp/profile/image/400w.webp 400w, /media/testapp/profile/image/500w.webp 500w, /media/testapp/profile/image/600w.webp 600w, /media/testapp/profile/image/700w.webp 700w\"\n          sizes=\"(min-width: 0px) and (max-width: 991px) 100vw, (min-width: 992px) and (max-width: 1199px) 33vw, 600px\"\u003e\n  \u003cimg src=\"/media/testapp/profile/image.jpg\" alt=\"Spiderman\" width=\"800\" height=\"800\" loading=\"lazy\"\u003e\n\u003c/picture\u003e\n```\n\nNote that arbitrary attributes can be passed\nto either the `\u003cpicture\u003e` or `\u003cimg\u003e` element\nby prefixing parameters to the `{% picture %}` tag\nwith `picture_` or `img_` respectively.\n\n## Setup\n\n### Installation\n\n```shell\npython3 -m pip install django-pictures\n```\n\n### Settings\n\n```python\n# settings.py\nINSTALLED_APPS = [\n    # ...\n    'pictures',\n]\n\n# the following are defaults, but you can override them\nPICTURES = {\n    \"BREAKPOINTS\": {\n        \"xs\": 576,\n        \"s\": 768,\n        \"m\": 992,\n        \"l\": 1200,\n        \"xl\": 1400,\n    },\n    \"GRID_COLUMNS\": 12,\n    \"CONTAINER_WIDTH\": 1200,\n    \"FILE_TYPES\": [\"WEBP\"],\n    \"PIXEL_DENSITIES\": [1, 2],\n    \"USE_PLACEHOLDERS\": True,\n    \"QUEUE_NAME\": \"pictures\",\n    \"PROCESSOR\": \"pictures.tasks.process_picture\",\n\n}\n```\n\nIf you have either Dramatiq or Celery installed, we will default to async\nimage processing. You will need workers to listen to the `pictures` queue.\n\n### Placeholders\n\nThis library comes with dynamically created placeholders to simplify local\ndevelopment. To enable them, add the following to enable the\n`PICTURES[\"USE_PLACEHOLDERS\"]` setting and add the following URL configuration:\n\n```python\n# urls.py\nfrom django.urls import include, path\nfrom pictures.conf import get_settings\n\nurlpatterns = [\n    # ...\n]\n\nif get_settings().USE_PLACEHOLDERS:\n    urlpatterns += [\n        path(\"_pictures/\", include(\"pictures.urls\")),\n    ]\n```\n\n### Legacy use-cases (email)\n\nAlthough the `picture`-tag is [adequate for most use-cases][caniuse-picture],\nsome remain, where a single `img` tag is necessary. Notably in email, where\n[most clients do support WebP][caniemail-webp] but not [srcset][caniemail-srcset].\nThe template tag `img_url` returns a single size image URL.\nIn addition to the ratio, you will need to define the `file_type`\nas well as the `width` (absolute width in pixels).\n\n```html\n{% load pictures %}\n\u003cimg src=\"{% img_url profile.picture ratio='3/2' file_type='webp' width=800 %}\" alt=\"profile picture\"\u003e\n```\n\n## Config\n\n### Aspect ratios\n\nYou can specify the aspect ratios of your images. Images will be cropped to the\nspecified aspect ratio. Aspect ratios are specified as a string with a slash\nbetween the width and height. For example, `16/9` will crop the image to 16:9.\n\n```python\n# models.py\nfrom django.db import models\nfrom pictures.models import PictureField\n\n\nclass Profile(models.Model):\n    title = models.CharField(max_length=255)\n    picture = PictureField(\n      upload_to=\"avatars\",\n      aspect_ratios=[None, \"1/1\", \"3/2\", \"16/9\"],\n    )\n```\n\n```html\n# template.html\n{% load pictures %}\n{% picture profile.picture img_alt=\"Spiderman\" ratio=\"16/9\" m=6 l=4 %}\n```\n\nIf you don't specify an aspect ratio or None in your template, the image will be\nserved with the original aspect ratio of the file.\n\nYou may only use aspect ratios in templates that have been defined on the model.\nThe model `aspect_ratios` will default to `[None]`, if other value is provided.\n\n### Breakpoints\n\nYou may define your own breakpoints they should be identical to the ones used\nin your CSS library. This can be achieved by overriding the `PICTURES[\"BREAKPOINTS\"]` setting.\n\n### Grid columns\n\nGrids are so common in web design that they even made it into CSS.\nWe default to 12 columns, but you can override this setting, via the\n`PICTURES[\"GRID_COLUMNS\"]` setting.\n\n### Container width\n\nContainers are commonly used to limit the maximum width of layouts,\nto promote better readability on larger screens. We default to `1200px`,\nbut you can override this setting, via the `PICTURES[\"CONTAINER_WIDTH\"]` setting.\n\nYou may also set it to `None`, should you not use a container.\n\n### File types\n\n[AVIF](https://caniuse.com/avif) ([WebP](https://caniuse.com/webp)'s successor)\nis the best and most efficient image format available today. It is part of\nBaseline 2024 and is supported by all major browsers. Additionally, most modern\ndevices will have hardware acceleration for AVIF decoding. This will not only\nreduce network IO but speed up page rendering.\n\n\u003e [!NOTE]\n\u003e Pillow 11.2.1 shipped without AVIF binaries.\n\u003e You will need to [install Pillow from source][libavif-install] for AVIF support.\n\u003e This should be resolved in upcoming releases, once\n\u003e [#8858](https://github.com/python-pillow/Pillow/pull/8858) has been merged.\n\nShould you still serve IE11, use add `JPEG` to the list. But, beware, this may\ndrastically increase your storage needs.\n\n### Pixel densities\n\nUnless you really care that your images hold of if you hold your UHD phone very\nclose to your eyeballs, you should be fine, serving at the default `1x` and `2x`\ndensities.\n\n### Async image processing\n\nIf you have either Dramatiq or Celery installed, we will default to async\nimage processing. You will need workers to listen to the `pictures` queue.\nYou can override the queue name, via the `PICTURES[\"QUEUE_NAME\"]` setting.\n\nYou can also override the processor, via the `PICTURES[\"PROCESSOR\"]` setting.\nThe default processor is `pictures.tasks.process_picture`. It takes a single\nargument, the `PictureFileFile` instance. You can use this to override the\nprocessor, should you need to do some custom processing.\n\n## Migrations\n\nDjango doesn't support file field migrations, but we do.\nYou can auto create the migration and replace Django's\n`AlterField` operation with `AlterPictureField`. That's it.\n\nYou can follow [the example][migration] in our test app, to see how it works.\n\n## Contrib\n\n### Django Rest Framework ([DRF])\n\nWe do ship with a read-only `PictureField` that can be used to include all\navailable picture sizes in a DRF serializer.\n\n```python\nfrom rest_framework import serializers\nfrom pictures.contrib.rest_framework import PictureField\n\nclass PictureSerializer(serializers.Serializer):\n    picture = PictureField()\n```\n\nThe response can be restricted to a fewer aspect ratios and file types, by\nproviding the `aspect_ratios` and `file_types` arguments to the DRF field.\n\n```python\nfrom rest_framework import serializers\nfrom pictures.contrib.rest_framework import PictureField\n\nclass PictureSerializer(serializers.Serializer):\n    picture = PictureField(aspect_ratios=[\"16/9\"], file_types=[\"WEBP\"])\n```\n\nYou also may provide optional GET parameters to the serializer\nto specify the aspect ratio and breakpoints you want to include in the response.\nThe parameters are prefixed with the `fieldname_`\nto avoid conflicts with other fields.\n\n```bash\ncurl http://localhost:8000/api/path/?picture_ratio=16%2F9\u0026picture_m=6\u0026picture_l=4\n# %2F is the url encoded slash\n```\n\n```json\n{\n  \"other_fields\": \"…\",\n  \"picture\": {\n    \"url\": \"/path/to/image.jpg\",\n    \"width\": 800,\n    \"height\": 800,\n    \"ratios\": {\n      \"1/1\": {\n        \"sources\": {\n          \"image/webp\": {\n            \"100\": \"/path/to/image/1/100w.webp\",\n            \"200\": \"…\"\n          }\n        },\n        \"media\": \"(min-width: 0px) and (max-width: 991px) 100vw, (min-width: 992px) and (max-width: 1199px) 33vw, 25vw\"\n      }\n    }\n  }\n}\n```\n\nNote that the `media` keys are only included, if you have specified breakpoints.\n\n### Django Cleanup\n\n`PictureField` is compatible with [Django Cleanup](https://github.com/un1t/django-cleanup),\nwhich automatically deletes its file and corresponding `SimplePicture` files.\n\n### external image processing (via CDNs)\n\nThis package is designed to accommodate growth, allowing you to start small and scale up as needed.\nShould you use a CDN, or some other external image processing service, you can\nset this up in two simple steps:\n\n1. Override `PICTURES[\"PROCESSOR\"]` to disable the default processing.\n1. Override `PICTURES[\"PICTURE_CLASS\"]` implement any custom behavior.\n\n```python\n# settings.py\nPICTURES = {\n    \"PROCESSOR\": \"pictures.tasks.noop\",  # disable default processing and do nothing\n    \"PICTURE_CLASS\": \"path.to.MyPicture\",  # override the default picture class\n}\n```\n\nThe `MyPicture`class should implement the url property, which returns the URL\nof the image. You may use the `Picture` class as a base class.\n\nAvailable attributes are:\n\n- `parent_name` - name of the source file uploaded to the `PictureField`\n- `aspect_ratio` - aspect ratio of the output image\n- `width` - width of the output image\n- `file_type` - format of the output image\n\n```python\n# path/to.py\nfrom pathlib import Path\nfrom pictures.models import Picture\n\n\nclass MyPicture(Picture):\n    @property\n    def url(self):\n        return (\n            f\"https://cdn.example.com/{Path(self.parent_name).stem}\"\n            f\"_{self.aspect_ratio}_{self.width}w.{self.file_type.lower()}\"\n        )\n```\n\n[caniemail-srcset]: https://www.caniemail.com/features/html-srcset/\n[caniemail-webp]: https://www.caniemail.com/features/image-webp/\n[caniuse-picture]: https://caniuse.com/picture\n[celery]: https://docs.celeryproject.org/en/stable/\n[django-rq]: https://github.com/rq/django-rq\n[dramatiq]: https://dramatiq.io/\n[drf]: https://www.django-rest-framework.org/\n[libavif-install]: https://pillow.readthedocs.io/en/latest/installation/building-from-source.html#external-libraries\n[migration]: tests/testapp/migrations/0002_alter_profile_picture.py\n","funding_links":["https://github.com/sponsors/codingjoe","https://buymeacoffee.com/codingjoe","https://www.paypal.me/codingjoe"],"categories":["Third-Party Packages","Image handling","Python"],"sub_categories":["Files/Images"],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcodingjoe%2Fdjango-pictures","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fcodingjoe%2Fdjango-pictures","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcodingjoe%2Fdjango-pictures/lists"}