Source code for ferrosoft.apps.ferrobase.forms.fields
# Copyright (c) 2025 Ferrosoft GmbH. All rights reserved.
from itertools import groupby
from uuid import UUID
from django import forms
from django.utils.choices import BaseChoiceIterator
from ferrosoft.apps.ferrobase.models import (
UnitCursor,
UnitSystem,
)
from ferrosoft.apps.ferrobase.models.registry import ModelRegistry
[docs]
class UnitChoiceIterator(BaseChoiceIterator):
"""Choice iterator for measurement units (reserved for future use)."""
[docs]
class UnitChoiceField(forms.ChoiceField):
"""Choice field that presents measurement units grouped by unit system.
Units are looked up from ``ModelRegistry.Unit`` and rendered as an
``<optgroup>`` per ``UnitSystem`` (Metric, US Customary, Imperial).
The field converts a submitted UUID string back to a ``MeasurementUnit``
instance via ``to_python``.
Args:
units: Iterable of ``UnitCursor`` objects defining the selectable units.
initial: Optional ``UnitCursor`` whose matching database unit is
pre-selected.
"""
def __init__(self, *args, **kwargs):
self.registry = ModelRegistry.Unit
units = kwargs.pop("units")
choices = [
(
UnitSystem(system).label,
[
(str(unit.id), str(unit))
for unit in sorted(units, key=lambda unit: unit.symbol)
],
)
for system, units in groupby(
self.registry.find(units), key=lambda unit: unit.system
)
]
kwargs.update(
{
"choices": choices,
"initial": self._get_initial(kwargs.pop("initial", None)),
}
)
super().__init__(*args, **kwargs)
[docs]
def to_python(self, value):
"""Convert a UUID string from the form submission to a ``MeasurementUnit``.
Args:
value: UUID string identifying the selected unit.
Returns:
MeasurementUnit: The corresponding unit instance from the registry.
"""
return self.registry.get(UUID(value))
[docs]
def valid_value(self, value):
"""Check that the submitted value identifies a known unit.
Args:
value: UUID string to validate.
Returns:
bool: ``True`` if the registry contains a unit with this UUID.
"""
return self.registry.exists(value)
def _get_initial(self, cursor: UnitCursor | None):
def find():
if cursor is None:
return None
unit = self.registry.get(cursor)
if unit is None:
return None
return str(unit.id)
return find