mirror of
https://github.com/inventree/InvenTree
synced 2024-08-30 18:33:04 +00:00
Updates for metadata information (#7343)
* Updates for metadata information - Override 'label' values with 'verbose_name' values - Only if 'label' is *not* translated, but 'verbose_name' is - Allows the translated model fields name to be pushed through to the metadata framework * Remove unused import * Add unit testing for metadata lookup * Update serializer: allow 'category' to be blank * Bump API version * Fix for unit test
This commit is contained in:
parent
797a0c10df
commit
ea6bdd3ca6
@ -1,12 +1,15 @@
|
||||
"""InvenTree API version information."""
|
||||
|
||||
# InvenTree API version
|
||||
INVENTREE_API_VERSION = 201
|
||||
INVENTREE_API_VERSION = 202
|
||||
|
||||
"""Increment this API version number whenever there is a significant change to the API that any clients need to know about."""
|
||||
|
||||
INVENTREE_API_TEXT = """
|
||||
|
||||
v202 - 2024-05-27 : https://github.com/inventree/InvenTree/pull/7343
|
||||
- Adjust "required" attribute of Part.category field to be optional
|
||||
|
||||
v201 - 2024-05-21 : https://github.com/inventree/InvenTree/pull/7074
|
||||
- Major refactor of the report template / report printing interface
|
||||
- This is a *breaking change* to the report template API
|
||||
|
@ -115,6 +115,31 @@ class InvenTreeMetadata(SimpleMetadata):
|
||||
|
||||
return metadata
|
||||
|
||||
def override_value(self, field_name, field_value, model_value):
|
||||
"""Override a value on the serializer with a matching value for the model.
|
||||
|
||||
This is used to override the serializer values with model values,
|
||||
if (and *only* if) the model value should take precedence.
|
||||
|
||||
The values are overridden under the following conditions:
|
||||
- field_value is None
|
||||
- model_value is callable, and field_value is not (this indicates that the model value is translated)
|
||||
- model_value is not a string, and field_value is a string (this indicates that the model value is translated)
|
||||
"""
|
||||
if model_value and not field_value:
|
||||
return model_value
|
||||
|
||||
if field_value and not model_value:
|
||||
return field_value
|
||||
|
||||
if callable(model_value) and not callable(field_value):
|
||||
return model_value
|
||||
|
||||
if type(model_value) is not str and type(field_value) is str:
|
||||
return model_value
|
||||
|
||||
return field_value
|
||||
|
||||
def get_serializer_info(self, serializer):
|
||||
"""Override get_serializer_info so that we can add 'default' values to any fields whose Meta.model specifies a default value."""
|
||||
self.serializer = serializer
|
||||
@ -134,7 +159,12 @@ class InvenTreeMetadata(SimpleMetadata):
|
||||
model_class = None
|
||||
|
||||
# Attributes to copy extra attributes from the model to the field (if they don't exist)
|
||||
extra_attributes = ['help_text', 'max_length']
|
||||
# Note that the attributes may be named differently on the underlying model!
|
||||
extra_attributes = {
|
||||
'help_text': 'help_text',
|
||||
'max_length': 'max_length',
|
||||
'label': 'verbose_name',
|
||||
}
|
||||
|
||||
try:
|
||||
model_class = serializer.Meta.model
|
||||
@ -165,10 +195,12 @@ class InvenTreeMetadata(SimpleMetadata):
|
||||
elif name in model_default_values:
|
||||
serializer_info[name]['default'] = model_default_values[name]
|
||||
|
||||
for attr in extra_attributes:
|
||||
if attr not in serializer_info[name]:
|
||||
if hasattr(field, attr):
|
||||
serializer_info[name][attr] = getattr(field, attr)
|
||||
for field_key, model_key in extra_attributes.items():
|
||||
field_value = serializer_info[name].get(field_key, None)
|
||||
model_value = getattr(field, model_key, None)
|
||||
|
||||
if value := self.override_value(name, field_value, model_value):
|
||||
serializer_info[name][field_key] = value
|
||||
|
||||
# Iterate through relations
|
||||
for name, relation in model_fields.relations.items():
|
||||
@ -186,13 +218,12 @@ class InvenTreeMetadata(SimpleMetadata):
|
||||
relation.model_field.get_limit_choices_to()
|
||||
)
|
||||
|
||||
for attr in extra_attributes:
|
||||
if attr not in serializer_info[name] and hasattr(
|
||||
relation.model_field, attr
|
||||
):
|
||||
serializer_info[name][attr] = getattr(
|
||||
relation.model_field, attr
|
||||
)
|
||||
for field_key, model_key in extra_attributes.items():
|
||||
field_value = serializer_info[name].get(field_key, None)
|
||||
model_value = getattr(relation.model_field, model_key, None)
|
||||
|
||||
if value := self.override_value(name, field_value, model_value):
|
||||
serializer_info[name][field_key] = value
|
||||
|
||||
if name in model_default_values:
|
||||
serializer_info[name]['default'] = model_default_values[name]
|
||||
|
@ -851,7 +851,9 @@ class PartSerializer(
|
||||
starred = serializers.SerializerMethodField()
|
||||
|
||||
# PrimaryKeyRelated fields (Note: enforcing field type here results in much faster queries, somehow...)
|
||||
category = serializers.PrimaryKeyRelatedField(queryset=PartCategory.objects.all())
|
||||
category = serializers.PrimaryKeyRelatedField(
|
||||
queryset=PartCategory.objects.all(), required=False, allow_null=True
|
||||
)
|
||||
|
||||
# Pricing fields
|
||||
pricing_min = InvenTree.serializers.InvenTreeMoneySerializer(
|
||||
|
@ -541,13 +541,58 @@ class PartOptionsAPITest(InvenTreeAPITestCase):
|
||||
category = actions['category']
|
||||
|
||||
self.assertEqual(category['type'], 'related field')
|
||||
self.assertTrue(category['required'])
|
||||
self.assertFalse(category['required'])
|
||||
self.assertFalse(category['read_only'])
|
||||
self.assertEqual(category['label'], 'Category')
|
||||
self.assertEqual(category['model'], 'partcategory')
|
||||
self.assertEqual(category['api_url'], reverse('api-part-category-list'))
|
||||
self.assertEqual(category['help_text'], 'Part category')
|
||||
|
||||
def test_part_label_translation(self):
|
||||
"""Test that 'label' values are correctly translated."""
|
||||
response = self.options(reverse('api-part-list'))
|
||||
|
||||
labels = {
|
||||
'IPN': 'IPN',
|
||||
'category': 'Category',
|
||||
'assembly': 'Assembly',
|
||||
'ordering': 'On Order',
|
||||
'stock_item_count': 'Stock Items',
|
||||
}
|
||||
|
||||
help_text = {
|
||||
'IPN': 'Internal Part Number',
|
||||
'active': 'Is this part active?',
|
||||
'barcode_hash': 'Unique hash of barcode data',
|
||||
'category': 'Part category',
|
||||
}
|
||||
|
||||
# Check basic return values
|
||||
for field, value in labels.items():
|
||||
self.assertEqual(response.data['actions']['POST'][field]['label'], value)
|
||||
|
||||
for field, value in help_text.items():
|
||||
self.assertEqual(
|
||||
response.data['actions']['POST'][field]['help_text'], value
|
||||
)
|
||||
|
||||
# Check again, with a different locale
|
||||
response = self.options(
|
||||
reverse('api-part-list'), headers={'Accept-Language': 'de'}
|
||||
)
|
||||
|
||||
translated = {
|
||||
'IPN': 'IPN (Interne Produktnummer)',
|
||||
'category': 'Kategorie',
|
||||
'assembly': 'Baugruppe',
|
||||
'ordering': 'Bestellt',
|
||||
'stock_item_count': 'Lagerartikel',
|
||||
}
|
||||
|
||||
for field, value in translated.items():
|
||||
label = response.data['actions']['POST'][field]['label']
|
||||
self.assertEqual(label, value)
|
||||
|
||||
def test_category(self):
|
||||
"""Test the PartCategory API OPTIONS endpoint."""
|
||||
actions = self.getActions(reverse('api-part-category-list'))
|
||||
|
Loading…
x
Reference in New Issue
Block a user