From 9d19b32b16d7424d349644b4fb8d9c3377d40365 Mon Sep 17 00:00:00 2001 From: Matthias Mair Date: Fri, 21 Aug 2026 23:46:26 +0200 Subject: [PATCH] feat(backend): rename filter (#12672) * feat(backend): rename filter * fix filter name * fix parameter used in test * remove temporary fix added in https://github.com/inventree/InvenTree/pull/12668 * remove unneeded filter redef * add changelog --------- Co-authored-by: Oliver --- CHANGELOG.md | 2 ++ src/backend/InvenTree/InvenTree/api_version.py | 5 ++++- src/backend/InvenTree/build/api.py | 2 +- src/backend/InvenTree/common/api.py | 2 +- src/backend/InvenTree/common/filters.py | 7 ++----- src/backend/InvenTree/common/test_api.py | 12 ++++++------ src/backend/InvenTree/company/api.py | 6 +++--- src/backend/InvenTree/order/api.py | 6 ++---- src/backend/InvenTree/part/api.py | 2 +- src/backend/InvenTree/stock/api.py | 4 ++-- src/frontend/src/components/tables/Filter.tsx | 2 +- 11 files changed, 25 insertions(+), 25 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f6631a6706..f1f4bf1f4e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Breaking Changes - [#12507](https://github.com/inventree/InvenTree/pull/12507) calling an invalid or repeated state transition now raises a ValidationError. Plugins implementing state transitions should evaluate the PR and adapt their usage of transitions to gain the new safeguards. +- [#12672](https://github.com/inventree/InvenTree/pull/12672) renames the newly added `tags` filter from 1.4.0 (https://github.com/inventree/InvenTree/pull/12077) to `tag_name` to remove a nameclash. + ### Added diff --git a/src/backend/InvenTree/InvenTree/api_version.py b/src/backend/InvenTree/InvenTree/api_version.py index 06064a4f1d..3cef64b3a5 100644 --- a/src/backend/InvenTree/InvenTree/api_version.py +++ b/src/backend/InvenTree/InvenTree/api_version.py @@ -1,11 +1,14 @@ """InvenTree API version information.""" # InvenTree API version -INVENTREE_API_VERSION = 533 +INVENTREE_API_VERSION = 534 """Increment this API version number whenever there is a significant change to the API that any clients need to know about.""" INVENTREE_API_TEXT = """ +v534 -> 2026-08-21 : https://github.com/inventree/InvenTree/pull/12672 + - rename 'tags' filter to 'tag_name' to avoid name clash with the 'tags' field on various API endpoints + v533 -> 2026-08-18 : https://github.com/inventree/InvenTree/pull/12317 - Refactors the PurchaseOrder, PurchaseOrderLineItem and PurchaseOrderExtraLine API endpoints to use DRF viewsets diff --git a/src/backend/InvenTree/build/api.py b/src/backend/InvenTree/build/api.py index 41328bf67b..b440b2d876 100644 --- a/src/backend/InvenTree/build/api.py +++ b/src/backend/InvenTree/build/api.py @@ -309,7 +309,7 @@ class BuildFilter(FilterSet): return queryset - tags = common.filters.TagsFilter() + tag_name = common.filters.TagsFilter() class BuildMixin: diff --git a/src/backend/InvenTree/common/api.py b/src/backend/InvenTree/common/api.py index 885d3efd69..489530c51e 100644 --- a/src/backend/InvenTree/common/api.py +++ b/src/backend/InvenTree/common/api.py @@ -786,7 +786,7 @@ class AttachmentFilter(FilterSet): return queryset.exclude(attachment=None).exclude(attachment='') return queryset.filter(Q(attachment=None) | Q(attachment='')).distinct() - tags = common.filters.TagsFilter() + tag_name = common.filters.TagsFilter() class AttachmentMixin: diff --git a/src/backend/InvenTree/common/filters.py b/src/backend/InvenTree/common/filters.py index aaa491e3b9..b584669bf3 100644 --- a/src/backend/InvenTree/common/filters.py +++ b/src/backend/InvenTree/common/filters.py @@ -104,19 +104,16 @@ class TagsFilter(rest_filters.CharFilter): ?tags=apple,banana → returns only items tagged with both 'apple' AND 'banana' """ - _is_viewset: bool = False - - def __init__(self, is_viewset: bool = False, *args, **kwargs): + def __init__(self, *args, **kwargs): """Initialize the filter.""" if 'label' not in kwargs: kwargs['label'] = _('Tags') - self._is_viewset = is_viewset super().__init__(*args, **kwargs) def filter(self, qs, value): """Filter queryset to items matching all provided tag names.""" - if not value or (self._is_viewset and InvenTree.helpers.is_bool(value)): + if not value: return qs tag_names = [t.strip() for t in value.split(',') if t.strip()] diff --git a/src/backend/InvenTree/common/test_api.py b/src/backend/InvenTree/common/test_api.py index 71602098ca..038bd02689 100644 --- a/src/backend/InvenTree/common/test_api.py +++ b/src/backend/InvenTree/common/test_api.py @@ -1391,7 +1391,7 @@ class TagAPITests(InvenTreeAPITestCase): """Filtering parts by a single tag should return only parts with that tag.""" url = reverse('api-part-list') - response = self.get(url, data={'tags': 'apple'}) + response = self.get(url, data={'tag_name': 'apple'}) pks = {p['pk'] for p in response.data} self.assertIn(self.part_a.pk, pks) @@ -1402,7 +1402,7 @@ class TagAPITests(InvenTreeAPITestCase): """Filtering by comma-separated tags should return only parts that have ALL tags.""" url = reverse('api-part-list') - response = self.get(url, data={'tags': 'apple,banana'}) + response = self.get(url, data={'tag_name': 'apple,banana'}) pks = {p['pk'] for p in response.data} self.assertIn(self.part_a.pk, pks) @@ -1413,7 +1413,7 @@ class TagAPITests(InvenTreeAPITestCase): """Tag filtering should be case-insensitive.""" url = reverse('api-part-list') - response = self.get(url, data={'tags': 'APPLE'}) + response = self.get(url, data={'tag_name': 'APPLE'}) pks = {p['pk'] for p in response.data} self.assertIn(self.part_a.pk, pks) @@ -1423,14 +1423,14 @@ class TagAPITests(InvenTreeAPITestCase): """Filtering by a tag that no part has should return an empty result set.""" url = reverse('api-part-list') - response = self.get(url, data={'tags': 'doesnotexist'}) + response = self.get(url, data={'tag_name': 'doesnotexist'}) self.assertEqual(len(response.data), 0) def test_part_filter_tag_whitespace(self): """Whitespace around comma-separated tag names should be ignored.""" url = reverse('api-part-list') - response = self.get(url, data={'tags': ' apple , banana '}) + response = self.get(url, data={'tag_name': ' apple , banana '}) pks = {p['pk'] for p in response.data} self.assertIn(self.part_a.pk, pks) @@ -1484,7 +1484,7 @@ class TagAPITests(InvenTreeAPITestCase): """ url = reverse('api-part-list') - response = self.get(url, data={'tags': 'true'}, expected_code=200) + response = self.get(url, data={'tag_name': 'true'}, expected_code=200) self.assertEqual(response.data, []) diff --git a/src/backend/InvenTree/company/api.py b/src/backend/InvenTree/company/api.py index c3e28728ca..58642a30fa 100644 --- a/src/backend/InvenTree/company/api.py +++ b/src/backend/InvenTree/company/api.py @@ -47,7 +47,7 @@ class CompanyFilter(FilterSet): model = Company fields = ['is_customer', 'is_manufacturer', 'is_supplier', 'name', 'active'] - tags = common.filters.TagsFilter() + tag_name = common.filters.TagsFilter() class CompanyMixin(OutputOptionsMixin): @@ -152,7 +152,7 @@ class ManufacturerPartFilter(FilterSet): field_name='manufacturer__active', label=_('Manufacturer is Active') ) - tags = common.filters.TagsFilter() + tag_name = common.filters.TagsFilter() class ManufacturerOutputOptions(OutputConfiguration): @@ -308,7 +308,7 @@ class SupplierPartFilter(FilterSet): else: return queryset.exclude(in_stock__gt=0) - tags = common.filters.TagsFilter() + tag_name = common.filters.TagsFilter() class SupplierPartOutputOptions(OutputConfiguration): diff --git a/src/backend/InvenTree/order/api.py b/src/backend/InvenTree/order/api.py index 492ca7e551..a357ed61eb 100644 --- a/src/backend/InvenTree/order/api.py +++ b/src/backend/InvenTree/order/api.py @@ -286,7 +286,7 @@ class OrderFilter(FilterSet): return queryset.filter(q1 | q2 | q3 | q4).distinct() - tags = common.filters.TagsFilter() + tag_name = common.filters.TagsFilter() class LineItemFilter(FilterSet): @@ -374,8 +374,6 @@ class PurchaseOrderFilter(OrderFilter): """ return queryset.filter(lines__build_order=build).distinct() - tags = common.filters.TagsFilter(is_viewset=True) - class PurchaseOrderOutputOptions(OutputConfiguration): """Output options for the PurchaseOrder endpoint.""" @@ -1546,7 +1544,7 @@ class SalesOrderShipmentFilter(FilterSet): return queryset.filter(q1 | q2).distinct() - tags = common.filters.TagsFilter() + tag_name = common.filters.TagsFilter() class SalesOrderShipmentMixin: diff --git a/src/backend/InvenTree/part/api.py b/src/backend/InvenTree/part/api.py index a773440c26..6cebb65686 100644 --- a/src/backend/InvenTree/part/api.py +++ b/src/backend/InvenTree/part/api.py @@ -936,7 +936,7 @@ class PartFilter(FilterSet): consumable = rest_filters.BooleanFilter() - tags = common.filters.TagsFilter() + tag_name = common.filters.TagsFilter() # Created date filters created_before = InvenTreeDateFilter( diff --git a/src/backend/InvenTree/stock/api.py b/src/backend/InvenTree/stock/api.py index 114be565ed..d6dfa94008 100644 --- a/src/backend/InvenTree/stock/api.py +++ b/src/backend/InvenTree/stock/api.py @@ -412,7 +412,7 @@ class StockLocationFilter(FilterSet): return queryset - tags = common.filters.TagsFilter() + tag_name = common.filters.TagsFilter() class StockLocationMixin(SerializerContextMixin): @@ -1067,7 +1067,7 @@ class StockFilter(FilterSet): children = loc_obj.getUniqueChildren() return queryset.filter(location__in=children) - tags = common.filters.TagsFilter() + tag_name = common.filters.TagsFilter() class StockApiMixin(SerializerContextMixin): diff --git a/src/frontend/src/components/tables/Filter.tsx b/src/frontend/src/components/tables/Filter.tsx index 55acea4e41..6597b4ef24 100644 --- a/src/frontend/src/components/tables/Filter.tsx +++ b/src/frontend/src/components/tables/Filter.tsx @@ -408,7 +408,7 @@ export function TagsFilter({ modelType?: ModelType; }): TableFilter { return { - name: 'tags', + name: 'tag_name', label: t`Tags`, description: t`Filter by tags`, placeholder: t`Select tags`,