{"id":23977966,"url":"https://github.com/ertgl/django-seq","last_synced_at":"2026-03-06T09:32:12.048Z","repository":{"id":37096873,"uuid":"488678659","full_name":"ertgl/django-seq","owner":"ertgl","description":"Django implementation of gapless sequences, with a configurable field type.","archived":false,"fork":false,"pushed_at":"2026-01-01T12:52:56.000Z","size":138,"stargazers_count":4,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2026-01-26T14:21:00.687Z","etag":null,"topics":["django","field","gapless-sequence","python"],"latest_commit_sha":null,"homepage":"","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/ertgl.png","metadata":{"files":{"readme":"README.md","changelog":null,"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":"2022-05-04T17:15:13.000Z","updated_at":"2026-01-01T12:52:53.000Z","dependencies_parsed_at":"2023-09-22T08:21:38.342Z","dependency_job_id":"f37e3cc1-6999-4933-9f81-1ef15c242877","html_url":"https://github.com/ertgl/django-seq","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/ertgl/django-seq","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ertgl%2Fdjango-seq","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ertgl%2Fdjango-seq/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ertgl%2Fdjango-seq/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ertgl%2Fdjango-seq/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/ertgl","download_url":"https://codeload.github.com/ertgl/django-seq/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ertgl%2Fdjango-seq/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":30168982,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-03-06T07:56:45.623Z","status":"ssl_error","status_checked_at":"2026-03-06T07:55:55.621Z","response_time":250,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.6:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"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","field","gapless-sequence","python"],"created_at":"2025-01-07T08:15:49.323Z","updated_at":"2026-03-06T09:32:12.027Z","avatar_url":"https://github.com/ertgl.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"# django-seq\n\nDjango implementation of gapless sequences, with a configurable field type.\n\n## Table of Contents\n\n- [Why?](#why)\n- [How?](#how)\n- [Installation](#installation)\n- [Usage](#usage)\n  - [SequenceField](#sequencefield)\n    - [Generating IDs using SequenceField](#generating-ids-using-sequencefield)\n    - [Generating IDs based on dependant fields](#generating-ids-based-on-dependant-fields)\n    - [Enabling automatic gap filling](#enabling-automatic-gap-filling)\n    - [Enabling automatic integrity error resolution](#enabling-automatic-integrity-error-resolution)\n    - [Drawbacks](#drawbacks)\n  - [Low Level API](#low-level-api)\n    - [Using the low level API](#using-the-low-level-api)\n- [License](#license)\n\n## Why?\n\nSome departments require their data to have IDs in sequential order. For\nexample in a financial system, the invoice IDs would likely need to be in\nsequential order.\n\nIn database management systems, sequences are used to generate unique\nidentifiers (IDs) for rows. For DBMs to support concurrency, it is possible\ngenerated IDs to be discarded. Which leads to have gaps between the IDs.\n\nWidely used DBMs such as PostgreSQL support concurrent operations. In order\nto support concurrent operations; sequence changes made during insert\noperations are never undone if an operation fails. Let's think we have\ntwo concurrent transactions. The first inserts a row with ID `1` and the\nsecond inserts a row with ID `2`. If the first fails, ID `1` will be\ndiscarded. Because insertion the row has been cancelled. Also, the second\ntransaction **does not** reduce the ID of the second row. As a result, the\nID of the first row in the table will be `2` instead of `1`. We can consider\nthe same scenario with different IDs. So, the discarded IDs are the gaps\nbetween the existing IDs. This is due to the nature of conflict/lock-free\nalgorithms used in\n[OLTP](https://en.wikipedia.org/wiki/Online_transaction_processing)\nenvironments.\n\n\n## How?\n\nOne way to solve this problem is to take control of sequence generation\nthrough a table and make the process part of the `transaction` to be\nexecuted. Thus, if the transaction fails, the change on the sequences\ntable can be rolled back automatically. `django-seq` follows this approach.\n\n\n**Table:** sequences\n\n| **key** | **value** |\n| ------- | --------- |\n| ...     | ...       |\n\n\nWhile inserting a data, make sure that the operation is within a\ntransaction. In the same transaction, select the corresponding\nsequence row by locking it for update. If it exists, update the\nsequence row with the next value. Otherwise, insert a sequence row\nwith value `1`. At the end of both cases, set data's ID to the value\nof the sequence.\n\n\n## Installation\n\n`django-seq` is available on [PyPI](https://pypi.org/project/django-seq/).\nIt can be installed and upgraded using [pip](https://pip.pypa.io):\n\n```shell\npip install django-seq\n```\n\n\n## Usage\n\n`django-seq` provides both high and low level APIs to manage sequences.\nWhile the high level one increases the development speed, the low level one\ngives developers more control over the sequence generation and allows it to\nbe used for different situations than the simple CRUD.\n\nMost of the time, using the high level API will suffice. Moreover, it\nrequires almost no change in the logic of any application.\nSee the section [SequenceField](#SequenceField) for more information.\n\n\n### SequenceField\n\n`SequenceField` is a subclass of `PositiveBigIntegerField` that can be used\nto automatically generate unique IDs for model instances.\n\nWhen a `SequenceField` is added to a model, `pre_save` signal is connected\nfor that model. When the signal is fired, the sequence is updated with the\nnext value. By default, the sequence name is the name of the model's table.\nYou are free to customize the sequence name by specifying the `key`\nparameter. You can specify the key with the following types:\n\n- `int`: The value will be converted to string.\n- `str`: The string will be used as is.\n- `django.db.models.F`: The value should be a pointer to a field of the\n    model instance or one of its relations. Allowed relationship types are\n    `one to one` and `many to one`. If the value points to a model instance,\n    it will be replaced with the primary key of the instance.\n- `list`: Objects in the list can be any of the above types. The evaluated\n    list members will be joined with the `separator` parameter's value\n    (which defaults to `.`) to form the sequence name.\n- `tuple`: Same as `list`.\n- `callable`: The callable will be called with the model instance being\n    saved, as the only argument. The return value will be used in the\n    sequence generation process. The callable can return any of the above\n    types.\n\n**Note:** If the evaluated key is a falsy value\n(an empty string or `None` in this context), the sequence generation process\nwill be skipped for that model instance.\n\n\n#### Generating IDs using SequenceField\n\nAdd the field to your model like any other field:\n\n```python\nfrom django.db import models\nfrom django_seq.models import SequenceField\n\n\nclass Invoice(models.Model):\n\n    number = SequenceField()\n```\n\n\n#### Generating IDs based on dependant fields\n\nOne of possible use cases of `django-seq` is to generate IDs based on some\ndependant fields. For example, in an issue tracking system, it may be\npreferable to generate issues' IDs based on the projects they belong to.\nThis can be achieved by specifying the `key` parameter as a list like below:\n\n```python\nfrom django.db import models\nfrom django_seq.models import SequenceField\n\n\nclass Project(models.Model):\n    pass\n\n\nclass Issue(models.Model):\n\n    project = models.ForeignKey(\n        to=Project,\n        on_delete=models.CASCADE,\n    )\n\n    index = SequenceField(\n        key=['projects', models.F('project'), 'issues'],\n    )\n\n    class Meta:\n\n        unique_together = (('project', 'index'),)\n```\n\nThe sequence name will be in the format of `projects.\u003cproject_id\u003e.issues`\nwhere `\u003cproject_id\u003e` is the primary key of the project. Thus, each project\nwill have its own sequence for its issues, that starts from `1`.\n\n\n#### Enabling automatic gap filling\n\nTo make the sequence generator fill the gaps, set `fill_gaps` parameter as `True`\nor a callable that takes model instance as a parameter and returns `True`. This is\nuseful when you want the deleted IDs to be reused. So, the sequence generator will\ngenerate an ID based on the first gap it finds, then sets the current value of the\nsequence to that new ID.\n\n```python\nfrom django.db import models\nfrom django_seq.models import SequenceField\n\n\nclass Item(models.Model):\n\n    number = SequenceField(\n      fill_gaps=True,\n    )\n```\n\n\n#### Enabling automatic integrity error resolution\n\nIn some cases, the sequence generator may fail to generate a unique ID. For example,\nwhen the sequence is used in a unique constraint and the current value is updated\nmanually in a way that causes the generator to generate an ID that already exists.\nIn such cases, the operation raises `IntegrityError`.\n\nTo make the sequence generator resolve the integrity errors automatically, set\n`resolve_integrity_errors` parameter as `True` or a callable that takes model instance\nas a parameter and returns `True`. So, the sequence generator will generate an ID based\non the first gap it finds after the last generated ID, then sets the current value of the\nsequence to that new ID.\n\n```python\nfrom django.db import models\nfrom django_seq.models import SequenceField\n\n\ndef _should_resolve_integrity_errors(invoice: \"Invoice\") -\u003e bool:\n  return True\n\n\nclass Invoice(models.Model):\n\n    number = SequenceField(\n      resolve_integrity_errors=_should_resolve_integrity_errors,\n    )\n```\n\n\n#### Drawbacks\n\nSince the technique implemented in `django-seq` is based on the concept of\nsynchronous/atomic transactions; concurrent Python processes mutating\nthe same sequence are not prevented to be started, but they will be waiting\nfor the current transaction to be committed or rolled back.\n\nIf a part of your system that generates IDs has heavy load, you may want\nto follow a different approach that suitable for the use case. In some\nsituations implementing a queue to generate IDs using the low level API can\nhelp to solve some tradeoff problems.\n\n\n### Low Level API\n\n`django_seq.models.AbstractSequence` provides two methods:\n\n- `get_next_value`: Returns the next value of the sequence. Every time it's\n    triggered, the sequence will be updated with the next value.\n- `get_current_value`: Returns the current value of the sequence.\n- `set_current_value`: Sets the current value of the sequence as the given\n    value and returns the value back.\n\n\n#### Using the low level API\n\nCalling `get_next_value` or `set_current_value` always must be within a transaction.\n\n```python\nfrom django.db import transaction\nfrom django_seq.models import get_sequence_model\n\n\nSequence = get_sequence_model()\n\n\nwith transaction.atomic():\n    value = Sequence.get_next_value('projects.1.issues')\n    assert Sequence.get_current_value('projects.1.issues') == value\n```\n\n## License\n\nThis project is licensed under the\n[MIT License](https://opensource.org/license/mit).\n\nSee the [LICENSE](LICENSE) file for more information.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fertgl%2Fdjango-seq","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fertgl%2Fdjango-seq","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fertgl%2Fdjango-seq/lists"}