Skip to content

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, "count" for discrete item quantities, compound Pint expressions such as "kg / m^2", or dimensionless units.

required

Raises:

Type Description
InvalidMeasurementInitializationError

If value cannot be converted to a Decimal or is a bool.

PintError

If unit is not parseable by Pint.

__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 magnitude and unit entries for pickling.

__setstate__

__setstate__(state)

Recreate the internal quantity from a serialized representation.

Parameters:

Name Type Description Default
state dict[str, str]

Serialized state containing magnitude and unit values.

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 " " or a single numeric token for a dimensionless value.

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 target_unit is invalid or incompatible with the source unit.

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 other is not a Measurement.

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 other is not a Measurement.

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 other is not a Measurement or a supported numeric type, including bool.

__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 other is a Measurement the result carries the derived units; if other is a scalar the result retains this measurement's unit.

Raises:

Type Description
CurrencyMismatchError

If both operands are currencies with different units.

MeasurementScalarTypeError

If other is not a Measurement or a numeric type, including bool.

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 " " for measurements with a unit, or as "" for dimensionless measurements.

__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

self if other is 0, otherwise the result of adding other to self.

__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 self from other.

Raises:

Type Description
MeasurementOperandTypeError

If other is neither 0 nor a Measurement instance.

__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 other by self.

__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 other by self.

Raises:

Type Description
MeasurementOperandTypeError

If other is not a Measurement instance or numeric scalar.

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

Measurement or string accepted by from_string.

required

Returns:

Type Description
bool

True when magnitudes are equal after unit normalization. The

bool

null-like values handled by _compare() return False.

Raises:

Type Description
UnsupportedComparisonError

If other cannot be interpreted as a measurement.

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

Measurement or string accepted by from_string.

required

Returns:

Type Description
bool

True when magnitudes differ after unit normalization. The

bool

null-like values handled by _compare() return False rather

bool

than True.

Raises:

Type Description
UnsupportedComparisonError

If other cannot be interpreted as a measurement.

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

Measurement or string accepted by from_string.

required

Returns:

Type Description
bool

True when this magnitude is smaller after unit normalization.

Raises:

Type Description
UnsupportedComparisonError

If other cannot be interpreted as a measurement.

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

Measurement or string accepted by from_string.

required

Returns:

Type Description
bool

True when this magnitude is smaller or equal after unit normalization.

Raises:

Type Description
UnsupportedComparisonError

If other cannot be interpreted as a measurement.

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

Measurement or string accepted by from_string.

required

Returns:

Type Description
bool

True when this magnitude is larger after unit normalization.

Raises:

Type Description
UnsupportedComparisonError

If other cannot be interpreted as a measurement.

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 other.

Raises:

Type Description
TypeError

If other cannot be interpreted as a measurement.

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 currency_units; each configured currency is treated as its own Pint dimension. Use count for discrete item quantities.

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 base_unit is an offset unit such as degC.

PintError

If Pint cannot parse or dimensionally evaluate base_unit.

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 Measurement class's

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 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 Measurement.from_string() are allowed to propagate; parser ValueError and dimensionality failures are wrapped in ValidationError. Empty strings, whitespace-only strings, invalid unit syntax, and unknown unit names that surface as ValueError use the generic invalid-value ValidationError. GeneralManager does not make exact non-wrapped Pint subclasses part of this field's stable API.

__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 editable=False.

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 ''."; non-currency values assigned to currency fields use "Unit must be a currency (AUD, CAD, CHF, EUR, GBP, JPY, USD)."; currency values assigned to non-currency fields use "Unit cannot be a currency."; wrong currencies use "Unit must be compatible with ''.".

PintError

Only non-ValueError Pint exceptions that escape Measurement.from_string() are allowed to propagate; parser ValueError and dimensionality failures are wrapped in ValidationError. Empty strings, whitespace-only strings, invalid unit syntax, and unknown unit names that surface as ValueError use the generic invalid-value ValidationError. GeneralManager does not make exact non-wrapped Pint subclasses part of this field's stable API.

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 null and blank error codes.

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