mirror of
https://github.com/inventree/InvenTree.git
synced 2026-08-30 16:58:06 +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
|
||||
|
||||
- [#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
|
||||
|
||||
|
||||
@@ -309,7 +309,7 @@ class BuildFilter(FilterSet):
|
||||
|
||||
return queryset
|
||||
|
||||
tags = common.filters.TagsFilter()
|
||||
tag_name = common.filters.TagsFilter()
|
||||
|
||||
|
||||
class BuildMixin:
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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()]
|
||||
|
||||
@@ -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, [])
|
||||
|
||||
|
||||
|
||||
@@ -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):
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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`,
|
||||
|
||||
Reference in New Issue
Block a user