Measurement API¶
general_manager.measurement.measurement.Measurement ¶
Decimal-backed measurement value with Pint unit conversion and arithmetic.
A Measurement stores a magnitude and unit, exposes the underlying Pint quantity for advanced integrations, and supports compatible arithmetic, comparison, string parsing, pickling, and explicit currency conversion. Currency conversions never use implicit rates; callers must pass an exchange_rate when converting between different configured currencies. Pint canonicalizes most unit names, so unit and string/repr output may use canonical names such as "kilogram" rather than the caller's spelling. GeneralManager keeps the public "count" spelling for discrete piece-count measurements even though Pint represents that base unit internally as "piece". Exact Pint exception subclasses/messages, canonical compound-unit ordering, aliases, and offset-unit internals are delegated to the installed Pint version and are not a stable GeneralManager API contract.
quantity property ¶
quantity
Access the underlying pint quantity for advanced operations.
Magnitudes are usually Decimal-backed. Offset units such as absolute temperatures may be float-backed because Pint performs those conversions with non-multiplicative offsets.
Returns:
| Name | Type | Description |
|---|---|---|
PlainQuantity | MeasurementQuantity | Pint quantity representing the measurement value and unit. |
magnitude property ¶
magnitude
Fetch the numeric component of the measurement.
This property always returns a Decimal by converting the underlying Pint magnitude with Decimal(str(quantity.magnitude)). For float-backed offset quantities, that preserves Pint's string representation rather than the original binary float.
Returns:
| Name | Type | Description |
|---|---|---|
Decimal | Decimal | Magnitude of the measurement in its current unit. |
unit property ¶
unit
Retrieve the unit label associated with the measurement.
The returned value is GeneralManager's public unit string, usually Pint's canonical unit string and not necessarily the spelling passed to the constructor. Empty-string and "dimensionless" inputs both expose "dimensionless". Discrete piece-count inputs such as "count" and "piece" expose "count" while Pint's internal quantity uses "piece". The Pint "pc" alias remains parsec, not piece count.
Returns:
| Name | Type | Description |
|---|---|---|
str | str | Public unit string used by GeneralManager. |
__init__ ¶
__init__(value, unit)
Create a Measurement from a numeric value and a unit label.
Converts the provided numeric-like value to a Decimal through Decimal(str(value)) and constructs the internal quantity using the given Pint unit expression. unit may be "dimensionless" or an empty string for dimensionless measurements. Discrete item quantities should use "count", which belongs to its own unit family rather than Pint's plain dimensionless family. Invalid units are reported by Pint and are not wrapped by this constructor. String values are numeric magnitudes only; use from_string() for combined "<value> <unit>" text. bool is rejected even though it is an int subclass in Python. Invalid numeric strings, including combined measurement text such as "1 kg" passed directly as value, raise InvalidMeasurementInitializationError. Decimal special values such as NaN and infinities are accepted or rejected according to Decimal and Pint quantity construction; later operations inherit that delegated behavior.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
value | Decimal | float | int | str | Numeric value to use as the measurement magnitude; strings and numeric types are coerced to Decimal. | required |
unit | str | Unit label registered in the module's unit registry, including configured currency codes, physical unit names, | required |
Raises:
| Type | Description |
|---|---|
InvalidMeasurementInitializationError | If |
PintError | If |
__set_quantity ¶
__set_quantity(quantity, unit=None)
Store quantity and cached public scalar values for internal reads.
__getstate__ ¶
__getstate__()
Produce a serialisable representation of the measurement.
Returns:
| Type | Description |
|---|---|
dict[str, str] | dict[str, str]: Mapping with |
__setstate__ ¶
__setstate__(state)
Recreate the internal quantity from a serialized representation.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
state | dict[str, str] | Serialized state containing | required |
Returns:
| Type | Description |
|---|---|
None | None |
from_string classmethod ¶
from_string(value)
Parse a textual representation into a Measurement.
Leading and trailing whitespace is ignored. A single token is parsed as a dimensionless Decimal value. Two-token input is split once on whitespace; the first token must be a Decimal-compatible magnitude and the remainder is passed to Pint as the unit expression, so spaced compound units such as "1 g / cm^3" are supported. Decimal parsing accepts the syntax accepted by Decimal(...); currency symbols such as $ are not accepted unless Pint knows them as unit text.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
value | str | A string in the form " | required |
Returns:
| Name | Type | Description |
|---|---|---|
Measurement | Measurement | Measurement constructed from the parsed magnitude and unit. |
Raises:
| Type | Description |
|---|---|
InvalidDimensionlessValueError | If a single-token input cannot be parsed as a number. |
InvalidMeasurementStringError | If the string does not contain a valid numeric magnitude followed by a parseable unit expression. |
Notes
Empty strings raise InvalidMeasurementStringError. Single-token inputs with invalid Decimal syntax raise InvalidDimensionlessValueError. Inputs containing whitespace are treated as magnitude-plus-unit text; invalid magnitudes or invalid Pint unit expressions are reported as InvalidMeasurementStringError.
format_decimal staticmethod ¶
format_decimal(value)
Normalise decimals for measurement storage and display.
This public helper exposes the same Decimal normalization used by measurement magnitude access and string formatting.
The value is passed through Decimal.normalize(). If the normalized value is integral, it is quantized to Decimal("1") so values such as Decimal("2.0") display and compare as Decimal("2"). Non-integral values keep their significant fractional digits; no rounding is applied. Special Decimal values such as NaN and infinities are delegated to Decimal's own normalize() and to_integral_value() behavior; if integral quantization raises InvalidOperation, the normalized value is returned unchanged. Signed zero follows Decimal normalization. The exact string form of Decimal special values is the standard-library Decimal result, not a separate GeneralManager guarantee.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
value | Decimal | Decimal value that should be normalised. | required |
Returns:
| Name | Type | Description |
|---|---|---|
Decimal | Decimal | Normalised decimal with insignificant trailing zeros removed. |
to ¶
to(target_unit, exchange_rate=None)
Convert this measurement to the specified target unit, handling currency conversions when applicable.
Physical and same-currency conversions are delegated to Pint. For different currencies, exchange_rate means target units per one source unit: Measurement(100, "EUR").to("USD", exchange_rate=1.1) returns 110 USD. Compound currency expressions keep the same rule for the currency component and still convert compatible physical components. Currency-to-currency conversion is only special-cased when each unit expression contains exactly one configured currency component; expressions with zero, multiple, inverse, or mismatched-power currency components fall back to Pint conversion and may raise Pint errors. When target_unit matches the current canonical unit exactly, or _canonical_unit_string(target_unit) canonicalizes to the current unit, to() returns this Measurement object unchanged instead of allocating a converted copy. Components with explicit power one, such as EUR ** 1, are treated as the same currency component; expressions such as EUR / EUR simplify before this check and therefore are not currency conversions. Fractional currency powers, such as EUR ** 0.5, are not handled by the exchange-rate shortcut and fall back to Pint conversion. Equivalent simplification beyond these rules follows Pint's parsed unit container; GeneralManager classifies the expression after Pint simplification and only inspects the resulting configured currency components. For example, if Pint simplifies EUR * meter / meter to a pure EUR unit, GeneralManager treats it as one currency component. GeneralManager does not guarantee exact Pint exception subclasses or messages for invalid units, incompatible conversions, or malformed target expressions.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
target_unit | str | Unit label or currency code to convert the measurement into. | required |
exchange_rate | float | None | Exchange rate to use when converting between different currencies; ignored for same-currency conversions and physical-unit conversions. | None |
Returns:
| Name | Type | Description |
|---|---|---|
Measurement | Measurement | The measurement expressed in the target unit. |
Raises:
| Type | Description |
|---|---|
MissingExchangeRateError | If converting between two different currencies without providing an exchange rate. |
PintError | If |
is_currency ¶
is_currency()
Determine whether the measurement's unit represents a configured currency.
Currency matching is case-sensitive and checks the canonical unit string against the module-level currency_units list. Compound units such as "EUR / kilogram" are not considered pure currencies by this helper. The configured codes are EUR, USD, GBP, JPY, CHF, AUD, and CAD; aliases are not registered. Percent and dimensionless units are treated as ordinary Pint units. The registry defines each currency as its own dimension named after the currency code, for example EUR = [EUR].
Returns:
| Name | Type | Description |
|---|---|---|
bool | bool | True if the unit matches one of the registered currency codes. |
__add__ ¶
__add__(other)
Return the sum of this Measurement and another Measurement while enforcing currency and dimensional rules.
If both operands are currency units their currency codes must match. If both are physical units their dimensionalities must match. Mixing currency and physical units is not permitted.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
other | Measurement | The addend measurement. | required |
Returns:
| Name | Type | Description |
|---|---|---|
Measurement | Measurement | A new Measurement representing the sum. |
Raises:
| Type | Description |
|---|---|
MeasurementOperandTypeError | If |
CurrencyMismatchError | If both operands are currencies with different currency codes. |
IncompatibleUnitsError | If both operands are physical units but have different dimensionalities or the result cannot be represented as a pint.Quantity. |
MixedUnitOperationError | If one operand is a currency and the other is a physical unit. |
__sub__ ¶
__sub__(other)
Subtract another Measurement from this one, enforcing currency and unit compatibility.
Performs subtraction for two currency Measurements only when they share the same currency code, or for two physical Measurements only when they have the same dimensionality; mixing currency and physical units is disallowed.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
other | Measurement | The measurement to subtract from this measurement. | required |
Returns:
| Name | Type | Description |
|---|---|---|
Measurement | Measurement | A new Measurement representing the difference. |
Raises:
| Type | Description |
|---|---|
MeasurementOperandTypeError | If |
CurrencyMismatchError | If both operands are currencies but use different currency codes. |
IncompatibleUnitsError | If both operands are physical units but have incompatible dimensionality. |
MixedUnitOperationError | If one operand is a currency and the other is a physical unit. |
__mul__ ¶
__mul__(other)
Multiply this measurement by another measurement or by a numeric scalar.
Multiplication combines units through Pint. Two pure currency measurements are rejected because the result would be a currency-squared amount. Currency multiplied by a non-currency measurement, including percent or dimensionless measurements, is allowed and produces the corresponding compound or simplified unit. Pint treats percent as 0.01 dimensionless during conversion, but multiplication preserves the compound unit until callers convert it, e.g. 100 EUR * 20 percent renders as 2000 EUR * percent and converts to 20 EUR.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
other | Measurement | Decimal | float | int | The multiplier. When a Measurement is provided, units are combined according to unit algebra; when a numeric scalar is provided, the magnitude is scaled and the unit is preserved. Bool is not accepted as a scalar. | required |
Returns:
| Name | Type | Description |
|---|---|---|
Measurement | Measurement | The product as a Measurement with the resulting magnitude and unit. |
Raises:
| Type | Description |
|---|---|
CurrencyScalarOperationError | If both operands are currency measurements (multiplying two currencies is not allowed). |
MeasurementScalarTypeError | If |
__truediv__ ¶
__truediv__(other)
Divide this measurement by another measurement or by a numeric scalar.
Division combines units through Pint. Dividing two measurements with the same currency produces a ratio with Pint's derived units, normally dimensionless for pure currency values. Dividing different currencies is rejected unless callers explicitly convert first with to().
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
other | Measurement | Decimal | float | int | The divisor; when a Measurement, must be compatible (currencies require same unit). Bool is not accepted as a scalar. | required |
Returns:
| Name | Type | Description |
|---|---|---|
Measurement | Measurement | The quotient as a new Measurement. If |
Raises:
| Type | Description |
|---|---|
CurrencyMismatchError | If both operands are currencies with different units. |
MeasurementScalarTypeError | If |
ZeroDivisionError | If dividing by a zero scalar or zero-magnitude measurement. |
__str__ ¶
__str__()
Return a human-readable string of the measurement, including its unit when not dimensionless.
The magnitude is read through the magnitude property, so output uses the same Decimal normalization as other public magnitude access.
Returns:
| Type | Description |
|---|---|
str | A string formatted as " |
__repr__ ¶
__repr__()
Return a detailed representation suitable for debugging.
The magnitude is read through the magnitude property, and the unit is GeneralManager's public unit string. The GeneralManager-owned shape is Measurement(<magnitude>, '<unit>'); exact magnitude and compound-unit spelling follow the documented Decimal/Pint boundaries.
Returns:
| Name | Type | Description |
|---|---|---|
str | str | Debug-friendly notation including magnitude and unit. |
__radd__ ¶
__radd__(other)
Allow right-side addition so sum() treats 0 as the neutral element.
Nonzero left operands delegate to __add__ and therefore raise MeasurementOperandTypeError unless they are Measurement instances.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
other | object | Left operand supplied by Python's arithmetic machinery; typically numeric zero when used with sum(). Bool is not treated as zero. | required |
Returns:
| Name | Type | Description |
|---|---|---|
Measurement | Measurement |
|
__rsub__ ¶
__rsub__(other)
Support right-side subtraction.
Numeric zero returns the negated measurement. Nonzero non-Measurement left operands raise MeasurementOperandTypeError.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
other | object | Left operand supplied by Python's arithmetic machinery; numeric zero produces a negated measurement. Bool is not treated as zero. | required |
Returns:
| Name | Type | Description |
|---|---|---|
Measurement | Measurement | Result of subtracting |
Raises:
| Type | Description |
|---|---|
MeasurementOperandTypeError | If |
__rmul__ ¶
__rmul__(other)
Support right-side multiplication.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
other | object | Left operand supplied by Python's arithmetic machinery. | required |
Returns:
| Name | Type | Description |
|---|---|---|
Measurement | Measurement | Result of multiplying |
__rtruediv__ ¶
__rtruediv__(other)
Support right-side division.
Reverse division follows Python's reflected arithmetic boundary: unsupported left operands raise MeasurementOperandTypeError. Forward division by unsupported non-measurement scalar-like operands raises MeasurementScalarTypeError instead. Bool is not accepted as a scalar. When the left operand is a Measurement, the method returns other.__truediv__(self) so all forward division currency, unit, and zero-divisor rules apply with the operands reversed.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
other | object | Left operand supplied by Python's arithmetic machinery. | required |
Returns:
| Name | Type | Description |
|---|---|---|
Measurement | Measurement | Result of dividing |
Raises:
| Type | Description |
|---|---|
MeasurementOperandTypeError | If |
ZeroDivisionError | If this measurement has zero magnitude. |
__eq__ ¶
__eq__(other)
Return whether this measurement equals another compatible measurement.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
other | object |
| required |
Returns:
| Type | Description |
|---|---|
bool |
|
bool | null-like values handled by |
Raises:
| Type | Description |
|---|---|
UnsupportedComparisonError | If |
IncomparableMeasurementError | If units are incompatible. |
__ne__ ¶
__ne__(other)
Return whether this measurement differs from another compatible measurement.
This uses the same comparison path as __eq__ with the != operation; it is not implemented as not __eq__(other). The documented null-like values therefore return False rather than the inverse of equality.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
other | object |
| required |
Returns:
| Type | Description |
|---|---|
bool |
|
bool | null-like values handled by |
bool | than |
Raises:
| Type | Description |
|---|---|
UnsupportedComparisonError | If |
IncomparableMeasurementError | If units are incompatible. |
__lt__ ¶
__lt__(other)
Return whether this measurement is less than another compatible measurement.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
other | object |
| required |
Returns:
| Type | Description |
|---|---|
bool |
|
Raises:
| Type | Description |
|---|---|
UnsupportedComparisonError | If |
IncomparableMeasurementError | If units are incompatible. |
__le__ ¶
__le__(other)
Return whether this measurement is less than or equal to another measurement.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
other | object |
| required |
Returns:
| Type | Description |
|---|---|
bool |
|
Raises:
| Type | Description |
|---|---|
UnsupportedComparisonError | If |
IncomparableMeasurementError | If units are incompatible. |
__gt__ ¶
__gt__(other)
Return whether this measurement is greater than another compatible measurement.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
other | object |
| required |
Returns:
| Type | Description |
|---|---|
bool |
|
Raises:
| Type | Description |
|---|---|
UnsupportedComparisonError | If |
IncomparableMeasurementError | If units are incompatible. |
__ge__ ¶
__ge__(other)
Check whether the measurement is greater than or equal to another value.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
other | object | Measurement or compatible representation used in the comparison. | required |
Returns:
| Name | Type | Description |
|---|---|---|
bool | bool | True when the measurement is greater than or equal to |
Raises:
| Type | Description |
|---|---|
TypeError | If |
ValueError | If units are incompatible. |
__hash__ ¶
__hash__()
Compute a hash using the measurement's canonical base magnitude and unit.
Hashing mirrors equality by converting to base units first. The base magnitude is normalized to a Decimal and quantized to 1e-9, matching offset-unit equality. Hash collisions remain possible, as with any Python hash. Non-offset equality uses exact Decimal comparison after conversion; quantization may introduce extra hash collisions but does not make unequal measurements compare equal. "Equivalent" means measurements for which the comparison methods report equality; additional Pint simplifications only matter when they affect that equality result. For Decimal special values and Pint base-unit conversion failures, hashing follows the same Decimal/Pint behavior described above.
Returns:
| Name | Type | Description |
|---|---|---|
int | int | Stable hash suitable for use in dictionaries and sets. Measurements |
int | that compare equal after unit conversion produce the same hash. |
general_manager.measurement.measurement_field.MeasurementField ¶
Bases: MeasurementFieldBase
__init__ ¶
__init__(
base_unit,
*args,
null=False,
blank=False,
editable=True,
unique=False,
**kwargs
)
Create a MeasurementField configured with a canonical base unit and paired backing columns.
Initializes the field's canonical base unit and derived dimensionality, records the editable flag, constructs a Decimal-backed value column (<name>_value) and Char-backed unit column (<name>_unit), and forwards remaining arguments to the base Field constructor. Stored magnitudes are converted to base_unit and rounded to the backing DecimalField(max_digits=30, decimal_places=10) precision; the unit column stores the Measurement's public unit spelling, such as gram for g and count for discrete item counts.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
base_unit | str | Multiplicative Pint unit used to normalize and store measurements. Currency units must be one of | required |
*args | object | Positional arguments forwarded to the base Field implementation. | () |
null | bool | If True, the backing columns may be NULL in the database. | False |
blank | bool | If True, forms may accept an empty value for this field. | False |
editable | bool | If False, assignments through the model API will be rejected. | True |
unique | bool | If True, the backing value column is created with a unique constraint. Equivalent values in different units share the same stored base magnitude and therefore conflict. | False |
**kwargs | object | Additional keyword arguments forwarded to the base Field implementation. | {} |
Raises:
| Type | Description |
|---|---|
InvalidMeasurementFieldBaseUnitError | If |
PintError | If Pint cannot parse or dimensionally evaluate |
contribute_to_class ¶
contribute_to_class(
cls, name, private_only=False, **kwargs
)
Attach the measurement field and its backing value and unit fields to the model and install the descriptor.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
cls | type[Model] | Model class receiving the field. | required |
name | str | Attribute name to use on the model for this field. | required |
private_only | bool | Whether the field should be treated as private. | False |
kwargs | object | Additional options forwarded to the base implementation. | {} |
get_col ¶
get_col(alias, output_field=None)
Return a Col expression that references this field's backing value column for ORM queries.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
alias | str | Table alias to use for the column reference. | required |
output_field | Field | None | Optional field to use as the expression's output type; defaults to the backing value field. | None |
Returns:
| Name | Type | Description |
|---|---|---|
Col | Col | Column expression targeting the numeric backing value field. |
get_lookup ¶
get_lookup(lookup_name)
Retrieve a lookup class from the underlying decimal field.
Filtering uses the backing value column. Lookup right-hand-side values that pass through this field may be Measurement instances or measurement strings and are prepared with get_prep_value(). Bare numeric values are interpreted by Django's decimal lookup machinery as already being in the stored base unit when the delegated DecimalField lookup handles the RHS directly; bare numbers are not accepted by direct get_prep_value() calls and are not valid descriptor-assignment values.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
lookup_name | str | Name of the lookup to resolve. | required |
Returns:
| Type | Description |
|---|---|
type[Lookup[object]] | type[models.Lookup]: Lookup class implementing the requested comparison. |
get_transform ¶
get_transform(lookup_name)
Return a transform callable provided by the underlying decimal field.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
lookup_name | str | Name of the transform to resolve. | required |
Returns:
| Type | Description |
|---|---|
type[Transform] | None | models.Transform | None: Transform class when available; otherwise None. |
deconstruct ¶
deconstruct()
Return serialization details so migrations can reconstruct the field.
Ensures the required base_unit argument is preserved alongside the standard Django field options emitted by the base implementation.
db_type ¶
db_type(connection)
Signal to Django that the field does not map to a single column.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
connection | BaseDatabaseWrapper | Database connection used for schema generation. | required |
Returns:
| Type | Description |
|---|---|
None | None |
run_validators ¶
run_validators(value)
Execute all configured validators when a measurement is provided.
Validators receive the Measurement object supplied to clean(). This hook does not convert that object to base_unit; assignment and preparation normalize the persisted backing value separately. Empty values skip validators, matching Django's field-validation convention. Non-empty values must be Measurement instances; direct calls with non-empty strings, non-empty lists, non-empty dictionaries, or other objects raise ValidationError with "Value must be a Measurement instance or None.". run_validators() itself does not check blank; direct calls with "", [], (), or {} skip validators regardless of the field's blank setting. clean() calls validate() first when blank enforcement is needed.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
value | MeasurementValidationValue | Measurement instance that should satisfy field validators, or an empty value. | required |
Returns:
| Type | Description |
|---|---|
None | None |
clean ¶
clean(value, model_instance=None)
Validate a measurement value before it is saved to the model.
This hook expects an already-normalized Python value. It does not parse strings; string parsing happens during descriptor assignment and value preparation. Empty literals are valid clean() inputs for Django's blank-validation path and are returned unchanged when blank=True; for example, clean(""), clean([]), clean(()), and clean({}) return the same object/value when blank values are allowed. clean(None) raises Django's standard null ValidationError when null=False and returns None when null=True. Direct calls such as clean("100 g") do not parse strings and raise ValidationError; assign strings through the descriptor or prepare them with get_prep_value(). For Measurement values, clean() runs validators exactly once with the same Measurement object and then returns that object.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
value | MeasurementValidationValue | Measurement provided by forms or assignment, or an empty literal handled by blank validation. | required |
model_instance | Model | None | Instance associated with the field, when available. | None |
Returns:
| Name | Type | Description |
|---|---|---|
MeasurementValidationValue | MeasurementValidationValue | The original validated value. |
Raises:
| Type | Description |
|---|---|
ValidationError | If validation fails due to null/blank constraints, non-empty non-Measurement values, or validator errors. Non-empty non-Measurement values use "Value must be a Measurement instance or None.". |
to_python ¶
to_python(value)
Return the value Django already supplied for this virtual field.
Measurement reconstruction happens in the descriptor from the paired <name>_value and <name>_unit columns. Django does not pass those two columns through this single-field hook, so this method preserves Measurement, string, list, tuple, dict, and None values unchanged. This is intentionally a validation passthrough, not a string-coercion hook; to_python("1 meter") returns "1 meter" rather than parsing it. Django's clean() path in this class does not call to_python() for blank literals; it handles them directly in validate(). Direct calls with unsupported runtime objects outside MeasurementValidationValue are outside the typed public contract and are returned unchanged only by Python's dynamic dispatch.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
value | MeasurementValidationValue | Value provided by Django. | required |
Returns:
| Name | Type | Description |
|---|---|---|
MeasurementValidationValue | MeasurementValidationValue | The original value without modification. |
get_prep_value ¶
get_prep_value(value)
Serialise a measurement for storage by converting it to the base unit magnitude.
Strings use Measurement.from_string, so accepted text follows that parser's grammar: a Python Decimal numeric token, optionally followed by a Pint unit expression. Signed decimals and exponent notation are accepted by Decimal; thousands separators are not. Leading and trailing whitespace is ignored by Python's whitespace split, but whitespace-only strings are invalid. Numeric strings without a unit create dimensionless measurements, so they are compatible only with dimensionless base_unit values.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
value | Measurement | str | None | Value provided by the model or form. | required |
Returns:
| Type | Description |
|---|---|
Decimal | None | Decimal | None: Decimal magnitude in the base unit quantized to the |
Decimal | None | backing field's ten decimal places, or None when no value is |
Decimal | None | supplied. Float-origin magnitudes follow the |
Decimal | None | Decimal conversion. |
Raises:
| Type | Description |
|---|---|
ValidationError | If the value cannot be parsed as a measurement, is not a Measurement/string/None, or uses a unit incompatible with |
PintError | Only non-ValueError Pint exceptions that escape |
__get__ ¶
__get__(instance, owner=None)
Resolve the field value on an instance, reconstructing the measurement when possible.
The descriptor reads <name>_value as a base-unit magnitude and <name>_unit as the Measurement public spelling for the unit originally assigned. It converts the base magnitude back to that stored unit for display. If stored data has drifted and the unit is no longer parseable or dimensionally compatible with the base unit, reconstruction falls back to a Measurement in base_unit rather than raising during attribute access. The fallback affects only the returned Measurement and does not mutate the backing columns. During fallback the stored <name>_value is treated as already being in base_unit. If the stored value column itself cannot be converted with Decimal(str(value)), that Decimal conversion error propagates; only stored unit failures use the fallback.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
instance | Model | None | Model instance owning the field, or None when accessed on the class. | required |
owner | type[Model] | None | Model class owning the descriptor. | None |
Returns:
| Type | Description |
|---|---|
MeasurementField | Measurement | None | MeasurementField | Measurement | None: Descriptor when accessed on the class, reconstructed measurement for instances, or None when either backing column is None. |
__set__ ¶
__set__(instance, value)
Set a measurement on a model instance after validating editability, type, and unit compatibility.
None clears both backing columns at assignment time, even when null=False; Django validation rejects that cleared value later if the field is not nullable. Saving without model validation relies on the database schema for the non-null backing columns. Non-empty strings are parsed with Measurement.from_string; an empty string is a parse error rather than a clear operation. For currency base units, assigned values must use a currency from the exported fixed currency_units set (EUR, USD, GBP, JPY, CHF, AUD, or CAD) and must match the specific base currency's Pint dimension. Currency aliases such as dollar are not part of this public field contract unless they are explicitly added to currency_units. The field does not accept an exchange-rate argument, so cross-currency assignments such as EUR into a USD field are rejected with ValidationError as incompatible. For non-currency base units, currency measurements are rejected.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
instance | Model | Model instance receiving the value. | required |
value | Measurement | str | None | A Measurement, a string parseable to a Measurement, or None to clear the field. | required |
Raises:
| Type | Description |
|---|---|
MeasurementFieldNotEditableError | If the field was declared with |
ValidationError | If the value is not a Measurement, valid parseable string, or None; if currency unit rules are violated; or if the unit is incompatible with the field's base unit. Invalid strings and wrong types use "Value must be a Measurement instance or None."; incompatible units use "Unit must be compatible with ' |
PintError | Only non-ValueError Pint exceptions that escape |
validate ¶
validate(value, model_instance=None)
Enforce null/blank constraints and run validators on the provided value.
This method is the Django validation hook for already-normalized Python values. Assignment and get_prep_value handle string parsing and unit compatibility; this method handles null, blank, and non-empty runtime type checks. Custom validators run through run_validators(), including when clean() calls it after this method. Empty literals ("", [], (), {}) are accepted only when blank=True and otherwise raise Django's standard blank ValidationError. Non-empty strings, lists, dictionaries, tuples, and other non-Measurement values raise ValidationError with "Value must be a Measurement instance or None.". Descriptor assignment and get_prep_value() do not use this blank path for empty strings; they parse "" and raise ValidationError.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
value | MeasurementValidationValue | Measurement value or empty literal under validation. | required |
model_instance | Model | None | Instance owning the field; unused but provided for API compatibility. | None |
Returns:
| Type | Description |
|---|---|
None | None |
Raises:
| Type | Description |
|---|---|
ValidationError | If the value violates null/blank constraints or is a non-empty non-Measurement value. Null and blank failures use Django's standard |
general_manager.measurement.measurement.ureg module-attribute ¶
ureg = UnitRegistry(auto_reduce_dimensions=True)
general_manager.measurement.measurement.currency_units module-attribute ¶
currency_units = [
"EUR",
"USD",
"GBP",
"JPY",
"CHF",
"AUD",
"CAD",
]