mirror of
https://github.com/inventree/InvenTree.git
synced 2025-12-16 09:18:10 +00:00
Add validator for ParameterTemplate model type
- Update migrations - Make Parameter class abstract (for now) - Validators
This commit is contained in:
@@ -1,5 +1,6 @@
|
|||||||
# Generated by Django 4.2.25 on 2025-10-28 11:11
|
# Generated by Django 4.2.25 on 2025-10-28 11:11
|
||||||
|
|
||||||
|
import common.validators
|
||||||
import InvenTree.models
|
import InvenTree.models
|
||||||
import InvenTree.validators
|
import InvenTree.validators
|
||||||
from django.db import migrations, models
|
from django.db import migrations, models
|
||||||
@@ -62,6 +63,17 @@ class Migration(migrations.Migration):
|
|||||||
verbose_name="Description",
|
verbose_name="Description",
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
(
|
||||||
|
"model_type",
|
||||||
|
models.CharField(
|
||||||
|
blank=True,
|
||||||
|
default="",
|
||||||
|
help_text="Target model type for this parameter",
|
||||||
|
max_length=100,
|
||||||
|
validators=[common.validators.validate_parameter_template_model_type],
|
||||||
|
verbose_name="Model type",
|
||||||
|
),
|
||||||
|
),
|
||||||
(
|
(
|
||||||
"checkbox",
|
"checkbox",
|
||||||
models.BooleanField(
|
models.BooleanField(
|
||||||
|
|||||||
@@ -8,6 +8,11 @@ def copy_templates(old_model, new_model):
|
|||||||
|
|
||||||
templates = []
|
templates = []
|
||||||
|
|
||||||
|
# Clear out all existing instances
|
||||||
|
new_model.objects.all().delete()
|
||||||
|
|
||||||
|
assert new_model.objects.count() == 0
|
||||||
|
|
||||||
for template in old_model.objects.all():
|
for template in old_model.objects.all():
|
||||||
templates.append(new_model(
|
templates.append(new_model(
|
||||||
name=template.name,
|
name=template.name,
|
||||||
@@ -23,6 +28,8 @@ def copy_templates(old_model, new_model):
|
|||||||
new_model.objects.bulk_create(templates)
|
new_model.objects.bulk_create(templates)
|
||||||
print(f"Migrated {len(templates)} ParameterTemplate instances.")
|
print(f"Migrated {len(templates)} ParameterTemplate instances.")
|
||||||
|
|
||||||
|
assert new_model.objects.count() == len(templates)
|
||||||
|
|
||||||
|
|
||||||
def forward_copy_templates(apps, schema_editor):
|
def forward_copy_templates(apps, schema_editor):
|
||||||
"""Forward migration: copy from PartParameterTemplate to ParameterTemplate."""
|
"""Forward migration: copy from PartParameterTemplate to ParameterTemplate."""
|
||||||
@@ -43,6 +50,7 @@ def reverse_copy_templates(apps, schema_editor):
|
|||||||
class Migration(migrations.Migration):
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
("part", "0132_partparametertemplate_selectionlist"),
|
||||||
("common", "0040_parametertemplate"),
|
("common", "0040_parametertemplate"),
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|||||||
@@ -72,14 +72,21 @@ class RenderMeta(enums.ChoicesMeta):
|
|||||||
"""Metaclass for rendering choices."""
|
"""Metaclass for rendering choices."""
|
||||||
|
|
||||||
choice_fnc = None
|
choice_fnc = None
|
||||||
|
allow_blank: bool = False
|
||||||
|
blank_label: str = '------'
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def choices(self):
|
def choices(self):
|
||||||
"""Return a list of choices for the enum class."""
|
"""Return a list of choices for the enum class."""
|
||||||
fnc = getattr(self, 'choice_fnc', None)
|
fnc = getattr(self, 'choice_fnc', None)
|
||||||
if fnc:
|
if fnc:
|
||||||
return fnc()
|
options = fnc()
|
||||||
return []
|
options = []
|
||||||
|
|
||||||
|
if self.allow_blank:
|
||||||
|
options.insert(0, ('', self.blank_label))
|
||||||
|
|
||||||
|
return options
|
||||||
|
|
||||||
|
|
||||||
class RenderChoices(models.TextChoices, metaclass=RenderMeta): # type: ignore
|
class RenderChoices(models.TextChoices, metaclass=RenderMeta): # type: ignore
|
||||||
@@ -2385,6 +2392,12 @@ class ParameterTemplate(
|
|||||||
class Meta:
|
class Meta:
|
||||||
"""Metaclass options for the ParameterTemplate model."""
|
"""Metaclass options for the ParameterTemplate model."""
|
||||||
|
|
||||||
|
class ModelChoices(RenderChoices):
|
||||||
|
"""Model choices for parameter templates."""
|
||||||
|
|
||||||
|
allow_blank = True
|
||||||
|
choice_fnc = common.validators.parameter_model_options
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_api_url() -> str:
|
def get_api_url() -> str:
|
||||||
"""Return the API URL associated with the ParameterTemplate model."""
|
"""Return the API URL associated with the ParameterTemplate model."""
|
||||||
@@ -2467,6 +2480,15 @@ class ParameterTemplate(
|
|||||||
|
|
||||||
return [x.strip() for x in self.choices.split(',') if x.strip()]
|
return [x.strip() for x in self.choices.split(',') if x.strip()]
|
||||||
|
|
||||||
|
model_type = models.CharField(
|
||||||
|
max_length=100,
|
||||||
|
default='',
|
||||||
|
blank=True,
|
||||||
|
validators=[common.validators.validate_parameter_template_model_type],
|
||||||
|
verbose_name=_('Model type'),
|
||||||
|
help_text=_('Target model type for this parameter'),
|
||||||
|
)
|
||||||
|
|
||||||
name = models.CharField(
|
name = models.CharField(
|
||||||
max_length=100,
|
max_length=100,
|
||||||
verbose_name=_('Name'),
|
verbose_name=_('Name'),
|
||||||
@@ -2532,6 +2554,9 @@ class Parameter(
|
|||||||
class Meta:
|
class Meta:
|
||||||
"""Meta options for Parameter model."""
|
"""Meta options for Parameter model."""
|
||||||
|
|
||||||
|
# TODO: Make this non-abstract, actually implement...
|
||||||
|
abstract = True
|
||||||
|
|
||||||
verbose_name = _('Parameter')
|
verbose_name = _('Parameter')
|
||||||
verbose_name_plural = _('Parameters')
|
verbose_name_plural = _('Parameters')
|
||||||
unique_together = [['model_type', 'model_id', 'template']]
|
unique_together = [['model_type', 'model_id', 'template']]
|
||||||
|
|||||||
@@ -711,11 +711,31 @@ class ParameterTemplateSerializer(
|
|||||||
'name',
|
'name',
|
||||||
'units',
|
'units',
|
||||||
'description',
|
'description',
|
||||||
|
'model_type',
|
||||||
'checkbox',
|
'checkbox',
|
||||||
'choices',
|
'choices',
|
||||||
'selectionlist',
|
'selectionlist',
|
||||||
]
|
]
|
||||||
|
|
||||||
|
def __init__(self, *args, **kwargs):
|
||||||
|
"""Override the model_type field to provide dynamic choices."""
|
||||||
|
super().__init__(*args, **kwargs)
|
||||||
|
|
||||||
|
if len(self.fields['model_type'].choices) == 0:
|
||||||
|
self.fields[
|
||||||
|
'model_type'
|
||||||
|
].choices = common.validators.attachment_model_options()
|
||||||
|
|
||||||
|
# Note: The choices are overridden at run-time on class initialization
|
||||||
|
model_type = serializers.ChoiceField(
|
||||||
|
label=_('Model Type'),
|
||||||
|
default='',
|
||||||
|
choices=common.validators.attachment_model_options(),
|
||||||
|
required=False,
|
||||||
|
allow_blank=True,
|
||||||
|
allow_null=True,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class IconSerializer(serializers.Serializer):
|
class IconSerializer(serializers.Serializer):
|
||||||
"""Serializer for an icon."""
|
"""Serializer for an icon."""
|
||||||
|
|||||||
@@ -10,6 +10,47 @@ import common.icons
|
|||||||
from common.settings import get_global_setting
|
from common.settings import get_global_setting
|
||||||
|
|
||||||
|
|
||||||
|
def parameter_model_types():
|
||||||
|
"""Return a list of valid parameter model choices."""
|
||||||
|
import InvenTree.models
|
||||||
|
|
||||||
|
return list(
|
||||||
|
InvenTree.helpers_model.getModelsWithMixin(
|
||||||
|
InvenTree.models.InvenTreeParameterMixin
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def parameter_model_options():
|
||||||
|
"""Return a list of options for models which support parameters."""
|
||||||
|
return [
|
||||||
|
(model.__name__.lower(), model._meta.verbose_name)
|
||||||
|
for model in parameter_model_types()
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
def validate_parameter_model_type(value: str):
|
||||||
|
"""Ensure that the provided parameter model is valid."""
|
||||||
|
model_names = [el[0] for el in parameter_model_options()]
|
||||||
|
if value not in model_names:
|
||||||
|
raise ValidationError('Model type does not support parameters')
|
||||||
|
|
||||||
|
|
||||||
|
def validate_parameter_template_model_type(value: str):
|
||||||
|
"""Ensure that the provided model type is valid.
|
||||||
|
|
||||||
|
Note: A ParameterTemplate may have a blank model type.
|
||||||
|
"""
|
||||||
|
value = str(value).strip()
|
||||||
|
|
||||||
|
if not value:
|
||||||
|
# Empty values are allowed
|
||||||
|
return
|
||||||
|
|
||||||
|
# Pass any other value to the Parameter model type validator
|
||||||
|
validate_parameter_model_type(value)
|
||||||
|
|
||||||
|
|
||||||
def attachment_model_types():
|
def attachment_model_types():
|
||||||
"""Return a list of valid attachment model choices."""
|
"""Return a list of valid attachment model choices."""
|
||||||
import InvenTree.models
|
import InvenTree.models
|
||||||
|
|||||||
Reference in New Issue
Block a user