Skip to main content

django-postgres-multirange

PyPI Python versions License Tests

PostgreSQL multirange fields for Django, built on the same primitives as django.contrib.postgres range fields.

A multirange is an ordered set of non-overlapping ranges — the native way to store things like available hours, blocked-out dates, or VLAN id spans in a single column.

Django declined to add these types to contrib.postgres. This package fills that gap.

Requirements

  • Python 3.10+
  • Django 5.2+
  • psycopg 3
  • PostgreSQL 14+ (developed against PostgreSQL 17)

Install

pip install django-postgres-multirange
# or, with the binary psycopg build:
pip install "django-postgres-multirange[binary]"

Add django.contrib.postgres to INSTALLED_APPS. Adding django_postgres_multirange is optional; the fields work without it.

Fields

Each range field Django already ships has a matching multirange:

Field PostgreSQL type Element
IntegerMultiRangeField int4multirange int4 / IntegerRangeField
BigIntegerMultiRangeField int8multirange int8 / BigIntegerRangeField
DecimalMultiRangeField nummultirange numeric / DecimalRangeField
DateTimeMultiRangeField tstzmultirange timestamptz / DateTimeRangeField
DateMultiRangeField datemultirange date / DateRangeField

Python values are psycopg.types.multirange.Multirange objects — a mutable sequence of Range. Lists of ranges or (lower, upper) pairs are accepted on assignment, the same way RangeField accepts a pair.

from django.contrib.postgres.indexes import GistIndex
from django.db import models
from django_postgres_multirange import IntegerMultiRangeField, Multirange
from psycopg.types.range import Range


class Schedule(models.Model):
    hours = IntegerMultiRangeField()

    class Meta:
        indexes = [GistIndex(fields=["hours"])]


Schedule.objects.create(hours=[(9, 12), (13, 17)])
row = Schedule.objects.get()
# PostgreSQL normalizes on write.
assert row.hours == Multirange([Range(9, 12), Range(13, 17)])

Continuous fields (DecimalMultiRangeField, DateTimeMultiRangeField) accept Django's default_bounds ("[)" by default) for pair input.

Empty ({} / Multirange()) is distinct from NULL.

Lookups

The lookups match RangeField, plus two that only make sense on a multirange:

Lookup Operator / function
contains @> element, range, or multirange
contained_by <@
overlap &&
fully_lt <<
fully_gt >>
not_lt &>
not_gt &<
adjacent_to `-
startswith / endswith lower() / upper() of the whole multirange
isempty isempty()
lower_inc, lower_inf, upper_inc, upper_inf bound tests
range_merge smallest covering range
len number of ranges
Schedule.objects.filter(hours__contains=10)
Schedule.objects.filter(hours__overlap=(12, 14))
Schedule.objects.filter(hours__contains=[(9, 10), (14, 15)])
Schedule.objects.filter(hours__range_merge__contains=16)
Schedule.objects.filter(hours__len=2)

GiST indexes and ExclusionConstraint work the same way they do for range fields (&&, @>, <@, …).

Development

make up     # Postgres 17 on localhost:54317
make test

See CONTRIBUTING.md for the full workflow.

Roadmap

Not in this release:

  • tsmultirange (Django has no tsrange counterpart)
  • union / intersection / difference / unnest database functions
  • range_agg / range_intersect_agg
  • user-defined range types
  • a non-JSON admin widget

License

BSD-3-Clause

Questions or issues: omar.bohsali@gmail.com

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

django_postgres_multirange-0.1.0.tar.gz (15.2 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

django_postgres_multirange-0.1.0-py3-none-any.whl (12.3 kB view details)

Uploaded Python 3

File details

Details for the file django_postgres_multirange-0.1.0.tar.gz.

File metadata

File hashes

Hashes for django_postgres_multirange-0.1.0.tar.gz
Algorithm Hash digest
SHA256 1aeb864950b5beb5e39db979fea1676a650bc8693010a15d55b0dc390df0a717
MD5 58616595b03f0365d3bbec151a48010c
BLAKE2b-256 0c7afbdb12acbd8f97faf0722e637fb41705400a834b18e19119b9b36867cdf6

See more details on using hashes here.

Provenance

The following attestation bundles were made for django_postgres_multirange-0.1.0.tar.gz:

Publisher: publish.yml on omarish/django-postgres-multirange

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file django_postgres_multirange-0.1.0-py3-none-any.whl.

File metadata

File hashes

Hashes for django_postgres_multirange-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 8539104cd3e58c80abc6d17f2f6a181d855718403a219c09375f1e5ce3a698a2
MD5 7000a96b0866dc6500daef1b0f517e65
BLAKE2b-256 1fe3096c999089de1f306d2d0a64c403f1fbd9c97d33b52f0ee0b32b286207b7

See more details on using hashes here.

Provenance

The following attestation bundles were made for django_postgres_multirange-0.1.0-py3-none-any.whl:

Publisher: publish.yml on omarish/django-postgres-multirange

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Release history Release notifications | RSS feed

This release

0.1.0 This release

2 files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page