From 6f636a9f0bae5cc377dcd8b0b58220500a2a19f2 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 8 Apr 2022 21:06:47 +0000 Subject: [PATCH 01/11] Bump actions/upload-artifact from 2 to 3 Bumps [actions/upload-artifact](https://github.com/actions/upload-artifact) from 2 to 3. - [Release notes](https://github.com/actions/upload-artifact/releases) - [Commits](https://github.com/actions/upload-artifact/compare/v2...v3) --- updated-dependencies: - dependency-name: actions/upload-artifact dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index c6569c7..3cdbc39 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -57,7 +57,7 @@ jobs: - uses: actions/checkout@v3 - run: python setup.py sdist bdist_wheel - run: python -m twine check dist/* - - uses: actions/upload-artifact@v2 + - uses: actions/upload-artifact@v3 with: path: dist/* From c2aa355b7a0af30a0715062e12adb204a99119e1 Mon Sep 17 00:00:00 2001 From: J Harley <502818+julzhk@users.noreply.github.com> Date: Tue, 26 Apr 2022 15:51:21 -0300 Subject: [PATCH 02/11] minor type in readme Otherwise: spelling --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index cc70313..2144030 100644 --- a/README.md +++ b/README.md @@ -191,5 +191,5 @@ python manage.py rendervariations 'app_name.model_name.field_name' [--replace] [ ``` The `replace` option will replace all existing files. The `ignore-missing` option will suspend missing source file errors and keep -rendering variations for other files. Othervise command will stop on first +rendering variations for other files. Otherwise command will stop on first missing file. From 39e2306f29863ab6cb22cb264d606afc8127cc88 Mon Sep 17 00:00:00 2001 From: J Harley <502818+julzhk@users.noreply.github.com> Date: Wed, 27 Apr 2022 10:58:09 -0300 Subject: [PATCH 03/11] Update Readme.md More extensive edit for clarity and readability --- README.md | 86 ++++++++++++++++++++++++++++++++++++------------------- 1 file changed, 57 insertions(+), 29 deletions(-) diff --git a/README.md b/README.md index 2144030..b006efc 100644 --- a/README.md +++ b/README.md @@ -4,19 +4,29 @@ # Django Standardized Image Field -Django Field that implement the following features: +## Why would I want this? -* Django-Storages compatible (S3) -* Resize images to different sizes +This is a drop-in replacement for the [Django ImageField](https://docs.djangoproject.com/en/1.8/ref/models/fields/#django.db.models.ImageField) that provides a standardized way to handle image uploads. +It is designed to be as easy to use as possible, and to provide a consistent interface for all image fields. +It allows images to be presented in various size variants (eg:thumbnails, mid, and hi-res versions) +and it provides a way to handle images that are too large with validators. + + +## Features + +Django Standardized Image Field implements the following features: + +* [Django-Storages](https://django-storages.readthedocs.io/en/latest/) compatible (eg: S3, Azure, Google Cloud Storage, etc) +* Resizes images to different sizes * Access thumbnails on model level, no template tags required -* Preserves original image -* Asynchronous rendering (Celery & Co) -* Restrict accepted image dimensions -* Rename files to a standardized name (using a callable upload_to) +* Preserves original images +* Can be rendered asynchronously (ie as a [Celery job](https://realpython.com/asynchronous-tasks-with-django-and-celery/)) +* Restricts acceptable image dimensions +* Renames file to a standardized name format (using a callable `upload_to` function, see below) ## Installation -Simply install the latest stable package using the command +Simply install the latest stable package using the following command ```bash pip install django-stdimage @@ -28,11 +38,13 @@ and add `'stdimage'` to `INSTALLED_APP`s in your settings.py, that's it! ## Usage +Now it's instally you can use either: `StdImageField` or `JPEGField`. + `StdImageField` works just like Django's own [ImageField](https://docs.djangoproject.com/en/dev/ref/models/fields/#imagefield) -except that you can specify different sized variations. +except that you can specify different size variations. -The `JPEGField` works similar to the `StdImageField` but all size variations are +The `JPEGField` is the same as the `StdImageField` but all images are converted to JPEGs, no matter what type the original file is. ### Variations @@ -58,7 +70,7 @@ class MyModel(models.Model): # is the same as dictionary-style call image = StdImageField(upload_to='path/to/img', variations={'thumbnail': (100, 75)}) - # variations are converted to JPEGs + # JPEGField variations are converted to JPEGs. jpeg = JPEGField( upload_to='path/to/img', variations={'full': (None, None), 'thumbnail': (100, 75)}, @@ -77,7 +89,7 @@ class MyModel(models.Model): }, delete_orphans=True) ``` -For using generated variations in templates use `myimagefield.variation_name`. +To use these variations in templates use `myimagefield.variation_name`. Example: @@ -85,15 +97,32 @@ Example: ``` -### Utils +### Upload to function + +You can use a function for the `upload_to` argument. Using [Django Dynamic Filenames][dynamic_filenames].[dynamic_filenames]: https://github.com/codingjoe/django-dynamic-filenames -Since version 4 the custom `upload_to` utils have been dropped in favor of -[Django Dynamic Filenames][dynamic_filenames]. +This allows images to be given unique paths and filenames based on the model instance. -[dynamic_filenames]: https://github.com/codingjoe/django-dynamic-filenames +Example + +```python +from django.db import models +from stdimage import StdImageField +from dynamic_filenames import FilePattern + +upload_to_pattern = FilePattern( + filename_pattern='my_model/{app_label:.25}/{model_name:.30}/{uuid:base32}{ext}', +) + + +class MyModel(models.Model): + # works just like django's ImageField + image = StdImageField(upload_to=upload_to_pattern) +``` ### Validators -The `StdImageField` doesn't implement any size validation. Validation can be specified using the validator attribute +The `StdImageField` doesn't implement any size validation out-of-the-box. +However, Validation can be specified using the validator attribute and using a set of validators shipped with this package. Validators can be used for both Forms and Models. @@ -120,9 +149,9 @@ Django [dropped support](https://docs.djangoproject.com/en/dev/releases/1.3/#del for automated deletions in version 1.3. Since version 5, this package supports a `delete_orphans` argument. It will delete -orphaned files, should a file be delete or replaced via Django form or and object with -a `StdImageField` be deleted. It will not be deleted if the field value is changed or -reassigned programatically. In those rare cases, you will need to handle proper deletion +orphaned files, should a file be deleted or replaced via a Django form and the object with +the `StdImageField` be deleted. It will not delete files if the field value is changed or +reassigned programatically. In these rare cases, you will need to handle proper deletion yourself. ```python @@ -141,10 +170,10 @@ class MyModel(models.Model): ### Async image processing Tools like celery allow to execute time-consuming tasks outside of the request. If you don't want -to wait for your variations to be rendered in request, StdImage provides your the option to pass a -async keyword and a util. -Note that the callback is not transaction save, but the file will be there. -This example is based on celery. +to wait for your variations to be rendered in request, StdImage provides you the option to pass an +async keyword and a 'render_variations' function that triggers the async task. +Note that the callback is not transaction save, but the file variations will be present. +The example below is based on celery. `tasks.py`: ```python @@ -177,19 +206,18 @@ def image_processor(file_name, variations, storage): class AsyncImageModel(models.Model): image = StdImageField( # above task definition can only handle one model object per image filename - upload_to='path/to/file/', + upload_to='path/to/file/', # or use a function render_variations=image_processor # pass boolean or callable ) processed = models.BooleanField(default=False) # flag that could be used for view querysets ``` ### Re-rendering variations -You might want to add new variations to a field. That means you need to render new variations for missing fields. +You might have added or changed variations to an existing field. That means you will need to render new variations. This can be accomplished using a management command. ```bash python manage.py rendervariations 'app_name.model_name.field_name' [--replace] [-i/--ignore-missing] ``` The `replace` option will replace all existing files. -The `ignore-missing` option will suspend missing source file errors and keep -rendering variations for other files. Otherwise command will stop on first -missing file. +The `ignore-missing` option will suspend 'missing source file' errors and keep +rendering variations for other files. Otherwise, the command will stop on first missing file. From bec1c82fde5958eba0743c6733ade5fafaacb0bc Mon Sep 17 00:00:00 2001 From: J Harley <502818+julzhk@users.noreply.github.com> Date: Tue, 3 May 2022 09:08:34 -0300 Subject: [PATCH 04/11] Update README.md Co-authored-by: Johannes Maron --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index b006efc..e1d3f63 100644 --- a/README.md +++ b/README.md @@ -22,7 +22,7 @@ Django Standardized Image Field implements the following features: * Preserves original images * Can be rendered asynchronously (ie as a [Celery job](https://realpython.com/asynchronous-tasks-with-django-and-celery/)) * Restricts acceptable image dimensions -* Renames file to a standardized name format (using a callable `upload_to` function, see below) +* Renames a file to a standardized name format (using a callable `upload_to` function, see below) ## Installation From bed5fed42062d14a43c55c1335cbbf3d3f05ed49 Mon Sep 17 00:00:00 2001 From: J Harley <502818+julzhk@users.noreply.github.com> Date: Tue, 3 May 2022 09:08:48 -0300 Subject: [PATCH 05/11] Update README.md Co-authored-by: Johannes Maron --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index e1d3f63..cac50c5 100644 --- a/README.md +++ b/README.md @@ -26,7 +26,7 @@ Django Standardized Image Field implements the following features: ## Installation -Simply install the latest stable package using the following command +Simply install the latest stable package using the following command: ```bash pip install django-stdimage From f6d953b0cd480a037964b259d379e8d1b2ab9b88 Mon Sep 17 00:00:00 2001 From: J Harley <502818+julzhk@users.noreply.github.com> Date: Tue, 3 May 2022 09:09:14 -0300 Subject: [PATCH 06/11] Update README.md Co-authored-by: Johannes Maron --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index cac50c5..c602c24 100644 --- a/README.md +++ b/README.md @@ -44,7 +44,7 @@ Now it's instally you can use either: `StdImageField` or `JPEGField`. [ImageField](https://docs.djangoproject.com/en/dev/ref/models/fields/#imagefield) except that you can specify different size variations. -The `JPEGField` is the same as the `StdImageField` but all images are +The `JPEGField` is identical to the `StdImageField` but all images are converted to JPEGs, no matter what type the original file is. ### Variations From 7db39615f709dccf839f90503b2baa76cbd48896 Mon Sep 17 00:00:00 2001 From: Johannes Maron Date: Sat, 21 May 2022 11:53:17 +0200 Subject: [PATCH 07/11] Update CI pipline (#288) --- .github/workflows/ci.yml | 27 ++++++++++++--------------- .github/workflows/release.yml | 10 ++++++---- setup.cfg | 16 +++++++--------- 3 files changed, 25 insertions(+), 28 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 3cdbc39..3070c08 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -46,16 +46,14 @@ jobs: - run: python -m pip install -r lint-requirements.txt - run: ${{ matrix.lint-command }} - dist: runs-on: ubuntu-latest steps: - - name: Install gettext - run: sudo apt install gettext -y - - uses: actions/setup-python@v3 - - run: python -m pip install --upgrade pip setuptools wheel twine readme-renderer - uses: actions/checkout@v3 - - run: python setup.py sdist bdist_wheel + - uses: actions/setup-python@v3 + - run: sudo apt install gettext -y + - run: python -m pip install --upgrade pip build wheel twine readme-renderer + - run: python -m build --sdist --wheel - run: python -m twine check dist/* - uses: actions/upload-artifact@v3 with: @@ -73,11 +71,11 @@ jobs: - "3.9" - "3.10" django-version: - - "3.2a" - - "4.0a" + - "3.2" + - "4.0" extra: - - "" - - "progressbar" + - "test" + - "test,progressbar" steps: - name: Set up Python ${{ matrix.python-version }} uses: actions/setup-python@v3 @@ -85,10 +83,9 @@ jobs: python-version: ${{ matrix.python-version }} - run: sudo apt install gettext -y - uses: actions/checkout@v3 - - run: python -m pip install --upgrade pip setuptools codecov wheel - - run: python -m pip install .[${{ matrix.extra }}] + - run: python -m pip install --upgrade pip codecov + - run: python -m pip install -e .[${{ matrix.extra }}] if: ${{ matrix.extra }} - - run: python -m pip install django~=${{ matrix.django-version }} - - name: Test with pytest - run: python setup.py test + - run: python -m pip install django~=${{ matrix.django-version }}a + - run: python -m pytest - run: codecov diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index ff70343..19f0b4e 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -1,10 +1,12 @@ -name: PyPi Release +name: Release -on: [release] +on: + release: + types: [published] jobs: - build: + PyPi: runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 @@ -14,5 +16,5 @@ jobs: - run: python -m build --sdist --wheel - run: python -m twine upload dist/* env: - TWINE_USERNAME: ${{ secrets.TWINE_USERNAME }} + TWINE_USERNAME: __token__ TWINE_PASSWORD: ${{ secrets.TWINE_PASSWORD }} diff --git a/setup.cfg b/setup.cfg index 10e8e2b..e6adaf7 100644 --- a/setup.cfg +++ b/setup.cfg @@ -38,11 +38,6 @@ install_requires = setup_requires = setuptools_scm - pytest-runner -tests_require = - pytest - pytest-cov - pytest-django [options.package_data] * = *.txt, *.rst, *.html, *.po @@ -52,20 +47,23 @@ exclude = tests [options.extras_require] +test = + pytest + pytest-cov + pytest-django progressbar = progressbar2>=3.0.0 [bdist_wheel] universal = 1 -[aliases] -test = pytest - [tool:pytest] +testpaths = + tests norecursedirs=venv env .eggs DJANGO_SETTINGS_MODULE=tests.settings addopts = --cov=stdimage --nomigrations --tb=short filterwarnings = - error + ignore:DeprecationWarning [coverage:run] source = . From 62dbd15f1e1e6b07199fbe2be552a81d73053842 Mon Sep 17 00:00:00 2001 From: Johannes Maron Date: Sat, 21 May 2022 11:55:00 +0200 Subject: [PATCH 08/11] Fix Pillow ANTIALIAS deprecation warning --- setup.cfg | 2 +- stdimage/models.py | 3 ++- tests/test_utils.py | 4 ++-- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/setup.cfg b/setup.cfg index e6adaf7..d93790f 100644 --- a/setup.cfg +++ b/setup.cfg @@ -63,7 +63,7 @@ norecursedirs=venv env .eggs DJANGO_SETTINGS_MODULE=tests.settings addopts = --cov=stdimage --nomigrations --tb=short filterwarnings = - ignore:DeprecationWarning + error [coverage:run] source = . diff --git a/stdimage/models.py b/stdimage/models.py index 97ff514..ff13cff 100644 --- a/stdimage/models.py +++ b/stdimage/models.py @@ -11,6 +11,7 @@ ImageFileDescriptor, ) from PIL import Image, ImageFile, ImageOps +from PIL.Image import Resampling from .validators import MinSizeValidator @@ -186,7 +187,7 @@ class StdImageField(ImageField): "width": None, "height": None, "crop": False, - "resample": Image.ANTIALIAS, + "resample": Resampling.LANCZOS, } def __init__( diff --git a/tests/test_utils.py b/tests/test_utils.py index 2ab2c68..7180204 100644 --- a/tests/test_utils.py +++ b/tests/test_utils.py @@ -1,7 +1,7 @@ import os import pytest -from PIL import Image +from PIL.Image import Resampling from stdimage.utils import render_variations from tests.models import ManualVariationsModel @@ -24,7 +24,7 @@ def test_render_variations(self, image_upload_file): "width": 150, "height": 150, "crop": True, - "resample": Image.ANTIALIAS, + "resample": Resampling.LANCZOS, } }, ) From c8c389f7a9b9b4bcb0521e9ec68a3893826c672b Mon Sep 17 00:00:00 2001 From: Johannes Maron Date: Sat, 21 May 2022 11:48:02 +0200 Subject: [PATCH 09/11] Add migrations to enable a smooth transition to django-pictures We to migrate from django-stimage to pictures, we need to include variations into the migration model state. This allows deleting obsolite files during the migration. --- stdimage/models.py | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/stdimage/models.py b/stdimage/models.py index ff13cff..528bc4c 100644 --- a/stdimage/models.py +++ b/stdimage/models.py @@ -307,6 +307,19 @@ def save_form_data(self, instance, data): file.delete(save=False) super().save_form_data(instance, data) + def deconstruct(self): + name, path, args, kwargs = super().deconstruct() + return ( + name, + path, + args, + { + **kwargs, + "variations": self._variations, + "force_min_size": self.force_min_size, + }, + ) + class JPEGFieldFile(StdImageFieldFile): @classmethod From 0730fccdb5223e93b225e112dbaada05c3ce8097 Mon Sep 17 00:00:00 2001 From: Johannes Maron Date: Sat, 21 May 2022 12:02:46 +0200 Subject: [PATCH 10/11] Set developent status classifiert to inactive --- setup.cfg | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.cfg b/setup.cfg index d93790f..54c817c 100644 --- a/setup.cfg +++ b/setup.cfg @@ -9,7 +9,7 @@ url = https://github.com/codingjoe/django-stdimage license = MIT license_file = LICENSE classifier = - Development Status :: 5 - Production/Stable + Development Status :: 7 - Inactive Environment :: Web Environment Framework :: Django Topic :: Multimedia :: Graphics :: Graphics Conversion From 0c5b5e58e966e125d5c35d8fb2199983a94cdc10 Mon Sep 17 00:00:00 2001 From: Johannes Maron Date: Sat, 21 May 2022 12:23:40 +0200 Subject: [PATCH 11/11] Add deprecation warning and migration notes. --- README.md | 85 ++++++++++++++++++++++++++++++++++++++++++++-- setup.cfg | 2 +- stdimage/models.py | 9 +++++ 3 files changed, 93 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index c602c24..77fe5f6 100644 --- a/README.md +++ b/README.md @@ -4,11 +4,92 @@ # Django Standardized Image Field +This package has been deprecated in favor of [django-pictures][django-pictures]. + +## Migration Instructions + +First, make sure you understand the differences between the two packages and +how to serve images in a modern web application via the [picture][picture-tag]-Element. + +Next, follow the setup instructions for [django-pictures][django-pictures]. + +Once you are set up, change your models to use the new `PictureField` and provide the + `aspect_ratios` you'd like to serve. Do create migrations just yet. + +This step should be followed by changing your templates and frontend. +The new placeholders feature for local development should help you +to do this almost effortlessly. + +Finally, run `makemigrations` and replace the `AlterField` operation with +`AlterPictureField`. + +We highly recommend to use Django's `image_width` and `image_height` fields, to avoid +unnecessary IO. If you can add these fields to your model, you can use the following +snippet to populate them: + +```python +import django.core.files.storage +from django.db import migrations, models +import pictures.models +from pictures.migrations import AlterPictureField + +def forward(apps, schema_editor): + for obj in apps.get_model("my-app.MyModel").objects.all().iterator(): + obj.image_width = obj.logo.width + obj.image_height = obj.logo.height + obj.save(update_fields=["image_height", "image_width"]) + +def backward(apps, schema_editor): + apps.get_model("my-app.MyModel").objects.all().update( + image_width=None, + image_height=None, + ) + +class Migration(migrations.Migration): + dependencies = [ + ('my-app', '0001_initial'), + ] + + operations = [ + migrations.AddField( + model_name="mymodel", + name="image_height", + field=models.PositiveIntegerField(editable=False, null=True), + ), + migrations.AddField( + model_name="mymodel", + name="image_width", + field=models.PositiveIntegerField(editable=False, null=True), + ), + migrations.RunPython(forward, backward), + AlterPictureField( + model_name="mymodel", + name="image", + field=pictures.models.PictureField( + aspect_ratios=["3/2", "3/1"], + breakpoints={"desktop": 1024, "mobile": 576}, + container_width=1200, + file_types=["WEBP"], + grid_columns=12, + height_field="image_height", + pixel_densities=[1, 2], + storage=django.core.files.storage.FileSystemStorage(), + upload_to="pictures/", + verbose_name="image", + width_field="image_width", + ), + ), + ] +``` + +[django-pictures]: https://github.com/codingjoe/django-pictures +[picture-tag]: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/picture + ## Why would I want this? This is a drop-in replacement for the [Django ImageField](https://docs.djangoproject.com/en/1.8/ref/models/fields/#django.db.models.ImageField) that provides a standardized way to handle image uploads. It is designed to be as easy to use as possible, and to provide a consistent interface for all image fields. -It allows images to be presented in various size variants (eg:thumbnails, mid, and hi-res versions) +It allows images to be presented in various size variants (eg:thumbnails, mid, and hi-res versions) and it provides a way to handle images that are too large with validators. @@ -103,7 +184,7 @@ You can use a function for the `upload_to` argument. Using [Django Dynamic Filen This allows images to be given unique paths and filenames based on the model instance. -Example +Example ```python from django.db import models diff --git a/setup.cfg b/setup.cfg index 54c817c..5262f42 100644 --- a/setup.cfg +++ b/setup.cfg @@ -63,7 +63,7 @@ norecursedirs=venv env .eggs DJANGO_SETTINGS_MODULE=tests.settings addopts = --cov=stdimage --nomigrations --tb=short filterwarnings = - error + ignore::DeprecationWarning [coverage:run] source = . diff --git a/stdimage/models.py b/stdimage/models.py index 528bc4c..bed00d7 100644 --- a/stdimage/models.py +++ b/stdimage/models.py @@ -1,5 +1,6 @@ import logging import os +import warnings from io import BytesIO from django.core.files.base import ContentFile @@ -18,6 +19,14 @@ logger = logging.getLogger() +warnings.warn( + "The django-stdimage is deprecated in favor of django-pictures.\n" + "Migration instructions are available in the README:\n" + "https://github.com/codingjoe/django-stdimage#migration-instructions", + DeprecationWarning, +) + + class StdImageFileDescriptor(ImageFileDescriptor): """The variation property of the field is accessible in instance cases.""" pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy