{"id":16213645,"url":"https://github.com/django-ordered-model/django-ordered-model","last_synced_at":"2025-05-13T19:16:58.651Z","repository":{"id":789579,"uuid":"486345","full_name":"django-ordered-model/django-ordered-model","owner":"django-ordered-model","description":"Get your Django models in order","archived":false,"fork":false,"pushed_at":"2024-11-15T15:16:07.000Z","size":547,"stargazers_count":753,"open_issues_count":23,"forks_count":159,"subscribers_count":14,"default_branch":"master","last_synced_at":"2025-05-12T22:39:25.563Z","etag":null,"topics":["django","django-admin"],"latest_commit_sha":null,"homepage":"","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"other","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/django-ordered-model.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGES.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2010-01-24T14:32:27.000Z","updated_at":"2025-05-10T19:34:19.000Z","dependencies_parsed_at":"2023-07-05T19:33:22.056Z","dependency_job_id":"efae42b3-0bf0-4c1f-8db4-9e86aca751e6","html_url":"https://github.com/django-ordered-model/django-ordered-model","commit_stats":{"total_commits":303,"total_committers":59,"mean_commits":5.135593220338983,"dds":0.7755775577557755,"last_synced_commit":"5e03a93823c4cded172d37dcfd4743d57b60b0e6"},"previous_names":[],"tags_count":34,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/django-ordered-model%2Fdjango-ordered-model","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/django-ordered-model%2Fdjango-ordered-model/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/django-ordered-model%2Fdjango-ordered-model/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/django-ordered-model%2Fdjango-ordered-model/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/django-ordered-model","download_url":"https://codeload.github.com/django-ordered-model/django-ordered-model/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":254010823,"owners_count":21999003,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["django","django-admin"],"created_at":"2024-10-10T11:07:19.096Z","updated_at":"2025-05-13T19:16:58.528Z","avatar_url":"https://github.com/django-ordered-model.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"django-ordered-model\n====================\n\n![Python versions](https://img.shields.io/pypi/pyversions/django-ordered-model.svg) [![Build Status](https://github.com/django-ordered-model/django-ordered-model/actions/workflows/test.yml/badge.svg)](https://github.com/django-ordered-model/django-ordered-model/actions/workflows/test.yml)\n[![PyPI version](https://badge.fury.io/py/django-ordered-model.svg)](https://badge.fury.io/py/django-ordered-model)\n[![Code style: black](https://img.shields.io/badge/code%20style-black-000000.svg)](https://github.com/python/black)\n\ndjango-ordered-model allows models to be ordered and provides a simple admin\ninterface for reordering them.\n\nBased on https://djangosnippets.org/snippets/998/ and\nhttps://djangosnippets.org/snippets/259/\n\nSee our [compatability notes](#compatibility-with-django-and-python) for the appropriate version to use with older Django and Python releases.\n\nInstallation\n------------\n\nPlease install using Pip:\n\n```bash\n$ pip install django-ordered-model\n```\n\nOr if you have checked out the repository:\n\n```bash\n$ python setup.py install\n```\n\nOr to use the latest development code from our master branch:\n\n```bash\n$ pip uninstall django-ordered-model\n$ pip install git+git://github.com/django-ordered-model/django-ordered-model.git\n```\n\nUsage\n-----\n\nAdd `ordered_model` to your `SETTINGS.INSTALLED_APPS`.\n\nInherit your model from `OrderedModel` to make it ordered:\n\n```python\nfrom django.db import models\nfrom ordered_model.models import OrderedModel\n\n\nclass Item(OrderedModel):\n    name = models.CharField(max_length=100)\n\n```\n\nThen run the usual `$ ./manage.py makemigrations` and `$ ./manage.py migrate` to update your database schema.\n\nModel instances now have a set of methods to move them relative to each other.\nTo demonstrate those methods we create two instances of `Item`:\n\n```python\nfoo = Item.objects.create(name=\"Foo\")\nbar = Item.objects.create(name=\"Bar\")\n```\n\n### Swap positions\n\n```python\nfoo.swap(bar)\n```\n\nThis swaps the position of two objects.\n\n### Move position up on position\n\n```python\nfoo.up()\nfoo.down()\n```\n\nMoving an object up or down just makes it swap its position with the neighbouring\nobject directly above of below depending on the direction.\n\n### Move to arbitrary position\n\n```python\nfoo.to(12)\nbar.to(13)\n```\n\nMove the object to an arbitrary position in the stack. This essentially sets the\norder value to the specified integer. Objects between the original and the new\nposition get their order value increased or decreased according to the direction\nof the move.\n\n### Move object above or below reference\n\n```python\nfoo.above(bar)\nfoo.below(bar)\n```\n\nMove the object directly above or below the reference object, increasing or\ndecreasing the order value for all objects between the two, depending on the\ndirection of the move.\n\n### Move to top of stack\n\n```python\nfoo.top()\n```\n\nThis sets the order value to the lowest value found in the stack and increases\nthe order value of all objects that were above the moved object by one.\n\n### Move to bottom of stack\n\n```python\nfoo.bottom()\n```\n\nThis sets the order value to the highest value found in the stack and decreases\nthe order value of all objects that were below the moved object by one.\n\n### Updating fields that would be updated during save()\n\nFor performance reasons, the `delete()`, `to()`, `below()`, `above()`, `top()`, and\n`bottom()` methods use Django's `update()` method to change the order of other objects\nthat are shifted as a result of one of these calls. If the model has fields that\nare typically updated in a customized save() method, or through other app level\nfunctionality such as `DateTimeField(auto_now=True)`, you can add additional fields\nto be passed through to `update()`. This will only impact objects where their order\nis being shifted as a result of an operation on the target object, not the target\nobject itself.\n\n```python\nfoo.to(12, extra_update={'modified': now()})\n```\n\n### Get the previous or next objects\n\n```python\nfoo.previous()\nfoo.next()\n```\n\nThe `previous()` and `next()` methods return the neighbouring objects directly above or below\nwithin the ordered stack.\n\n## Subset Ordering\n\nIn some cases, ordering objects is required only on a subset of objects. For example,\nan application that manages contact lists for users, in a many-to-one/many relationship,\nwould like to allow each user to order their contacts regardless of how other users\nchoose their order. This option is supported via the `order_with_respect_to` parameter.\n\nA simple example might look like so:\n\n```python\nclass Contact(OrderedModel):\n    user = models.ForeignKey(User, on_delete=models.CASCADE)\n    phone = models.CharField()\n    order_with_respect_to = 'user'\n```\n\nIf objects are ordered with respect to more than one field, `order_with_respect_to` supports\ntuples to define multiple fields:\n\n```python\nclass Model(OrderedModel)\n    # ...\n    order_with_respect_to = ('field_a', 'field_b')\n```\n\nIn a many-to-many relationship you need to use a separate through model which is derived from the OrderedModel.\nFor example, an application which manages pizzas with toppings.\n\nA simple example might look like so:\n\n```python\nclass Topping(models.Model):\n    name = models.CharField(max_length=100)\n\n\nclass Pizza(models.Model):\n    name = models.CharField(max_length=100)\n    toppings = models.ManyToManyField(Topping, through='PizzaToppingsThroughModel')\n\n\nclass PizzaToppingsThroughModel(OrderedModel):\n    pizza = models.ForeignKey(Pizza, on_delete=models.CASCADE)\n    topping = models.ForeignKey(Topping, on_delete=models.CASCADE)\n    order_with_respect_to = 'pizza'\n\n    class Meta:\n        ordering = ('pizza', 'order')\n```\n\nYou can also specify `order_with_respect_to` to a field on a related model. An example use-case can be made with the following models:\n\n```python\nclass ItemGroup(models.Model):\n    user = models.ForeignKey(User, on_delete=models.CASCADE)\n    general_info = models.CharField(max_length=100)\n\nclass GroupedItem(OrderedModel):\n    group = models.ForeignKey(ItemGroup, on_delete=models.CASCADE)\n    specific_info = models.CharField(max_length=100)\n    order_with_respect_to = 'group__user'\n```\n\nHere items are put into groups that have some general information used by its items, but the ordering of the items is independent of the group the item is in.\n\nIn all cases `order_with_respect_to` must specify a `ForeignKey` field on the model, or a Django Check `E002`, `E005` or `E006` error will be raised with further help.\n\nWhen you want ordering on the baseclass instead of subclasses in an ordered list of objects of various classes, specify the full module path of the base class:\n\n```python\nclass BaseQuestion(OrderedModel):\n    order_class_path = __module__ + '.BaseQuestion'\n    question = models.TextField(max_length=100)\n\n    class Meta:\n        ordering = ('order',)\n\nclass MultipleChoiceQuestion(BaseQuestion):\n    good_answer = models.TextField(max_length=100)\n    wrong_answer1 = models.TextField(max_length=100)\n    wrong_answer2 = models.TextField(max_length=100)\n    wrong_answer3 = models.TextField(max_length=100)\n\nclass OpenQuestion(BaseQuestion):\n    answer = models.TextField(max_length=100)\n```\n\nOrdering of ManyToMany Relationship query results\n-----------------\n\nDjango ManyToMany relationships created by `ManyToManyField` [do not respect `Meta.ordering` on the intermediate model](https://code.djangoproject.com/ticket/30460) in results fetched from the 'members' queryset. For example with our usual `Pizza` example, getting the `Toppings` for a `hawaiian_pizza` instance using `PizzaToppingsThroughModel.objects.filter(pizza=hawaiian_pizza).all()` is correctly ordered (by the ThroughModel `Meta.ordering`). However `hawaiian_pizza.toppings.all()` is not, and returns the objects following the 'to' model ordering.\n\nTo work around this, explicitly add an ordering clause, e.g. with `hawaiian_pizza.toppings.all().order_by('pizzatoppingsthroughmodel__order')` or use our `OrderedManyToManyField` which does this by default:\n\n```python\nfrom ordered_model.fields import OrderedManyToManyField\n\nclass Pizza(models.Model):\n    name = models.CharField(max_length=100)\n    toppings = OrderedManyToManyField(Topping, through=\"PizzaToppingsThroughModel\")\n\n\nclass PizzaToppingsThroughModel(OrderedModel):\n    pizza = models.ForeignKey(Pizza, on_delete=models.CASCADE)\n    topping = models.ForeignKey(Topping, on_delete=models.CASCADE)\n    order_with_respect_to = \"pizza\"\n\n    class Meta:\n        ordering = (\"pizza\", \"order\")\n```\n\nWith this definition `hawaiian_pizza.toppings.all()` returns toppings in order.\n\nCustom Manager and QuerySet\n-----------------\nWhen your model extends `OrderedModel`, it inherits a custom `ModelManager` instance which in turn provides additional operations on the resulting `QuerySet`. For example if `Item` is an `OrderedModel` subclass, the  queryset `Item.objects.all()` has functions:\n\n* `above_instance(object)`,\n* `below_instance(object)`,\n* `get_min_order()`,\n* `get_max_order()`,\n* `above(index)`,\n* `below(index)`\n\nIf your `Model` uses a custom `ModelManager` (such as `ItemManager` below) please have it extend `OrderedModelManager`, or else Django Check `E003` will be raised.\n\nIf your `ModelManager` returns a custom `QuerySet` (such as `ItemQuerySet` below) please have it extend `OrderedModelQuerySet`, or Django Check `E004` will be raised.\n\n```python\nfrom ordered_model.models import OrderedModel, OrderedModelManager, OrderedModelQuerySet\n\nclass ItemQuerySet(OrderedModelQuerySet):\n    pass\n\nclass ItemManager(OrderedModelManager):\n    def get_queryset(self):\n        return ItemQuerySet(self.model, using=self._db)\n\nclass Item(OrderedModel):\n    objects = ItemManager()\n```\n\nIf another Django plugin requires you to use specific `Model`, `QuerySet` or `ModelManager` classes, you might need to construct intermediate classes using multiple inheritance, [see an example in issue 270](https://github.com/django-ordered-model/django-ordered-model/issues/270).\n\nCustom ordering field\n---------------------\nExtending `OrderedModel` creates a `models.PositiveIntegerField` field called `order` and the appropriate migrations. It customises the default `class Meta` to then order returned querysets by this field. If you wish to use an existing model field to store the ordering, subclass `OrderedModelBase` instead and set the attribute `order_field_name` to match your field name and the `ordering` attribute on `Meta`:\n\n```python\nclass MyModel(OrderedModelBase):\n    ...\n    sort_order = models.PositiveIntegerField(editable=False, db_index=True)\n    order_field_name = \"sort_order\"\n\n    class Meta:\n        ordering = (\"sort_order\",)\n```\nSetting `order_field_name` is specific for this library to know which field to change when ordering actions are taken. The `Meta` `ordering` line is existing Django functionality to use a field for sorting.\nSee `tests/models.py` object `CustomOrderFieldModel` for an example.\n\n\nAdmin integration\n-----------------\n\nTo add arrows in the admin change list page to do reordering, you can use the\n`OrderedModelAdmin` and the `move_up_down_links` field:\n\n```python\nfrom django.contrib import admin\nfrom ordered_model.admin import OrderedModelAdmin\nfrom models import Item\n\n\nclass ItemAdmin(OrderedModelAdmin):\n    list_display = ('name', 'move_up_down_links')\n\nadmin.site.register(Item, ItemAdmin)\n```\n\n![ItemAdmin screenshot](./static/items.png)\n\n\nFor a many-to-many relationship you need one of the following inlines.\n\n`OrderedTabularInline` or `OrderedStackedInline` just like the django admin.\n\nFor the `OrderedTabularInline` it will look like this:\n\n```python\nfrom django.contrib import admin\nfrom ordered_model.admin import OrderedTabularInline, OrderedInlineModelAdminMixin\nfrom models import Pizza, PizzaToppingsThroughModel\n\n\nclass PizzaToppingsTabularInline(OrderedTabularInline):\n    model = PizzaToppingsThroughModel\n    fields = ('topping', 'order', 'move_up_down_links',)\n    readonly_fields = ('order', 'move_up_down_links',)\n    ordering = ('order',)\n    extra = 1\n\n\nclass PizzaAdmin(OrderedInlineModelAdminMixin, admin.ModelAdmin):\n    model = Pizza\n    list_display = ('name', )\n    inlines = (PizzaToppingsTabularInline, )\n\n\nadmin.site.register(Pizza, PizzaAdmin)\n```\n\n![PizzaAdmin screenshot](./static/pizza.png)\n\n\nFor the `OrderedStackedInline` it will look like this:\n\n```python\nfrom django.contrib import admin\nfrom ordered_model.admin import OrderedStackedInline, OrderedInlineModelAdminMixin\nfrom models import Pizza, PizzaToppingsThroughModel\n\n\nclass PizzaToppingsStackedInline(OrderedStackedInline):\n    model = PizzaToppingsThroughModel\n    fields = ('topping', 'move_up_down_links',)\n    readonly_fields = ('move_up_down_links',)\n    ordering = ('order',)\n    extra = 1\n\n\nclass PizzaAdmin(OrderedInlineModelAdminMixin, admin.ModelAdmin):\n    list_display = ('name', )\n    inlines = (PizzaToppingsStackedInline, )\n\n\nadmin.site.register(Pizza, PizzaAdmin)\n```\n\n![PizzaAdmin screenshot](./static/pizza-stacked.png)\n\n**Note:** `OrderedModelAdmin` requires the inline subclasses of `OrderedTabularInline` and `OrderedStackedInline` to be listed on `inlines` so that we register appropriate URL routes. If you are using Django 3.0 feature `get_inlines()` or `get_inline_instances()` to return the list of inlines dynamically, consider it a filter and still add them to `inlines` or you might encounter a “No Reverse Match” error when accessing model change view.\n\nRe-ordering models\n------------------\n\nIn certain cases the models will end up in a not properly ordered state. This can be caused\nby bypassing the 'delete' / 'save' methods, or when a user changes a foreign key of a object\nwhich is part of the 'order_with_respect_to' fields. You can use the following command to\nre-order one or more models.\n\n    $ ./manage.py reorder_model \u003capp_name\u003e.\u003cmodel_name\u003e \\\n            [\u003capp_name\u003e.\u003cmodel_name\u003e ... ]\n\n    The arguments are as follows:\n    - `\u003capp_name\u003e`: Name of the application for the model.\n    - `\u003cmodel_name\u003e`: Name of the model that's an OrderedModel.\n\n\nDjango Rest Framework\n---------------------\n\nTo support updating ordering fields by Django Rest Framework, we include a serializer `OrderedModelSerializer` that intercepts writes to the ordering field, and calls `OrderedModel.to()` method to effect a re-ordering:\n\n```python\nfrom rest_framework import routers, serializers, viewsets\nfrom ordered_model.serializers import OrderedModelSerializer\nfrom tests.models import CustomItem\n\nclass ItemSerializer(serializers.HyperlinkedModelSerializer, OrderedModelSerializer):\n    class Meta:\n        model = CustomItem\n        fields = ['pkid', 'name', 'modified', 'order']\n\nclass ItemViewSet(viewsets.ModelViewSet):\n    queryset = CustomItem.objects.all()\n    serializer_class = ItemSerializer\n\nrouter = routers.DefaultRouter()\nrouter.register(r'items', ItemViewSet)\n```\n\nNote that you need to include the 'order' field (or your custom field name) in the `Serializer`'s `fields` list, either explicitly or using `__all__`. See [ordered_model/serializers.py](ordered_model/serializers.py) for the implementation.\n\nTest suite\n----------\n\nTo run the tests against your current environment, use:\n\n```bash\n$ pip install djangorestframework\n$ django-admin test --pythonpath=. --settings=tests.settings\n```\n\nOtherwise please install `tox` and run the tests for a specific environment with `-e` or all environments:\n\n```bash\n$ tox -e py36-django30\n$ tox\n```\n\nCompatibility with Django and Python\n-----------------------------------------\n\n|django-ordered-model version | Django version      | Python version    | DRF (optional)\n|-----------------------------|---------------------|-------------------|----------------\n| **3.8.x**                   | **3.x**, **4.x**, **5.x** | **3.10** to **3.12** | 3.15 and above\n| **3.7.x**                   | **3.x**, **4.x**    | **3.5** and above | 3.12 and above\n| **3.6.x**                   | **3.x**, **4.x**    | **3.5** and above | 3.12 and above\n| **3.5.x**                   | **3.x**, **4.x**    | **3.5** and above | -\n| **3.4.x**                   | **2.x**, **3.x**    | **3.5** and above | -\n| **3.3.x**                   | **2.x**             | **3.4** and above | -\n| **3.2.x**                   | **2.x**             | **3.4** and above | -\n| **3.1.x**                   | **2.x**             | **3.4** and above | -\n| **3.0.x**                   | **2.x**             | **3.4** and above | -\n| **2.1.x**                   | **1.x**             | **2.7** to 3.6    | -\n| **2.0.x**                   | **1.x**             | **2.7** to 3.6    | -\n\n\nMaintainers\n-----------\n\n * [Ben Firshman](https://github.com/bfirsh)\n * [Chris Shucksmith](https://github.com/shuckc)\n * [Sardorbek Imomaliev](https://github.com/imomaliev)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdjango-ordered-model%2Fdjango-ordered-model","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fdjango-ordered-model%2Fdjango-ordered-model","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdjango-ordered-model%2Fdjango-ordered-model/lists"}