{"id":8115,"library":"django-sequences","title":"Django Sequences","description":"django-sequences is a Python library for Django that provides a reliable way to generate gapless sequences of integer values. Unlike Django's default auto-incrementing primary keys, which can have gaps due to rolled-back transactions, this library ensures sequential integrity. It is currently at version 3.0, actively maintained, and compatible with modern Django versions (3.2 and up).","status":"active","version":"3.0","language":"en","source_language":"en","source_url":"https://github.com/aaugustin/django-sequences","tags":["django","sequence","gapless","integer","transaction","database","numbering"],"install":[{"cmd":"pip install django-sequences","lang":"bash","label":"Install stable version"}],"dependencies":[{"reason":"Core framework dependency; tested with Django 3.2 (LTS), 4.0, 4.1, 4.2 (LTS), 5.0, 5.1, 5.2 (LTS), and 6.0.","package":"Django","optional":false}],"imports":[{"note":"The top-level package name is 'sequences', not 'django_sequences'.","wrong":"from django_sequences import get_next_value","symbol":"get_next_value","correct":"from sequences import get_next_value"},{"note":"Provides an object-oriented API for sequence generation.","symbol":"Sequence","correct":"from sequences import Sequence"},{"note":"Required to register the application in Django settings.","symbol":"SequencesConfig","correct":"INSTALLED_APPS = [..., 'sequences.apps.SequencesConfig', ...]"}],"quickstart":{"code":"import os\nfrom django.db import transaction\nfrom sequences import get_next_value\n\n# Assume 'Invoice' is a Django model with an 'number' field\n# from invoices.models import Invoice\n\n# For demonstration, we'll mock a model and save operation\nclass MockInvoice:\n    _instances = []\n    def __init__(self, number):\n        self.number = number\n        print(f\"Created MockInvoice with number: {self.number}\")\n        MockInvoice._instances.append(self)\n\ndef create_invoice_with_sequence(sequence_name='invoice_numbers'):\n    try:\n        with transaction.atomic():\n            next_invoice_number = get_next_value(sequence_name)\n            # Replace with your actual model creation:\n            # Invoice.objects.create(number=next_invoice_number)\n            MockInvoice(number=next_invoice_number)\n            print(f\"Successfully committed invoice with number: {next_invoice_number}\")\n    except Exception as e:\n        print(f\"Transaction failed: {e}\")\n\n# Example usage:\ncreate_invoice_with_sequence('orders')\ncreate_invoice_with_sequence('orders')\ncreate_invoice_with_sequence('shipments', initial_value=1000)\ncreate_invoice_with_sequence('shipments')\n","lang":"python","description":"To generate a gapless sequence, ensure that `get_next_value()` and the subsequent database save operation occur within the same atomic transaction. The library provides `get_next_value()` for a functional approach and a `Sequence` class for an object-oriented API. Remember to add `sequences.apps.SequencesConfig` to your `INSTALLED_APPS` and run migrations."},"warnings":[{"fix":"Always wrap your `get_next_value()` call and model creation/update in `django.db.transaction.atomic()`.","message":"For django-sequences to guarantee gapless values, `get_next_value()` and the corresponding model save operation MUST be executed within the same database transaction. If the transaction is committed, the value is consumed; otherwise, if the transaction rolls back, the value is not consumed by the user's model but the sequence counter is still incremented internally.","severity":"gotcha","affected_versions":"All versions"},{"fix":"This is expected behavior and a trade-off for performance. Design your application to handle occasional internal sequence gaps if auditing the `sequences` table directly.","message":"If a transaction involving `get_next_value()` is rolled back (e.g., due to an error in your code after retrieving the value but before committing), the internal sequence counter for `django-sequences` itself is NOT rolled back for performance reasons. This means there will be a 'gap' in the sequence numbers generated by `django-sequences` if you inspect its internal table, even though your application's committed records remain gapless.","severity":"gotcha","affected_versions":"All versions"},{"fix":"Minimize the amount of work performed within the `transaction.atomic()` block that calls `get_next_value()`. Consider using `nowait=True` on `get_next_value()` for non-blocking behavior, though this might raise exceptions on contention.","message":"Database transactions that call `get_next_value()` for a given sequence are serialized. This means concurrent calls for the *same* sequence will block, potentially impacting performance. Keep transactions involving `get_next_value()` as short as possible.","severity":"gotcha","affected_versions":"All versions"},{"fix":"Use the `read committed` isolation level for optimal compatibility and guarantees. If using `repeatable read`, implement retry logic for transactions that encounter serialization errors.","message":"The `read uncommitted` database isolation level is NOT supported and will lead to gaps and incorrect behavior. The `repeatable read` level is supported but requires application-level handling of serialization failures and retries.","severity":"breaking","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":"Ensure all calls to `get_next_value()` and the subsequent `model.save()` are within a `with transaction.atomic():` block.","cause":"`get_next_value` was called, but the model saving the value was not wrapped in an atomic transaction or the transaction failed to commit, leading to a duplicate value being attempted later.","error":"IntegrityError: duplicate key value violates unique constraint"},{"fix":"Optimize the code within the `transaction.atomic()` block to be as fast as possible. If extreme concurrency is needed and occasional non-sequential IDs are acceptable, consider alternative ID generation strategies or sharding sequences across different names.","cause":"Access to `get_next_value()` for a specific sequence is serialized at the database level to ensure gapless numbers. Long-running transactions or very high contention can lead to performance bottlenecks.","error":"Application is slow when multiple users/processes try to generate sequence numbers simultaneously."},{"fix":"Verify that `django.db.transaction.atomic()` strictly wraps both the `get_next_value()` call and the database `create()` or `save()` for the record that uses the generated number.","cause":"This typically happens if the `get_next_value()` call and the record creation/update are not within the same atomic transaction, or if a transaction committed a value, but later records were inserted using a different mechanism or without using the sequence correctly.","error":"Gaps appear in generated sequence numbers in my application's records."}]}