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 <oliver.henry.walters@gmail.com>
This commit is contained in:
Matthias Mair
2026-08-22 07:46:26 +10:00
committed by GitHub
co-authored by Oliver
parent a435738507
commit 9d19b32b16
11 changed files with 25 additions and 25 deletions
+2
View File
@@ -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
@@ -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
+1 -1
View File
@@ -309,7 +309,7 @@ class BuildFilter(FilterSet):
return queryset
tags = common.filters.TagsFilter()
tag_name = common.filters.TagsFilter()
class BuildMixin:
+1 -1
View File
@@ -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:
+2 -5
View File
@@ -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()]
+6 -6
View File
@@ -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, [])
+3 -3
View File
@@ -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):
+2 -4
View File
@@ -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:
+1 -1
View File
@@ -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(
+2 -2
View File
@@ -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):
@@ -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`,