{"id":7177,"library":"django-sortedm2m","title":"Django Sorted Many-to-Many","description":"django-sortedm2m is a drop-in replacement for Django's built-in ManyToManyField that preserves the order of related objects. It is actively maintained by the Jazzband community, with version 4.0.0 being the latest, offering support for recent Django and Python versions. Releases are frequent, focusing on compatibility updates, bug fixes, and minor enhancements.","status":"active","version":"4.0.0","language":"en","source_language":"en","source_url":"https://github.com/jazzband/django-sortedm2m","tags":["django","many-to-many","m2m","sorting","admin","database","jazzband"],"install":[{"cmd":"pip install django-sortedm2m","lang":"bash","label":"Install with pip"}],"dependencies":[{"reason":"Core framework dependency. Version 4.0.0 of django-sortedm2m supports Django 4.x, 5.0, and 5.1, and Python 3.6+.","package":"Django","optional":false}],"imports":[{"note":"The primary field to use in your Django models for ordered many-to-many relationships.","symbol":"SortedManyToManyField","correct":"from sortedm2m.fields import SortedManyToManyField"},{"note":"Required for manually editing migrations when changing between ManyToManyField and SortedManyToManyField.","symbol":"AlterSortedManyToManyField","correct":"from sortedm2m.operations import AlterSortedManyToManyField"}],"quickstart":{"code":"import os\nimport django\nfrom django.conf import settings\nfrom django.db import models\n\n# Minimal Django settings for demonstration\nsettings.configure(\n    INSTALLED_APPS=[\n        'django.contrib.auth',\n        'django.contrib.contenttypes',\n        'sortedm2m',\n        'my_app', # Your app name\n    ],\n    DATABASES={\n        'default': {\n            'ENGINE': 'django.db.backends.sqlite3',\n            'NAME': ':memory:',\n        }\n    },\n    MEDIA_ROOT=os.path.join(os.path.dirname(__file__), 'media'),\n    STATIC_URL='/static/',\n    DEBUG=True,\n    USE_TZ=True\n)\ndjango.setup()\n\n# Define your models\nclass Photo(models.Model):\n    name = models.CharField(max_length=50)\n    image = models.ImageField(upload_to='photos', blank=True, null=True)\n\n    def __str__(self):\n        return self.name\n\nclass Gallery(models.Model):\n    name = models.CharField(max_length=50)\n    photos = SortedManyToManyField(Photo)\n\n    def __str__(self):\n        return self.name\n\n# Example usage:\nif __name__ == '__main__':\n    # Create some photos\n    photo1 = Photo.objects.create(name='Sunset View')\n    photo2 = Photo.objects.create(name='Mountain Hike')\n    photo3 = Photo.objects.create(name='City Lights')\n\n    # Create a gallery\n    gallery = Gallery.objects.create(name='Travel Memories')\n\n    # Add photos in a specific order\n    gallery.photos.add(photo2)\n    gallery.photos.add(photo1)\n    gallery.photos.add(photo3)\n\n    print(f\"Gallery: {gallery.name}\")\n    print(\"Photos in order:\")\n    for photo in gallery.photos.all():\n        print(f\"- {photo.name}\")\n\n    # Reorder photos (example: move photo3 to the beginning)\n    gallery.photos.set([photo3.pk, photo2.pk, photo1.pk])\n    print(\"\\nPhotos after reordering:\")\n    for photo in gallery.photos.all():\n        print(f\"- {photo.name}\")\n\n    # Example with filtering and adding\n    new_gallery = Gallery.objects.create(name='Nature Wonders')\n    for photo in Photo.objects.order_by('name'):\n        new_gallery.photos.add(photo)\n    print(f\"\\nNew Gallery: {new_gallery.name}\")\n    print(\"Photos added by name order:\")\n    for photo in new_gallery.photos.all():\n        print(f\"- {photo.name}\")\n","lang":"python","description":"This quickstart demonstrates how to define models using `SortedManyToManyField` and how to add and retrieve related objects, maintaining their order. It also shows how to explicitly set the order using the `set()` method on the manager. Remember to add 'sortedm2m' to your `INSTALLED_APPS`."},"warnings":[{"fix":"Upgrade your Django and Python versions to supported ones (e.g., Django >= 4.2, Python >= 3.6) or pin `django-sortedm2m` to an earlier compatible version (e.g., `django-sortedm2m<4.0.0`).","message":"Version 4.0.0 of `django-sortedm2m` dropped support for older, outdated versions of Django and Python. Ensure your project meets the new requirements (Django 4.x, 5.0, 5.1 and Python 3.6+) before upgrading.","severity":"breaking","affected_versions":"4.0.0+"},{"fix":"Upgrade to `django-sortedm2m` version 4.0.0 or later, which includes a fix for this Django 5.1 incompatibility.","message":"When upgrading `django-sortedm2m` with Django 5.1, versions prior to 4.0.0 may encounter an `AttributeError: 'ManyToManyRel' object has no attribute 'is_hidden'` due to a change in Django's internal API.","severity":"breaking","affected_versions":"<4.0.0 with Django 5.1"},{"fix":"After running `makemigrations`, edit the generated migration file. Locate the `migrations.AlterField` operation and replace it with `operations.AlterSortedManyToManyField`. Ensure you add `from sortedm2m.operations import AlterSortedManyToManyField` to the migration file.","message":"When migrating an existing `ManyToManyField` to a `SortedManyToManyField` (or vice-versa), Django's `makemigrations` will generate an `AlterField` operation. This must be manually changed in the migration file to `AlterSortedManyToManyField` (imported from `sortedm2m.operations`) to ensure correct database schema changes.","severity":"gotcha","affected_versions":"All versions"},{"fix":"Add `'sortedm2m'` to `INSTALLED_APPS`. Remove affected fields from `filter_horizontal` or `filter_vertical`. Consider using `raw_id_fields` if you need a simpler input for IDs.","message":"For proper functioning of the custom sortable widget in the Django admin, ensure 'sortedm2m' is included in your `INSTALLED_APPS` setting. Additionally, avoid listing `SortedManyToManyField` fields in `filter_horizontal` or `filter_vertical` tuples in your `ModelAdmin` definitions, as this can interfere with the custom widget's functionality.","severity":"gotcha","affected_versions":"All versions"}],"env_vars":null,"last_verified":"2026-04-16T00:00:00.000Z","next_check":"2026-07-15T00:00:00.000Z","problems":[{"fix":"Add `'sortedm2m'` to your `INSTALLED_APPS` list in `settings.py`. If the package is not installed, run `pip install django-sortedm2m`.","cause":"The 'sortedm2m' app is not listed in your project's `INSTALLED_APPS` or the package is not installed in your Python environment.","error":"ModuleNotFoundError: No module named 'sortedm2m'"},{"fix":"Upgrade `django-sortedm2m` to version 4.0.0 or newer: `pip install --upgrade django-sortedm2m`.","cause":"This error occurs when using `django-sortedm2m` with Django 5.1, if `django-sortedm2m` is an older version that predates the fix for this Django API change.","error":"AttributeError: 'ManyToManyRel' object has no attribute 'is_hidden'"},{"fix":"Manually edit the generated migration file. Change the `migrations.AlterField` operation to `AlterSortedManyToManyField` (import it from `sortedm2m.operations`).","cause":"Django's automatic migration generation for changing field types (specifically to `SortedManyToManyField`) creates a generic `AlterField` operation, which does not correctly handle the intermediate model and ordering field setup specific to `django-sortedm2m`.","error":"A migration created by Django's makemigrations will not work as expected when changing ManyToManyField to SortedManyToManyField"}]}