# Django 4.2, is a 2nd autofield-like field on a model possible?

**URL:** <https://forum.djangoproject.com/t/django-4-2-is-a-2nd-autofield-like-field-on-a-model-possible/28278>\
**Category:** Using the ORM\
**Created:** [February 20, 2024, 9:18pm UTC](https://forum.djangoproject.com/t/django-4-2-is-a-2nd-autofield-like-field-on-a-model-possible/28278 "2024-02-20T21:18:59Z")\
**Posts on this page:** 6\
**Page:** 1

<div class="post-metadata">

**Author:** ![jaddison](https://sea2.discourse-cdn.com/flex026/user_avatar/forum.djangoproject.com/jaddison/32/19332_2.png) [@jaddison](https://forum.djangoproject.com/u/jaddison)\
**Post date:** [February 20, 2024, 9:18pm UTC](https://forum.djangoproject.com/t/django-4-2-is-a-2nd-autofield-like-field-on-a-model-possible/28278/1 "2024-02-20T21:18:59Z")

</div>

- we use UUIDField for all primary keys on our models
- we have a requirement to have an autoincrementing field as well

One cannot simply add an AutoField to the model, as it is required to be primary key, and we already have a primary key.

Have added a PositiveBigIntegerField for this purpose, and added a manual migration step to add a sequence/identity and apply it to the field. However, when an instance of the model is saved, it complains about a `NULL` value.

Assuming this is the correct approach, what is actually needed is for the compiled INSERT statement to either

- not include the sequenced column in the insert (but still return the generated value via RETURNING) or
- leverage the SQL `DEFAULT` keyword.

The latter feels like the correct approach, but I’m at a loss for how to achieve this. The objective is to transparently create a database-saved instance of this model with the ORM python instance of it containing the generated value (like you would see with an AutoField as PK).

---

<div class="post-metadata">

**Author:** ![KenWhitesell](https://sea2.discourse-cdn.com/flex026/user_avatar/forum.djangoproject.com/kenwhitesell/32/280_2.png) [@KenWhitesell](https://forum.djangoproject.com/u/KenWhitesell)\
**Post date:** [February 20, 2024, 9:28pm UTC](https://forum.djangoproject.com/t/django-4-2-is-a-2nd-autofield-like-field-on-a-model-possible/28278/2 "2024-02-20T21:28:53Z")

</div>

See the ticket [#8576 (Multiple AutoFields in a model) – Django](https://code.djangoproject.com/ticket/8576) and the thread at [Rationale for unique AutoField's](https://forum.djangoproject.com/t/rationale-for-unique-autofields/23122)

---

<div class="post-metadata">

**Author:** ![charettes](https://sea2.discourse-cdn.com/flex026/user_avatar/forum.djangoproject.com/charettes/32/27_2.png) [@charettes](https://forum.djangoproject.com/u/charettes)\
**Post date:** [February 20, 2024, 9:44pm UTC](https://forum.djangoproject.com/t/django-4-2-is-a-2nd-autofield-like-field-on-a-model-possible/28278/3 "2024-02-20T21:44:56Z")

</div>

@jaddison you might also be interested in [this accepted feature request to include a non-primary key serial field to contrib.postgres](https://code.djangoproject.com/ticket/27452).

With the recent extended support for `db_returning`, generated, and `db_default` in `main` I suspect this one might be easier to implement now if you want to have a shot at it. Happy to support you through code reviews.

To add support for it to 4.2 you’ll likely have to tweak a few things, I suggest you have a look at [how `db_default` was implemented in 5.0](https://github.com/django/django/pull/16092). It includes [the introduction of a `DatabaseDefault` expression](https://github.com/django/django/commit/7414704e88d73dafbcfbb85f9bc54cb6111439d3#diff-359ab56295cce36b9597e1d65185f695f271955617b40f43df58ce3fe76fd0c8R1128) which you might be able to draw inspiration from by [having the field `pre_save`](https://github.com/django/django/commit/7414704e88d73dafbcfbb85f9bc54cb6111439d3#diff-35c61cb049d39b2e890f12f8c1f9583fb8ca351a8495718b5375a7120f775d05R976) return an instance of. [From there setting `db_returning` on your field subclass](https://github.com/django/django/commit/7414704e88d73dafbcfbb85f9bc54cb6111439d3#diff-35c61cb049d39b2e890f12f8c1f9583fb8ca351a8495718b5375a7120f775d05R920) should do the trick 🤞.

Hope it helps!

---

<div class="post-metadata">

**Author:** ![jaddison](https://sea2.discourse-cdn.com/flex026/user_avatar/forum.djangoproject.com/jaddison/32/19332_2.png) [@jaddison](https://forum.djangoproject.com/u/jaddison)\
**Post date:** [February 21, 2024, 12:47am UTC](https://forum.djangoproject.com/t/django-4-2-is-a-2nd-autofield-like-field-on-a-model-possible/28278/4 "2024-02-21T00:47:28Z")

</div>

@charettes thank you - that was the breadcrumb I needed. The following rough implementation works:

```python
class Default(Expression):
    def as_sql(self, compiler, connection):
        return "DEFAULT", []

class AutoPositiveBigIntegerField(models.PositiveBigIntegerField):
    @property
    def db_returning(self):
        return True

    def get_default(self):
        return Default()

class Invoice(models.Model):
    ...
    number = AutoPositiveBigIntegerField(unique=True)

```

The above, coupled with the following manual migration on the `number` field:

```python
from django.db import migrations

class Migration(migrations.Migration):

    dependencies = [
        ("billing", "0003_my_migration"),
    ]

    operations = [
        migrations.RunSQL(
            "ALTER TABLE invoice ALTER COLUMN number ADD GENERATED BY DEFAULT AS IDENTITY;",
            reverse_sql="ALTER TABLE invoice ALTER COLUMN number DROP IDENTITY IF EXISTS;"
        ),
    ]

```

---

<div class="post-metadata">

**Author:** ![jaddison](https://sea2.discourse-cdn.com/flex026/user_avatar/forum.djangoproject.com/jaddison/32/19332_2.png) [@jaddison](https://forum.djangoproject.com/u/jaddison)\
**Post date:** [February 21, 2024, 5:07pm UTC](https://forum.djangoproject.com/t/django-4-2-is-a-2nd-autofield-like-field-on-a-model-possible/28278/5 "2024-02-21T17:07:40Z")

</div>

A coworker (thanks Mark!) pointedout that I could go one step further and remove the need for the custom migration by overriding `db_type_suffix`:

```python
class Default(Expression):
    def as_sql(self, compiler, connection):
        return "DEFAULT", []

class AutoPositiveBigIntegerField(models.PositiveBigIntegerField):
    @property
    def db_returning(self):
        return True

    def get_default(self):
        return Default()

    def db_type_suffix(self, connection):
        return "GENERATED BY DEFAULT AS IDENTITY"

class Invoice(models.Model):
    ...
    number = AutoPositiveBigIntegerField(unique=True)

```

---

<div class="post-metadata">

**Author:** ![tom](https://sea2.discourse-cdn.com/flex026/user_avatar/forum.djangoproject.com/tom/32/6818_2.png) [@tom](https://forum.djangoproject.com/u/tom)\
**Post date:** [June 8, 2024, 12:15pm UTC](https://forum.djangoproject.com/t/django-4-2-is-a-2nd-autofield-like-field-on-a-model-possible/28278/6 "2024-06-08T12:15:46Z")

</div>

@jaddison there is now some work on this: [Fixed #27452 -- Added serial fields to contrib.postgres. by csirmazbendeguz · Pull Request #18123 · django/django · GitHub](https://github.com/django/django/pull/18123)

It would be really great to have any input you might have on the review.
