diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 4ca51c53..55daaedb 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -20,42 +20,40 @@ jobs: matrix: python-version: - - '3.7' - - '3.8' - - '3.9' - - '3.10' - - '3.11' + - "3.8" + - "3.9" + - "3.10" + - "3.11" steps: - - uses: actions/checkout@v2 - - - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v2 - with: - python-version: ${{ matrix.python-version }} - - - name: Get pip cache dir - id: pip-cache - run: echo "::set-output name=dir::$(pip cache dir)" - - - name: Cache - uses: actions/cache@v2 - with: - path: ${{ steps.pip-cache.outputs.dir }} - key: - ${{ matrix.python-version }}-v1-${{ hashFiles('**/setup.py') }}-${{ hashFiles('**/tox.ini') }} - restore-keys: | - ${{ matrix.python-version }}-v1- - - - name: Install Python dependencies - run: | - python -m pip install --upgrade pip - python -m pip install --upgrade tox tox-gh-actions - - - name: Tox tests - run: tox -v - - - name: Upload coverage - uses: codecov/codecov-action@v1 - with: - name: Python ${{ matrix.python-version }} + - uses: actions/checkout@v2 + + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v2 + with: + python-version: ${{ matrix.python-version }} + + - name: Get pip cache dir + id: pip-cache + run: echo "::set-output name=dir::$(pip cache dir)" + + - name: Cache + uses: actions/cache@v2 + with: + path: ${{ steps.pip-cache.outputs.dir }} + key: ${{ matrix.python-version }}-v1-${{ hashFiles('**/setup.py') }}-${{ hashFiles('**/tox.ini') }} + restore-keys: | + ${{ matrix.python-version }}-v1- + + - name: Install Python dependencies + run: | + python -m pip install --upgrade pip + python -m pip install --upgrade tox tox-gh-actions + + - name: Tox tests + run: tox -v + + - name: Upload coverage + uses: codecov/codecov-action@v1 + with: + name: Python ${{ matrix.python-version }} diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 1e413b10..bc8367d5 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -3,6 +3,15 @@ Changelog (Unreleased) ~~~~~~~~~~~~ +* **Backwards icompatible:** Rename the (``content_type``, ``object_id``) index on ``TaggedItem``. + It is very unlikely for this to affect your code itself, and a migration will rename the index. This should not cause any downtime according to my research (Postgres does not lock the table for index renames, and Oracle holds a tiny lock to do it, and the change is only to the metadata, so is not dependent on table size). + +* **Backwards incompatible:** Remove the ``.indexed_together`` and ``.unique_together`` attributes on ``TaggedItem`` + + We are instead using ``constraints`` and ``indexes`` to set up these properties. +* Remove support for Django 3.2. +* Remove usage of deprecated APIs for Django 4.2 +* Remove support for Python 3.7 (no code changes involved) * Fix ``tag_kwargs`` and ``TAGGIT_CASE_INSENSITIVE=True`` discrepency. 4.0.0 (2023-05-04) diff --git a/setup.cfg b/setup.cfg index 74607fd9..086949a9 100644 --- a/setup.cfg +++ b/setup.cfg @@ -44,6 +44,7 @@ exclude = tests* [flake8] # E501: line too long ignore = E501 +exclude = .venv,.git,.tox [isort] profile = black diff --git a/taggit/managers.py b/taggit/managers.py index 85542c90..90a24a3d 100644 --- a/taggit/managers.py +++ b/taggit/managers.py @@ -84,8 +84,19 @@ def get_queryset(self, extra_filters=None): ) def get_prefetch_queryset(self, instances, queryset=None): - if queryset is not None: - raise ValueError("Custom queryset can't be used for this lookup.") + if queryset is None: + return self.get_prefetch_querysets(instances) + else: + return self.get_prefetch_querysets(instances, [queryset]) + + def get_prefetch_querysets(self, instances, querysets=None): + if querysets is not None: + # this queryset is meant to be used for filtering down the prefetch + # this work has not been done yet. + # + # Some hint from Django: asserting that len(querysets) == 1 if it's not None + # and then using that to filter down the qs + raise ValueError("Custom querysets can't be used for this lookup.") instance = instances[0] db = self._db or router.db_for_read(type(instance), instance=instance) diff --git a/taggit/migrations/0002_auto_20150616_2121.py b/taggit/migrations/0002_auto_20150616_2121.py index af30bc08..9d5e76d3 100644 --- a/taggit/migrations/0002_auto_20150616_2121.py +++ b/taggit/migrations/0002_auto_20150616_2121.py @@ -1,11 +1,23 @@ -from django.db import migrations +from django.db import migrations, models class Migration(migrations.Migration): dependencies = [("taggit", "0001_initial")] operations = [ - migrations.AlterIndexTogether( - name="taggeditem", index_together={("content_type", "object_id")} + # this migration was modified from previously being + # a ModifyIndexTogether operation. + # + # If you are a long-enough user of this library, the name + # of the index does not match what is written here. Please + # query the DB itself to find out what the name originally was. + migrations.AddIndex( + "taggeditem", + models.Index( + fields=("content_type", "object_id"), + # this is not the name of the index in previous version, + # but this is necessary to deal with index_together issues. + name="taggit_tagg_content_8fc721_idx", + ), ) ] diff --git a/taggit/migrations/0003_taggeditem_add_unique_index.py b/taggit/migrations/0003_taggeditem_add_unique_index.py index c46d1145..337719ff 100644 --- a/taggit/migrations/0003_taggeditem_add_unique_index.py +++ b/taggit/migrations/0003_taggeditem_add_unique_index.py @@ -1,4 +1,4 @@ -from django.db import migrations +from django.db import migrations, models class Migration(migrations.Migration): @@ -8,7 +8,13 @@ class Migration(migrations.Migration): ] operations = [ - migrations.AlterUniqueTogether( - name="taggeditem", unique_together={("content_type", "object_id", "tag")} - ) + # this migration was modified to declare a uniqueness constraint differently + # this change was written on 2023-09-20, if any issues occurred from this please report it upstream + migrations.AddConstraint( + model_name="taggeditem", + constraint=models.UniqueConstraint( + fields=("content_type", "object_id", "tag"), + name="taggit_taggeditem_content_type_id_object_id_tag_id_4bb97a8e_uniq", + ), + ), ] diff --git a/taggit/migrations/0006_rename_taggeditem_content_type_object_id_taggit_tagg_content_8fc721_idx.py b/taggit/migrations/0006_rename_taggeditem_content_type_object_id_taggit_tagg_content_8fc721_idx.py new file mode 100644 index 00000000..c23966c0 --- /dev/null +++ b/taggit/migrations/0006_rename_taggeditem_content_type_object_id_taggit_tagg_content_8fc721_idx.py @@ -0,0 +1,16 @@ +# Generated by Django 4.2.5 on 2023-09-19 23:16 +from django.db import migrations + + +class Migration(migrations.Migration): + dependencies = [ + ("taggit", "0005_auto_20220424_2025"), + ] + + operations = [ + migrations.RenameIndex( + model_name="taggeditem", + new_name="taggit_tagg_content_8fc721_idx", + old_fields=("content_type", "object_id"), + ), + ] diff --git a/taggit/models.py b/taggit/models.py index 05bca49f..8d7f60bd 100644 --- a/taggit/models.py +++ b/taggit/models.py @@ -187,5 +187,15 @@ class Meta: verbose_name = _("tagged item") verbose_name_plural = _("tagged items") app_label = "taggit" - index_together = [["content_type", "object_id"]] - unique_together = [["content_type", "object_id", "tag"]] + indexes = [ + models.Index( + fields=["content_type", "object_id"], + ) + ] + + constraints = [ + models.UniqueConstraint( + fields=("content_type", "object_id", "tag"), + name="taggit_taggeditem_content_type_id_object_id_tag_id_4bb97a8e_uniq", + ) + ] diff --git a/tests/settings.py b/tests/settings.py index 3e96efdf..4e7a4b21 100644 --- a/tests/settings.py +++ b/tests/settings.py @@ -43,3 +43,5 @@ STATIC_URL = "/static/" DEFAULT_AUTO_FIELD = "django.db.models.AutoField" + +USE_TZ = True diff --git a/tests/tests.py b/tests/tests.py index 4b837fc5..cb0f1687 100644 --- a/tests/tests.py +++ b/tests/tests.py @@ -67,6 +67,9 @@ UUIDTaggedItem, ) +if DJANGO_VERSION < (4, 2): + TestCase.assertQuerySetEqual = TestCase.assertQuerysetEqual + class BaseTaggingTestCase(TestCase): def assert_tags_equal(self, qs, tags, sort=True, attr="name"): @@ -591,7 +594,7 @@ def test_lookup_by_tag(self): pks = self.pet_model.objects.filter(tags__name__in=["fuzzy"]) model_name = self.pet_model.__name__ - self.assertQuerysetEqual( + self.assertQuerySetEqual( pks, [f"<{model_name}: kitty>", f"<{model_name}: cat>"], ordered=False, @@ -609,7 +612,7 @@ def test_exclude(self): pks = self.food_model.objects.exclude(tags__name__in=["red"]) model_name = self.food_model.__name__ - self.assertQuerysetEqual( + self.assertQuerySetEqual( pks, [f"<{model_name}: pear>", f"<{model_name}: guava>"], ordered=False, @@ -963,20 +966,23 @@ def _get_form_str(self, form_str): # https://github.com/django/django/commit/98756c685ee173bbd43f21ed0553f808be835ce5 # https://github.com/django/django/commit/232b60a21b951bd16b8c95b34fcbcbf3ecd89fca form_str %= { - "help_start": '