Opened 2 years ago

Last modified 10 months ago

#34151 assigned Bug

Adding explicit primary key with different type doesn't update related ManyToManyFields.

Reported by: STANISLAV LEPEKHOV Owned by: Akash Kumar Sen
Component: Migrations Version: 4.1
Severity: Normal Keywords: migrations pk uuid relation
Cc: Sarah Abderemane Triage Stage: Accepted
Has patch: no Needs documentation: no
Needs tests: no Patch needs improvement: no
Easy pickings: no UI/UX: no

Description (last modified by STANISLAV LEPEKHOV)

Hello!
When i changed this models:

class StoreChain(models.Model):
    places = models.ManyToManyField(Place, blank=True)

class Place(models.Model):
    pass

to this edition (set pk with uuid type):

class StoreChain(models.Model):
    uid = models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, unique=True)
    places = models.ManyToManyField(Place, blank=True)

class Place(models.Model):
    uid = models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, unique=True)

Django creates a migration file that affects only the model tables, while the relationship table remains unchanged, which will cause an error, because the data type of the _ID fields in it also needs to be changed:
https://i.ibb.co/XZYjvYp/hU0HJyqF.jpg

Change History (18)

comment:1 by STANISLAV LEPEKHOV, 2 years ago

Description: modified (diff)

comment:2 by STANISLAV LEPEKHOV, 2 years ago

Triage Stage: UnreviewedAccepted

comment:3 by Mariusz Felisiak, 2 years ago

Easy pickings: unset
Triage Stage: AcceptedUnreviewed

Thanks for the report. Please don't accept your own tickets.

comment:4 by Mariusz Felisiak, 2 years ago

Component: Core (Management commands)Migrations
Summary: django.db.migrations doesn't update *_id column data type in related table when changing pk type of linked models.Adding explicit primary key with different type doesn't update related ManyToManyFields.
Triage Stage: UnreviewedAccepted

comment:5 by Bhuvnesh, 2 years ago

IMO solving ticket-29574 might also solve this issue.

Last edited 2 years ago by Bhuvnesh (previous) (diff)

in reply to:  4 comment:6 by STANISLAV LEPEKHOV, 2 years ago

Replying to Mariusz Felisiak:
Thanks for the comments!
I wanted to note that the problem also occurs for the models.ForeignKey fields.
The migration succeeds (in the absence of links), and after adding the link, an exception is thrown.
I apologize in advance for the machine translation =)

Here is the code that will throw the exception:

class StoreChain(models.Model):
    places = models.ManyToManyField(Place, blank=True)

class Place(models.Model):
    pass

if change to this edition (set pk with uuid type) and add relation after:

class StoreChain(models.Model):
    uid = models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, unique=True)
    place = models.ForeignKey (Place, blank=True, null=True)

class Place(models.Model):
    uid = models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, unique=True)

comment:7 by Bhuvnesh, 2 years ago

Owner: changed from nobody to Bhuvnesh
Status: newassigned

comment:8 by Aravind Swaminathan, 2 years ago

Does this problem occur only while using a uuid field or even with other type fields as primary key?

in reply to:  8 comment:9 by STANISLAV LEPEKHOV, 2 years ago

Replying to Aravind Swaminathan:

Does this problem occur only while using a uuid field or even with other type fields as primary key?

I found an error when changing the type to UUID, I did not check with other types of fields.

comment:10 by Bhuvnesh, 2 years ago

The migration generated for this contains removing the old pk and adding new pk :

class Migration(migrations.Migration):

    dependencies = [
        ("ticket_34151", "0001_initial"),
    ]

    operations = [
        migrations.RemoveField(
            model_name="place",
            name="id",
        ),
        migrations.RemoveField(
            model_name="storechain",
            name="id",
        ),
        migrations.AddField(
            model_name="place",
            name="uid",
            field=models.UUIDField(
                default=uuid.uuid4,
                editable=False,
                primary_key=True,
                serialize=False,
                unique=True,
            ),
        ),
        migrations.AddField(
            model_name="storechain",
            name="uid",
            field=models.UUIDField(
                default=uuid.uuid4,
                editable=False,
                primary_key=True,
                serialize=False,
                unique=True,
            ),
        ),
    ]

but on some databases (mysql, postgres) we cannot delete a pk of a model which is referenced by other.
I tried to implement a solution(draft PR ) which is working fine for sqlite3 but throws the following error on some dbs (mysql, postgres) during the RemoveField operation:

(1829, "Cannot drop column 'id': needed in a foreign key constraint 'ticket_34151_storech_placespk_id_ca7ce831_fk_ticket_34' of table 'ticket_34151_storechain'")

This made me think if it is really possible to add a new pk to a referenced model for dbs like mysql and postgres?

Version 0, edited 2 years ago by Bhuvnesh (next)

comment:11 by Bhuvnesh, 22 months ago

STANISLAV, With a default uuid value you cannot insert previous values of id in the new table ? So you can only change pk to uuid field when the db is empty ? Correct me if i'm wrong.

in reply to:  11 comment:12 by STANISLAV LEPEKHOV, 22 months ago

Replying to Bhuvnesh:

STANISLAV, With a default uuid value you cannot insert previous values of id in the new table ? So you can only change pk to uuid field when the db is empty ? Correct me if i'm wrong.

The essence of the problem is that I cannot correctly change the DATA TYPE for the pk field (including with an empty database). If I have an integer data type and there is a m2m field in another model containing pk of these objects, then when changing the data type to uuid, there will be no changes in the m2m table after migration (the type int, int will remain there)

comment:13 by Bhuvnesh, 22 months ago

Owner: Bhuvnesh removed
Status: assignednew

OK, so i did some more work on this and found that it is much more complicated than i thought it would be:

  1. the naive solution could be to remake all the tables that are referencing to a model's pk but even that is not possible in MySql as we cannot drop a table if it is referenced by other table.(throws error in case when 2 models are related to each other)
  2. the only solution i can think of (and tried) is deleting all related models and then recreating them with new uid field.
  3. One more way to do this can be to turn off FOREIGN_KEYS_CHECKS and then remake table, but that can be risky and can lead to data inconsistencies.

So i'm deassigning myself in case someone has a better solution.

Last edited 22 months ago by Bhuvnesh (previous) (diff)

comment:14 by Ayush Bisht, 22 months ago

Owner: set to Ayush Bisht
Status: newassigned

comment:15 by Priyank Panchal, 20 months ago

Owner: changed from Ayush Bisht to Priyank Panchal

comment:16 by Sarah Abderemane, 20 months ago

Cc: Sarah Abderemane added

comment:17 by Priyank Panchal, 16 months ago

Owner: Priyank Panchal removed
Status: assignednew

comment:18 by Akash Kumar Sen, 10 months ago

Owner: set to Akash Kumar Sen
Status: newassigned
Note: See TracTickets for help on using tickets.
Back to Top