mirror of
https://github.com/almet/notmyidea.git
synced 2025-04-28 19:42:37 +02:00
103 lines
3.2 KiB
Markdown
103 lines
3.2 KiB
Markdown
---
|
|
title: Changing the primary key of a model in Django
|
|
tags: django, orm, migrations
|
|
---
|
|
|
|
I had to change the primary key of a django model, and I wanted to create a
|
|
migration for this.
|
|
|
|
The previous model was using django [automatic primary key fields](https://
|
|
docs.djangoproject.com/en/5.0/topics/db/models/#automatic-primary-key-fields)
|
|
|
|
I firstly changed the model to include the new `uuid` field, and added the `id`
|
|
field (the old primary key), like this:
|
|
|
|
```python
|
|
|
|
uuid = models.UUIDField(
|
|
unique=True, primary_key=True, default=uuid.uuid4, editable=False
|
|
)
|
|
id = models.IntegerField(null=True, blank=True)
|
|
```
|
|
|
|
Then I created the migration, it:
|
|
|
|
- Adds a new `uuid` field/column in the database
|
|
- Iterate over the existing items in the table, and generates an uuid for them
|
|
- Change the old primary key to a different type
|
|
- Drop the old index
|
|
- Mark the new uuid as a primary key.
|
|
|
|
To generate the migrations I did `django-admin makemigrations`, and iterated on
|
|
it. Here is the migration I ended up with:
|
|
|
|
```python
|
|
import uuid
|
|
|
|
from django.db import migrations, models
|
|
|
|
class Migration(migrations.Migration):
|
|
dependencies = [
|
|
("umap", "0017_migrate_to_openstreetmap_oauth2"),
|
|
]
|
|
|
|
operations = [
|
|
# Add the new uuid field
|
|
migrations.AddField(
|
|
model_name="datalayer",
|
|
name="uuid",
|
|
field=models.UUIDField(
|
|
default=uuid.uuid4, editable=False, null=True, serialize=False
|
|
),
|
|
),
|
|
# Generate UUIDs for existing records
|
|
migrations.RunSQL("UPDATE umap_datalayer SET uuid = gen_random_uuid()"),
|
|
# Remove the primary key constraint
|
|
migrations.RunSQL("ALTER TABLE umap_datalayer DROP CONSTRAINT umap_datalayer_pk"),
|
|
# Drop the "id" primary key…
|
|
migrations.AlterField(
|
|
"datalayer", name="id", field=models.IntegerField(null=True, blank=True)
|
|
),
|
|
# … to put it back on the "uuid"
|
|
migrations.AlterField(
|
|
model_name="datalayer",
|
|
name="uuid",
|
|
field=models.UUIDField(
|
|
default=uuid.uuid4,
|
|
editable=False,
|
|
unique=True,
|
|
primary_key=True,
|
|
serialize=False,
|
|
),
|
|
),
|
|
]
|
|
```
|
|
|
|
## Generating UUIDs in pure python
|
|
|
|
The uuid generation can also be done with pure python, like this. It works with all databases, but might be slower. Use it with `migrations.RunPython()`.
|
|
|
|
```python
|
|
def gen_uuid(apps, schema_editor):
|
|
DataLayer = apps.get_model("umap", "DataLayer")
|
|
for row in DataLayer.objects.all():
|
|
row.uuid = uuid.uuid4()
|
|
row.save(update_fields=["uuid"])
|
|
```
|
|
|
|
## Getting the constraint name
|
|
|
|
One of the things that took me some time is to have a way to get the constraint name before removing it. I wanted to do this with the Django ORM, but I didn't find how. So here is how in plain SQL. This only works with PostgreSQL, though.
|
|
|
|
```python
|
|
migrations.RunSQL("""
|
|
DO $$
|
|
BEGIN
|
|
EXECUTE 'ALTER TABLE umap_datalayer DROP CONSTRAINT ' || (
|
|
SELECT indexname
|
|
FROM pg_indexes
|
|
WHERE tablename = 'umap_datalayer' AND indexname LIKE '%pkey'
|
|
);
|
|
END $$;
|
|
"""),
|
|
```
|