2
0
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:
Oliver Walters
2025-11-10 10:29:24 +00:00
parent ed8b7732ea
commit 06688d8a56
5 changed files with 108 additions and 2 deletions

View File

@@ -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(

View File

@@ -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"),
] ]

View File

@@ -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']]

View File

@@ -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."""

View File

@@ -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