django-ordered-model
An abstract Django model mixin that keeps rows ordered relative to each other, with admin drag-style controls and DRF support.
Repository Health
Technical Analysis
django-ordered-model gives Django models a persisted position field and a full set of methods for rearranging rows relative to one another: swap, up, down, to, above, below, top, and bottom. Instead of hand-rolling order-shifting update queries every time a list needs reordering, a model simply inherits from OrderedModel and gets a managed order PositiveIntegerField plus a custom manager/queryset that keeps every other row’s position consistent on save, delete, and bulk_create.
Ordering can be scoped to a subset of rows via order_with_respect_to, so a many-to-one relationship (contacts per user, line items per invoice) orders correctly within each group rather than across the whole table, including support for multi-field and cross-relation lookups. A separate OrderedManyToManyField addresses a long-standing Django limitation where many-to-many querysets don’t respect the intermediate model’s ordering.
For the admin, OrderedModelAdmin plus OrderedTabularInline/OrderedStackedInline add move-up/move-down arrow links directly in the change list and inline formsets, and a reorder_model management command can repair ordering that drifted from bypassed save/delete paths. A bundled OrderedModelSerializer lets Django REST Framework endpoints reorder objects by writing to the order field directly.
What You Get
- An
OrderedModelabstract base class adding a managed, indexedorderfield with correct positioning on create, delete, and bulk_create - Relative-move methods on every instance:
swap(),up(),down(),to(position),above(ref),below(ref),top(),bottom() order_with_respect_tofor scoping order to a subset (single field, multiple fields, or a path through a related model)OrderedModelAdmin,OrderedTabularInline, andOrderedStackedInlinefor admin move-up/move-down arrow controls- A
reorder_modelmanagement command to repair ordering that drifted from bypassed save/delete calls OrderedManyToManyFieldsoManyToManyFieldquerysets respect the through model’s orderingOrderedModelSerializerfor Django REST Framework endpoints that need to reorder via API writes
Common Use Cases
- Reordering items in a Django admin change list with click-to-move arrow links instead of a raw numeric field
- Per-user or per-parent ordered lists, such as contacts under a user or line items under an invoice, via
order_with_respect_to - Exposing drag-and-drop reordering through a DRF API by writing new position values through
OrderedModelSerializer - Ordering a many-to-many relationship’s results consistently by swapping in
OrderedManyToManyField - Recovering from ordering drift caused by raw SQL updates or bulk operations that bypass model methods, using the
reorder_modelcommand
Under The Hood
Architecture
The library centers on OrderedModelBase (models.py), an abstract model that pairs a custom OrderedModelQuerySet/OrderedModelManager with per-instance move methods. Every mutating operation funnels through get_ordering_queryset(), which scopes to the current order_with_respect_to group, and to(), which is the single primitive that above(), below(), top(), bottom(), and up()/down() all delegate to for the actual increase/decrease-and-shift logic. save() and delete() detect when the WRT group has changed and re-shuffle the old and new groups accordingly, and a post_delete signal handler (registered per-subclass, not globally) repairs ordering when instances are removed via cascade or queryset .delete() rather than the instance method. A Meta.check() override adds custom Django system checks (E001-E006, W003) that catch missing Meta.ordering, wrong manager/queryset base classes, and invalid order_with_respect_to field paths at manage.py check time rather than at runtime. admin.py layers OrderedModelAdmin and the tabular/stacked inline variants on top, registering the extra URL routes needed for the arrow-link admin views.
Tech Stack
Pure Python/Django with no runtime dependencies beyond Django itself (compatible across Django 3.x-5.x per the compatibility table, Python 3.10-3.12 for the current 3.8.x line). Optional integration with Django REST Framework (3.15+) via a bundled serializer mixin. Packaging is plain setuptools (setup.py), tests run through tox across a matrix of Django/Python versions, and CI is GitHub Actions running the black formatter as a lint gate plus a separate test-and-coverage workflow.
Code Quality
The project has a single, extensive tests/tests.py file (1,700+ lines) covering swap/move/order-with-respect-to combinations, admin inline behavior, DRF serialization, and the custom system checks, run against a real Django test settings module rather than mocks. Code is formatted with black and enforced in CI. Naming is consistent and methods are documented with short docstrings; error handling favors explicit ValueError/TypeError raises with descriptive messages (e.g. rejecting non-integer to() calls, mismatched WRT groups) over silent failure. There is no static type-checking (no mypy config or type hints), consistent with the project’s age and the Django ecosystem norms at the time it was written.
What Makes It Unique
Rather than treating ordering as a bolt-on utility function, the library models it as a first-class Django abstraction: system checks that fail loudly at check-time if the manager/queryset chain is set up incorrectly, signal-based repair for the delete paths Django itself doesn’t let you intercept per-instance, and an OrderedManyToManyField that specifically works around a documented Django limitation where M2M querysets ignore the through model’s Meta.ordering. That combination of proactive validation and edge-case coverage (cascaded deletes, bulk_create, cross-relation order_with_respect_to) is more thorough than most ad hoc “add a position field” implementations.