2
0
mirror of https://github.com/inventree/InvenTree.git synced 2025-06-18 13:05:42 +00:00

Adds a metadata serializer class for accessing instance metadata via the API

- Adds endpoint for Part
- Adds endpoint for PartCategory
- Adds endpoint for StockItem
- Adds endpoint for StockLocation
This commit is contained in:
Oliver Walters
2022-05-16 20:20:32 +10:00
parent cd68d5a80e
commit 37a74dbfef
7 changed files with 212 additions and 3 deletions

View File

@ -44,6 +44,7 @@ from stock.models import StockItem, StockLocation
from common.models import InvenTreeSetting
from build.models import Build, BuildItem
import order.models
from plugin.serializers import MetadataSerializer
from . import serializers as part_serializers
@ -203,6 +204,15 @@ class CategoryDetail(generics.RetrieveUpdateDestroyAPIView):
return response
class CategoryMetadata(generics.RetrieveUpdateAPIView):
"""API endpoint for viewing / updating PartCategory metadata"""
def get_serializer(self, *args, **kwargs):
return MetadataSerializer(PartCategory, *args, **kwargs)
queryset = PartCategory.objects.all()
class CategoryParameterList(generics.ListAPIView):
""" API endpoint for accessing a list of PartCategoryParameterTemplate objects.
@ -587,6 +597,17 @@ class PartScheduling(generics.RetrieveAPIView):
return Response(schedule)
class PartMetadata(generics.RetrieveUpdateAPIView):
"""
API endpoint for viewing / updating Part metadata
"""
def get_serializer(self, *args, **kwargs):
return MetadataSerializer(Part, *args, **kwargs)
queryset = Part.objects.all()
class PartSerialNumberDetail(generics.RetrieveAPIView):
"""
API endpoint for returning extra serial number information about a particular part
@ -1912,7 +1933,15 @@ part_api_urls = [
re_path(r'^tree/', CategoryTree.as_view(), name='api-part-category-tree'),
re_path(r'^parameters/', CategoryParameterList.as_view(), name='api-part-category-parameter-list'),
re_path(r'^(?P<pk>\d+)/?', CategoryDetail.as_view(), name='api-part-category-detail'),
# Category detail endpoints
re_path(r'^(?P<pk>\d+)/', include([
re_path(r'^metadata/', CategoryMetadata.as_view(), name='api-part-category-metadata'),
# PartCategory detail endpoint
re_path(r'^.*$', CategoryDetail.as_view(), name='api-part-category-detail'),
])),
path('', CategoryList.as_view(), name='api-part-category-list'),
])),
@ -1973,6 +2002,9 @@ part_api_urls = [
# Endpoint for validating a BOM for the specific Part
re_path(r'^bom-validate/', PartValidateBOM.as_view(), name='api-part-bom-validate'),
# Part metadata
re_path(r'^metadata/', PartMetadata.as_view(), name='api-part-metadata'),
# Part detail endpoint
re_path(r'^.*$', PartDetail.as_view(), name='api-part-detail'),
])),

View File

@ -1021,6 +1021,59 @@ class PartDetailTests(InvenTreeAPITestCase):
self.assertEqual(data['in_stock'], 9000)
self.assertEqual(data['unallocated_stock'], 9000)
def test_part_metadata(self):
"""
Tests for the part metadata endpoint
"""
url = reverse('api-part-metadata', kwargs={'pk': 1})
part = Part.objects.get(pk=1)
# Metadata is initially null
self.assertIsNone(part.metadata)
part.metadata = {'foo': 'bar'}
part.save()
response = self.get(url, expected_code=200)
self.assertEqual(response.data['metadata']['foo'], 'bar')
# Add more data via the API
# Using the 'patch' method causes the new data to be merged in
self.patch(
url,
{
'metadata': {
'hello': 'world',
}
},
expected_code=200
)
part.refresh_from_db()
self.assertEqual(part.metadata['foo'], 'bar')
self.assertEqual(part.metadata['hello'], 'world')
# Now, issue a PUT request (existing data will be replacted)
self.put(
url,
{
'metadata': {
'x': 'y'
},
},
expected_code=200
)
part.refresh_from_db()
self.assertFalse('foo' in part.metadata)
self.assertFalse('hello' in part.metadata)
self.assertEqual(part.metadata['x'], 'y')
class PartAPIAggregationTest(InvenTreeAPITestCase):
"""

View File

@ -245,6 +245,24 @@ class PartTest(TestCase):
self.assertEqual(float(self.r1.get_internal_price(1)), 0.08)
self.assertEqual(float(self.r1.get_internal_price(10)), 0.5)
def test_metadata(self):
"""Unit tests for the Part metadata field"""
p = Part.objects.get(pk=1)
self.assertIsNone(p.metadata)
self.assertIsNone(p.get_metadata('test'))
self.assertEqual(p.get_metadata('test', backup_value=123), 123)
# Test update via the set_metadata() method
p.set_metadata('test', 3)
self.assertEqual(p.get_metadata('test'), 3)
for k in ['apple', 'banana', 'carrot', 'carrot', 'banana']:
p.set_metadata(k, k)
self.assertEqual(len(p.metadata.keys()), 4)
class TestTemplateTest(TestCase):