API Reference¶
This page documents all public classes, functions, and parameters in django-pint-field.
Model Fields¶
BasePintField¶
django_pint_field.models.BasePintField
Base class for all PintField types. You do not use this directly; use IntegerPintField or DecimalPintField instead.
Parameters:
Parameter |
Type |
Required |
Default |
Description |
|---|---|---|---|---|
|
|
Yes |
– |
The default unit for this field. Can be a string ( |
|
|
No |
|
Available units for this field. Each item can be a string or a display/value pair. The |
|
|
No |
|
Allow NULL in the database. |
|
|
No |
|
Allow blank values in forms. |
|
|
No |
|
Human-readable field name. |
|
|
No |
|
Help text for forms. |
Key methods:
db_type(connection)– Returns"pint_field"(the PostgreSQL composite type).from_db_value(value, expression, connection)– Converts the database composite value into aPintFieldProxy.get_prep_value(value)– Prepares a Python value for use in database queries.to_python(value)– Converts any input value to a PintQuantity.validate(value, model_instance)– Runs field validation including dimensionality checks.formfield(**kwargs)– Returns the appropriate form field for this model field.value_to_string(obj)– Returns a string representation for serialization.
IntegerPintField¶
django_pint_field.models.IntegerPintField
A PintField that stores the magnitude as an integer. Inherits all parameters from BasePintField.
Best for measurements where whole numbers are expected (counts, simple weights where decimal places are not needed). Internally, the value is stored as a decimal, but form input and display use integers.
Uses IntegerPintFormField as the default form field.
from django_pint_field.models import IntegerPintField
class Product(models.Model):
weight = IntegerPintField("gram", unit_choices=["gram", "kilogram"])
DecimalPintField¶
django_pint_field.models.DecimalPintField
A PintField that stores the magnitude as a decimal. Inherits all parameters from BasePintField, plus:
Parameter |
Type |
Required |
Default |
Description |
|---|---|---|---|---|
|
|
No |
|
Number of decimal places to show in display and forms. |
|
|
No |
|
Rounding method for decimal operations. Accepts any Python |
Available rounding methods: ROUND_CEILING, ROUND_DOWN, ROUND_FLOOR, ROUND_HALF_DOWN, ROUND_HALF_EVEN, ROUND_HALF_UP, ROUND_UP, ROUND_05UP.
Uses DecimalPintFormField as the default form field.
from django_pint_field.models import DecimalPintField
class Sample(models.Model):
mass = DecimalPintField(
"gram",
display_decimal_places=4,
rounding_method="ROUND_HALF_UP",
)
Field Descriptor and Proxy¶
PintFieldDescriptor¶
django_pint_field.models.PintFieldDescriptor
A descriptor that intercepts attribute access on PintField model attributes. When you access instance.weight, the descriptor returns a PintFieldProxy instead of a raw value. You do not interact with this class directly.
PintFieldProxy¶
django_pint_field.helpers.PintFieldProxy
Wraps a Pint Quantity to add attribute-based unit conversion. Returned whenever you access a PintField value on a model instance.
Attribute access patterns:
Access |
Returns |
Example |
|---|---|---|
|
|
|
|
|
|
|
|
The underlying Pint |
|
|
|
|
|
|
|
|
|
|
|
|
Supported operators:
Comparison: ==, !=, <, <=, >, >=
Arithmetic: +, -, *, /, //, %
All comparisons and arithmetic operations are unit-aware and raise errors on incompatible dimensions.
PintFieldMixin¶
django_pint_field.models.PintFieldMixin
A mixin added to all PintField types. Adds dynamic unit conversion properties to the model class at creation time.
Key methods:
add_properties(cls, name)– Called during model class creation. For a field namedweight, addsweight__<unit>properties for compatible units available in the active registry.contribute_to_class(cls, name, **kwargs)– Standard Django field hook. Installs thePintFieldDescriptorand callsadd_properties.
Form Fields¶
IntegerPintFormField¶
django_pint_field.forms.IntegerPintFormField
Form field for IntegerPintField models.
Parameter |
Type |
Required |
Default |
Description |
|---|---|---|---|---|
|
|
Yes |
– |
Default unit for the field. |
|
|
No |
|
Available unit choices. |
|
|
No |
|
Minimum allowed value. |
|
|
No |
|
Maximum allowed value. |
|
|
No |
|
Whether the field is required. |
DecimalPintFormField¶
django_pint_field.forms.DecimalPintFormField
Form field for DecimalPintField models.
Parameter |
Type |
Required |
Default |
Description |
|---|---|---|---|---|
|
|
Yes |
– |
Default unit for the field. |
|
|
No |
|
Available unit choices. |
|
|
No |
|
Decimal places to display. |
|
|
No |
|
Rounding method for display. |
|
|
No |
|
Whether the field is required. |
Widgets¶
PintFieldWidget¶
django_pint_field.widgets.PintFieldWidget
The default widget for PintFields. Renders a numeric input alongside a unit selection dropdown.
Parameter |
Type |
Required |
Default |
Description |
|---|---|---|---|---|
|
|
Yes |
– |
Default unit for the widget. |
|
|
No |
|
Available unit choices for the dropdown. |
|
|
No |
|
HTML attributes for the numeric input. |
TabledPintFieldWidget¶
django_pint_field.widgets.TabledPintFieldWidget
Extends PintFieldWidget by adding a table that shows the current value converted to all available units.
Inherits all parameters from PintFieldWidget, plus:
Parameter |
Type |
Default |
Description |
|---|---|---|---|
|
|
|
Number of decimal places in the conversion table. |
|
|
|
CSS class for the |
|
|
|
CSS class for the |
|
|
|
CSS class for the |
|
|
|
CSS class for header |
|
|
|
CSS class for body |
|
|
|
CSS class for |
|
|
|
CSS class for |
|
|
|
CSS class for unit name cells. |
|
|
|
CSS class for value cells. |
|
|
|
CSS class for the wrapper around the input section. |
|
|
|
CSS class for the wrapper around the table section. |
|
|
|
Custom header text for the units column. |
|
|
|
Custom header text for the values column. |
|
|
|
Whether to display units in value cells. |
The widget template can be overridden at: templates/django_pint_field/tabled_django_pint_field_widget.html
Serializer Fields (Django REST Framework)¶
IntegerPintRestField¶
django_pint_field.rest.IntegerPintRestField
String-based serialization for IntegerPintField.
Parameter |
Type |
Default |
Description |
|---|---|---|---|
|
|
|
If |
Accepts string input like "1000 gram" or "Quantity(1000 gram)".
DecimalPintRestField¶
django_pint_field.rest.DecimalPintRestField
String-based serialization for DecimalPintField.
Parameter |
Type |
Default |
Description |
|---|---|---|---|
|
|
|
If |
Accepts string input like "1.5 gram" or "Quantity(1.5 gram)".
PintRestField¶
django_pint_field.rest.PintRestField
Dictionary-based serialization for any PintField.
Output format:
{ "magnitude": 1.5, "units": "gram" }
Accepts dictionary input with magnitude and units keys.
Aggregates¶
django_pint_field.aggregates
All aggregates accept a field name as the first argument and return results with proper unit handling.
Aggregate |
Returns |
Description |
|---|---|---|
|
|
Count of non-null values. |
|
|
Average value. |
|
|
Sum of values. |
|
|
Maximum value. |
|
|
Minimum value. |
|
|
Standard deviation. Set |
|
|
Variance. Set |
|
|
Continuous percentile ( |
|
|
Median (50th percentile). |
|
|
Unit-aware window wrapper for running/partitioned aggregates. |
Aggregates that return a PintFieldProxy accept an optional output_unit= to convert the result. The pint_histogram(queryset, field_name, *, buckets, min_value, max_value) helper returns a list of {"bucket", "lower", "upper", "count"} dicts (boundaries as Quantity, in base units) computed with PostgreSQL width_bucket.
The unit-bearing aggregates set window_compatible = False, so a bare django.db.models.Window(PintSum(...)) raises ValueError (it would otherwise silently return a base-unit number in the wrong unit). Use PintWindow(PintSum("field"), order_by=..., partition_by=..., frame=...) for running totals and partitioned aggregates; it accepts the same arguments as Window, returns a result in the field’s base unit (use the wrapped aggregate’s output_unit, or .quantity.to(...), to convert), and rejects ordered-set aggregates (PintPercentile, PintMedian) since PERCENTILE_CONT cannot be used in an OVER clause. For PintCount (no unit conversion), a plain Window works.
from django_pint_field.aggregates import PintAvg, PintSum
Product.objects.aggregate(
avg_weight=PintAvg("weight"),
total_weight=PintSum("weight"),
)
Expressions¶
django_pint_field.expressions
SQL-native query expressions that read and transform the pint_field composite directly in PostgreSQL.
Expression |
Returns |
Description |
|---|---|---|
|
|
The base-unit |
|
|
The originally stored |
|
|
The magnitude converted to |
from django_pint_field import PintConvert
Product.objects.annotate(kg=PintConvert("weight", "kilogram")).filter(kg__gte=2)
PintConvert raises ValueError for an empty to_unit and pint.UndefinedUnitError for an unknown one, both at expression construction; a to_unit whose dimensionality is incompatible with the field raises ValidationError when the query is built. See Querying for the indexing and precision caveats.
Lookups¶
PintFields support the following lookups. All comparisons operate on the comparator component (the base-unit magnitude), so they work correctly across different units.
Supported lookups:
Lookup |
Example |
|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Unsupported lookups (raise PintFieldLookupError):
contains, icontains, in, startswith, istartswith, endswith, iendswith, date, year, iso_year, month, day, week, iso_week_day, week_day, quarter, time, hour, minute, second, regex, iregex, search, isearch
These lookups are disabled because PintFields store composite types, not simple text or date values.
Indexes¶
PintFieldComparatorIndex¶
django_pint_field.indexes.PintFieldComparatorIndex
A specialized index that targets the comparator component of PintField columns. This provides better query performance than a standard index on the full composite field.
Parameter |
Type |
Required |
Default |
Description |
|---|---|---|---|---|
|
|
Yes |
– |
Field names to index. |
|
|
No |
auto-generated |
Index name. |
|
|
No |
|
Django |
|
|
No |
|
Additional columns to include (covering index). |
|
|
No |
|
PostgreSQL tablespace for the index. |
|
|
No |
|
Operator classes for the index. |
from django_pint_field.indexes import PintFieldComparatorIndex
class Product(models.Model):
weight = DecimalPintField("gram")
class Meta:
indexes = [
PintFieldComparatorIndex(fields=["weight"]),
]
Adapters¶
PintDumper¶
django_pint_field.adapters.PintDumper
A psycopg3 Dumper subclass that serializes Pint Quantity objects into the PostgreSQL composite type format. This is registered automatically when django-pint-field initializes; you should not need to interact with it directly.
Exceptions¶
PintFieldLookupError¶
django_pint_field.exceptions.PintFieldLookupError
Subclass of Django’s FieldError. Raised when an unsupported lookup is used on a PintField.
# This raises PintFieldLookupError:
Product.objects.filter(weight__contains="gram")
Units¶
ureg¶
django_pint_field.units.ureg
The global UnitRegistry instance used by all PintFields. By default, this is a pint.UnitRegistry(non_int_type=Decimal).
If you set DJANGO_PINT_FIELD_UNIT_REGISTER in your settings, ureg points to your custom registry instead.
from django_pint_field.units import ureg
quantity = ureg.Quantity("500 gram")
Validation¶
QuantityConverter¶
django_pint_field.validation.QuantityConverter
Handles conversion of various input types to Pint Quantity objects.
Parameter |
Type |
Default |
Description |
|---|---|---|---|
|
|
– |
Default unit for conversions. |
|
|
|
|
|
|
|
Custom registry; if omitted, |
Method:
convert(value)– Converts the input to aQuantity. Accepts:str,int,float,Decimal,Quantity,list/tuple,dict, orPintFieldProxy.
Validator Functions¶
Function |
Description |
|---|---|
|
Validates and normalizes unit choices. Returns |
|
Checks that a value’s unit dimensionality matches the default unit. Raises |
|
Validates that a required field has a non-empty value. |
|
Validates that a decimal value does not exceed the context precision. |
|
Validates that a value falls within the specified range, with unit-aware comparison. |
Typing and Performance¶
django-pint-field ships a py.typed marker (PEP 561), so type checkers and IDEs recognize its public API as typed.
Each PintField instance reuses a single PintFieldConverter (via get_cached_converter()), so loading N rows allocates one converter rather than one per row. This is an internal optimization and does not change any returned values.
For a model instance loaded from the database, the field value is wrapped once in a PintFieldProxy and stored on the instance, so repeated reads return the same object. Attribute-based unit access (e.g. obj.weight.kilogram) is available on loaded instances.