Skip to content

Commit c4b209f

Browse files
committed
Refactor: Update model option retrieval methods in validators and serializers for consistency
1 parent 2de4988 commit c4b209f

File tree

5 files changed

+63
-27
lines changed

5 files changed

+63
-27
lines changed

src/backend/InvenTree/common/admin.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ class AttachmentAdmin(admin.ModelAdmin):
1515
def formfield_for_dbfield(self, db_field, request, **kwargs):
1616
"""Provide custom choices for 'model_type' field."""
1717
if db_field.name == 'model_type':
18-
db_field.choices = common.validators.get_model_options(
18+
db_field.choices = common.validators.get_model_options_for_mixin(
1919
InvenTree.models.InvenTreeAttachmentMixin
2020
)
2121

src/backend/InvenTree/common/api.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -734,13 +734,13 @@ def validate_delete(self, queryset, request) -> None:
734734
- Extract all model types from the provided queryset
735735
- Ensure that the user has correct 'delete' permissions for each model
736736
"""
737-
from common.validators import get_model_class_from_label
737+
from common.validators import resolve_model_from_label
738738
from users.permissions import check_user_permission
739739

740740
model_types = queryset.values_list('model_type', flat=True).distinct()
741741

742742
for model_type in model_types:
743-
if model_class := get_model_class_from_label(
743+
if model_class := resolve_model_from_label(
744744
model_type, InvenTreeAttachmentMixin
745745
):
746746
if not check_user_permission(request.user, model_class, 'delete'):

src/backend/InvenTree/common/models.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2186,7 +2186,7 @@ def check_permission(self, permission, user):
21862186
"""Check if the user has the required permission for this attachment."""
21872187
from InvenTree.models import InvenTreeAttachmentMixin
21882188

2189-
model_class = common.validators.get_model_class_from_label(
2189+
model_class = common.validators.resolve_model_from_label(
21902190
self.model_type, InvenTreeAttachmentMixin
21912191
)
21922192

src/backend/InvenTree/common/serializers.py

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -616,9 +616,6 @@ class Meta:
616616
result = serializers.CharField()
617617

618618

619-
ALLOWED_IMAGE_CTS = common.validators.get_model_options(InvenTreeImageMixin)
620-
621-
622619
class InvenTreeImageSerializer(
623620
InvenTree.serializers.RemoteImageMixin, InvenTreeModelSerializer
624621
):
@@ -664,8 +661,10 @@ class Meta:
664661
def __init__(self, *args, **kwargs):
665662
"""Initialize the serializer and set allowed content types."""
666663
super().__init__(*args, **kwargs)
667-
# inject your allowed list
668-
self.fields['content_type'].choices = ALLOWED_IMAGE_CTS
664+
665+
self.fields[
666+
'content_type'
667+
].choices = common.validators.get_model_options_for_mixin(InvenTreeImageMixin)
669668

670669
def validate_content_type(self, ct_value):
671670
"""Turn the incoming model-name string into a real ContentType instance."""
@@ -834,7 +833,9 @@ def __init__(self, *args, **kwargs):
834833
super().__init__(*args, **kwargs)
835834

836835
if len(self.fields['model_type'].choices) == 0:
837-
self.fields['model_type'].choices = common.validators.get_model_options(
836+
self.fields[
837+
'model_type'
838+
].choices = common.validators.get_model_options_for_mixin(
838839
InvenTreeAttachmentMixin
839840
)
840841

@@ -854,7 +855,7 @@ def __init__(self, *args, **kwargs):
854855
# Note: The choices are overridden at run-time on class initialization
855856
model_type = serializers.ChoiceField(
856857
label=_('Model Type'),
857-
choices=common.validators.get_model_options(InvenTreeAttachmentMixin),
858+
choices=common.validators.get_model_options_for_mixin(InvenTreeAttachmentMixin),
858859
required=True,
859860
allow_blank=False,
860861
allow_null=False,
@@ -873,7 +874,7 @@ def save(self, **kwargs):
873874
# Ensure that the user has permission to attach files to the specified model
874875
user = self.context.get('request').user
875876

876-
target_model_class = common.validators.get_model_class_from_label(
877+
target_model_class = common.validators.resolve_model_from_label(
877878
model_type, InvenTreeAttachmentMixin
878879
)
879880

src/backend/InvenTree/common/validators.py

Lines changed: 50 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -4,41 +4,75 @@
44
from typing import Union
55

66
from django.core.exceptions import ValidationError
7+
from django.db.models import Model
78
from django.utils.translation import gettext_lazy as _
89

910
import common.icons
1011
from common.settings import get_global_setting
1112

1213

13-
def get_model_types(mixin_class):
14-
"""Return a list of valid attachment model choices."""
14+
def get_mixin_models(mixin_class: type[Model]):
15+
"""Return a list of Django model classes that inherit from the given mixin.
16+
17+
This function lazily imports the helper to avoid circular imports.
18+
19+
Args:
20+
mixin_class: The mixin class to search for.
21+
22+
Returns:
23+
A list of Django model classes that subclass the provided mixin.
24+
"""
25+
# Lazy import to prevent circular dependency issues
1526
import InvenTree.helpers_model
1627

1728
return list(InvenTree.helpers_model.getModelsWithMixin(mixin_class))
1829

1930

20-
def get_model_options(mixin_class):
21-
"""Return a list of options for models."""
22-
return [
23-
(model.__name__.lower(), model._meta.verbose_name)
24-
for model in get_model_types(mixin_class)
25-
]
31+
def get_model_options_for_mixin(mixin_class: type[Model]):
32+
"""Build choice options for all models that inherit from a given mixin.
33+
34+
Each option is a tuple of:
35+
- key: the lowercase model class name (e.g. 'part', 'stockitem')
36+
- label: the model's verbose name for display
37+
38+
Args:
39+
mixin_class: The mixin class used to filter models.
40+
41+
Returns:
42+
A list of (key, label) tuples suitable for form field choices.
43+
"""
44+
options = []
45+
for model in get_mixin_models(mixin_class):
46+
key = model.__name__.lower()
47+
label = str(model._meta.verbose_name)
48+
options.append((key, label))
49+
return options
2650

2751

2852
def attachment_model_options():
2953
"""Return a list of valid attachment model choices."""
3054
import InvenTree.models
3155

32-
return get_model_options(InvenTree.models.InvenTreeAttachmentMixin)
56+
return get_model_options_for_mixin(InvenTree.models.InvenTreeAttachmentMixin)
57+
3358

59+
def resolve_model_from_label(label: str, mixin_class: type[Model]):
60+
"""Resolve a model class by a case-insensitive label among models inheriting a mixin.
3461
35-
def get_model_class_from_label(label: str, mixin_class):
36-
"""Returns the model class matching the given label from a set of models inheriting a specific mixin."""
62+
Args:
63+
label: The identifier string to match (typically the model class name in lowercase).
64+
mixin_class: The mixin class that candidate models must inherit from.
65+
66+
Returns:
67+
The Django model class that matches the provided label.
68+
"""
3769
if not label:
3870
raise ValidationError(_('No attachment model type provided'))
3971

40-
for model in get_model_types(mixin_class):
41-
if model.__name__.lower() == label.lower():
72+
label_lc = label.lower()
73+
74+
for model in get_mixin_models(mixin_class):
75+
if model.__name__.lower() == label_lc:
4276
return model
4377

4478
raise ValidationError(_('Invalid attachment model type') + f": '{label}'")
@@ -49,7 +83,8 @@ def validate_attachment_model_type(value):
4983
import InvenTree.models
5084

5185
model_names = [
52-
el[0] for el in get_model_options(InvenTree.models.InvenTreeAttachmentMixin)
86+
el[0]
87+
for el in get_model_options_for_mixin(InvenTree.models.InvenTreeAttachmentMixin)
5388
]
5489
if value not in model_names:
5590
raise ValidationError('Model type does not support attachments')
@@ -82,7 +117,7 @@ def limit_image_content_types():
82117
import InvenTree.models
83118

84119
allowed_models = [
85-
m[0] for m in get_model_options(InvenTree.models.InvenTreeImageMixin)
120+
m[0] for m in get_model_options_for_mixin(InvenTree.models.InvenTreeImageMixin)
86121
]
87122

88123
return {'model__in': allowed_models}

0 commit comments

Comments
 (0)