mirror of
https://github.com/inventree/InvenTree.git
synced 2026-08-31 17:28:07 +00:00
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:
@@ -10,6 +10,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||||||
### Breaking Changes
|
### 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.
|
- [#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
|
### Added
|
||||||
|
|
||||||
|
|||||||
@@ -1,11 +1,14 @@
|
|||||||
"""InvenTree API version information."""
|
"""InvenTree API version information."""
|
||||||
|
|
||||||
# InvenTree API version
|
# 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."""
|
"""Increment this API version number whenever there is a significant change to the API that any clients need to know about."""
|
||||||
|
|
||||||
INVENTREE_API_TEXT = """
|
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
|
v533 -> 2026-08-18 : https://github.com/inventree/InvenTree/pull/12317
|
||||||
- Refactors the PurchaseOrder, PurchaseOrderLineItem and PurchaseOrderExtraLine API endpoints to use DRF viewsets
|
- Refactors the PurchaseOrder, PurchaseOrderLineItem and PurchaseOrderExtraLine API endpoints to use DRF viewsets
|
||||||
|
|
||||||
|
|||||||
@@ -309,7 +309,7 @@ class BuildFilter(FilterSet):
|
|||||||
|
|
||||||
return queryset
|
return queryset
|
||||||
|
|
||||||
tags = common.filters.TagsFilter()
|
tag_name = common.filters.TagsFilter()
|
||||||
|
|
||||||
|
|
||||||
class BuildMixin:
|
class BuildMixin:
|
||||||
|
|||||||
@@ -786,7 +786,7 @@ class AttachmentFilter(FilterSet):
|
|||||||
return queryset.exclude(attachment=None).exclude(attachment='')
|
return queryset.exclude(attachment=None).exclude(attachment='')
|
||||||
return queryset.filter(Q(attachment=None) | Q(attachment='')).distinct()
|
return queryset.filter(Q(attachment=None) | Q(attachment='')).distinct()
|
||||||
|
|
||||||
tags = common.filters.TagsFilter()
|
tag_name = common.filters.TagsFilter()
|
||||||
|
|
||||||
|
|
||||||
class AttachmentMixin:
|
class AttachmentMixin:
|
||||||
|
|||||||
@@ -104,19 +104,16 @@ class TagsFilter(rest_filters.CharFilter):
|
|||||||
?tags=apple,banana → returns only items tagged with both 'apple' AND 'banana'
|
?tags=apple,banana → returns only items tagged with both 'apple' AND 'banana'
|
||||||
"""
|
"""
|
||||||
|
|
||||||
_is_viewset: bool = False
|
def __init__(self, *args, **kwargs):
|
||||||
|
|
||||||
def __init__(self, is_viewset: bool = False, *args, **kwargs):
|
|
||||||
"""Initialize the filter."""
|
"""Initialize the filter."""
|
||||||
if 'label' not in kwargs:
|
if 'label' not in kwargs:
|
||||||
kwargs['label'] = _('Tags')
|
kwargs['label'] = _('Tags')
|
||||||
|
|
||||||
self._is_viewset = is_viewset
|
|
||||||
super().__init__(*args, **kwargs)
|
super().__init__(*args, **kwargs)
|
||||||
|
|
||||||
def filter(self, qs, value):
|
def filter(self, qs, value):
|
||||||
"""Filter queryset to items matching all provided tag names."""
|
"""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
|
return qs
|
||||||
|
|
||||||
tag_names = [t.strip() for t in value.split(',') if t.strip()]
|
tag_names = [t.strip() for t in value.split(',') if t.strip()]
|
||||||
|
|||||||
@@ -1391,7 +1391,7 @@ class TagAPITests(InvenTreeAPITestCase):
|
|||||||
"""Filtering parts by a single tag should return only parts with that tag."""
|
"""Filtering parts by a single tag should return only parts with that tag."""
|
||||||
url = reverse('api-part-list')
|
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}
|
pks = {p['pk'] for p in response.data}
|
||||||
|
|
||||||
self.assertIn(self.part_a.pk, pks)
|
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."""
|
"""Filtering by comma-separated tags should return only parts that have ALL tags."""
|
||||||
url = reverse('api-part-list')
|
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}
|
pks = {p['pk'] for p in response.data}
|
||||||
|
|
||||||
self.assertIn(self.part_a.pk, pks)
|
self.assertIn(self.part_a.pk, pks)
|
||||||
@@ -1413,7 +1413,7 @@ class TagAPITests(InvenTreeAPITestCase):
|
|||||||
"""Tag filtering should be case-insensitive."""
|
"""Tag filtering should be case-insensitive."""
|
||||||
url = reverse('api-part-list')
|
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}
|
pks = {p['pk'] for p in response.data}
|
||||||
|
|
||||||
self.assertIn(self.part_a.pk, pks)
|
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."""
|
"""Filtering by a tag that no part has should return an empty result set."""
|
||||||
url = reverse('api-part-list')
|
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)
|
self.assertEqual(len(response.data), 0)
|
||||||
|
|
||||||
def test_part_filter_tag_whitespace(self):
|
def test_part_filter_tag_whitespace(self):
|
||||||
"""Whitespace around comma-separated tag names should be ignored."""
|
"""Whitespace around comma-separated tag names should be ignored."""
|
||||||
url = reverse('api-part-list')
|
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}
|
pks = {p['pk'] for p in response.data}
|
||||||
|
|
||||||
self.assertIn(self.part_a.pk, pks)
|
self.assertIn(self.part_a.pk, pks)
|
||||||
@@ -1484,7 +1484,7 @@ class TagAPITests(InvenTreeAPITestCase):
|
|||||||
"""
|
"""
|
||||||
url = reverse('api-part-list')
|
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, [])
|
self.assertEqual(response.data, [])
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -47,7 +47,7 @@ class CompanyFilter(FilterSet):
|
|||||||
model = Company
|
model = Company
|
||||||
fields = ['is_customer', 'is_manufacturer', 'is_supplier', 'name', 'active']
|
fields = ['is_customer', 'is_manufacturer', 'is_supplier', 'name', 'active']
|
||||||
|
|
||||||
tags = common.filters.TagsFilter()
|
tag_name = common.filters.TagsFilter()
|
||||||
|
|
||||||
|
|
||||||
class CompanyMixin(OutputOptionsMixin):
|
class CompanyMixin(OutputOptionsMixin):
|
||||||
@@ -152,7 +152,7 @@ class ManufacturerPartFilter(FilterSet):
|
|||||||
field_name='manufacturer__active', label=_('Manufacturer is Active')
|
field_name='manufacturer__active', label=_('Manufacturer is Active')
|
||||||
)
|
)
|
||||||
|
|
||||||
tags = common.filters.TagsFilter()
|
tag_name = common.filters.TagsFilter()
|
||||||
|
|
||||||
|
|
||||||
class ManufacturerOutputOptions(OutputConfiguration):
|
class ManufacturerOutputOptions(OutputConfiguration):
|
||||||
@@ -308,7 +308,7 @@ class SupplierPartFilter(FilterSet):
|
|||||||
else:
|
else:
|
||||||
return queryset.exclude(in_stock__gt=0)
|
return queryset.exclude(in_stock__gt=0)
|
||||||
|
|
||||||
tags = common.filters.TagsFilter()
|
tag_name = common.filters.TagsFilter()
|
||||||
|
|
||||||
|
|
||||||
class SupplierPartOutputOptions(OutputConfiguration):
|
class SupplierPartOutputOptions(OutputConfiguration):
|
||||||
|
|||||||
@@ -286,7 +286,7 @@ class OrderFilter(FilterSet):
|
|||||||
|
|
||||||
return queryset.filter(q1 | q2 | q3 | q4).distinct()
|
return queryset.filter(q1 | q2 | q3 | q4).distinct()
|
||||||
|
|
||||||
tags = common.filters.TagsFilter()
|
tag_name = common.filters.TagsFilter()
|
||||||
|
|
||||||
|
|
||||||
class LineItemFilter(FilterSet):
|
class LineItemFilter(FilterSet):
|
||||||
@@ -374,8 +374,6 @@ class PurchaseOrderFilter(OrderFilter):
|
|||||||
"""
|
"""
|
||||||
return queryset.filter(lines__build_order=build).distinct()
|
return queryset.filter(lines__build_order=build).distinct()
|
||||||
|
|
||||||
tags = common.filters.TagsFilter(is_viewset=True)
|
|
||||||
|
|
||||||
|
|
||||||
class PurchaseOrderOutputOptions(OutputConfiguration):
|
class PurchaseOrderOutputOptions(OutputConfiguration):
|
||||||
"""Output options for the PurchaseOrder endpoint."""
|
"""Output options for the PurchaseOrder endpoint."""
|
||||||
@@ -1546,7 +1544,7 @@ class SalesOrderShipmentFilter(FilterSet):
|
|||||||
|
|
||||||
return queryset.filter(q1 | q2).distinct()
|
return queryset.filter(q1 | q2).distinct()
|
||||||
|
|
||||||
tags = common.filters.TagsFilter()
|
tag_name = common.filters.TagsFilter()
|
||||||
|
|
||||||
|
|
||||||
class SalesOrderShipmentMixin:
|
class SalesOrderShipmentMixin:
|
||||||
|
|||||||
@@ -936,7 +936,7 @@ class PartFilter(FilterSet):
|
|||||||
|
|
||||||
consumable = rest_filters.BooleanFilter()
|
consumable = rest_filters.BooleanFilter()
|
||||||
|
|
||||||
tags = common.filters.TagsFilter()
|
tag_name = common.filters.TagsFilter()
|
||||||
|
|
||||||
# Created date filters
|
# Created date filters
|
||||||
created_before = InvenTreeDateFilter(
|
created_before = InvenTreeDateFilter(
|
||||||
|
|||||||
@@ -412,7 +412,7 @@ class StockLocationFilter(FilterSet):
|
|||||||
|
|
||||||
return queryset
|
return queryset
|
||||||
|
|
||||||
tags = common.filters.TagsFilter()
|
tag_name = common.filters.TagsFilter()
|
||||||
|
|
||||||
|
|
||||||
class StockLocationMixin(SerializerContextMixin):
|
class StockLocationMixin(SerializerContextMixin):
|
||||||
@@ -1067,7 +1067,7 @@ class StockFilter(FilterSet):
|
|||||||
children = loc_obj.getUniqueChildren()
|
children = loc_obj.getUniqueChildren()
|
||||||
return queryset.filter(location__in=children)
|
return queryset.filter(location__in=children)
|
||||||
|
|
||||||
tags = common.filters.TagsFilter()
|
tag_name = common.filters.TagsFilter()
|
||||||
|
|
||||||
|
|
||||||
class StockApiMixin(SerializerContextMixin):
|
class StockApiMixin(SerializerContextMixin):
|
||||||
|
|||||||
@@ -408,7 +408,7 @@ export function TagsFilter({
|
|||||||
modelType?: ModelType;
|
modelType?: ModelType;
|
||||||
}): TableFilter {
|
}): TableFilter {
|
||||||
return {
|
return {
|
||||||
name: 'tags',
|
name: 'tag_name',
|
||||||
label: t`Tags`,
|
label: t`Tags`,
|
||||||
description: t`Filter by tags`,
|
description: t`Filter by tags`,
|
||||||
placeholder: t`Select tags`,
|
placeholder: t`Select tags`,
|
||||||
|
|||||||
Reference in New Issue
Block a user