#31788: Models migration with change field foreign to many and deleting unique
together.
----------------------------+------------------------------------
     Reporter:  budzichd    |                    Owner:  nobody
         Type:  Bug         |                   Status:  new
    Component:  Migrations  |                  Version:  3.0
     Severity:  Normal      |               Resolution:
     Keywords:              |             Triage Stage:  Accepted
    Has patch:  0           |      Needs documentation:  0
  Needs tests:  0           |  Patch needs improvement:  0
Easy pickings:  0           |                    UI/UX:  0
----------------------------+------------------------------------

Old description:

> I have models like
>
> {{{
> class Authors(models.Model):
>
>     project_data_set = models.ManyToManyField(
>         ProjectDataSet,
>         on_delete=models.PROTECT
>     )
>     state = models.IntegerField()
>     start_date = models.DateField()
>
>     class Meta:
>          unique_together = (('project_data_set', 'state', 'start_date'),)
> }}}
>
> and
>
> {{{
> class DataSet(models.Model):
>     name = models.TextField(max_length=50)
>

> class Project(models.Model):
>     data_sets = models.ManyToManyField(
>         DataSet,
>         through='ProjectDataSet',
>     )
>     name = models.TextField(max_length=50)
>

> class ProjectDataSet(models.Model):
>     """
>     Cross table of data set and project
>     """
>     data_set = models.ForeignKey(DataSet, on_delete=models.PROTECT)
>     project = models.ForeignKey(Project, on_delete=models.PROTECT)
>
>     class Meta:
>         unique_together = (('data_set', 'project'),)
> }}}
>
> when i want to change field project_data_set in Authors model from
> foreign key field to many to many field I must delete a unique_together,
> cause it can't be on many to many field.
> Then my model should be like:
>
> {{{
> class Authors(models.Model):
>
>     project_data_set = models.ManyToManyField(
>         ProjectDataSet,
>     )
>
>     state = models.IntegerField()
>     start_date = models.DateField()
> }}}
>
> But when I want to do a migrations.
> 1. python3 manage.py makemigrations
> 2. python3 manage.py migrate
> I have error:
> {{{ValueError: Found wrong number (0) of constraints for
> app_authors(project_data_set, state, start_date)}}}
> The database is on production, so I can't delete previous initial
> migrations, and this error isn't depending on database, cause I delete it
> and error is still the same.
> My solve is to first delete unique_together, then do a makemigrations and
> then migrate. After that change the field from foreign key to many to
> many field, then do a makemigrations and then migrate.
> But in this way I have 2 migrations instead of one.
>
> I added attachment with this project, download it and then do
> makemigrations and then migrate to see this error.

New description:

 I have models like

 {{{#!python
 class Authors(models.Model):
     project_data_set = models.ForeignKey(
         ProjectDataSet,
         on_delete=models.PROTECT
     )
     state = models.IntegerField()
     start_date = models.DateField()

     class Meta:
          unique_together = (('project_data_set', 'state', 'start_date'),)
 }}}

 and

 {{{
 class DataSet(models.Model):
     name = models.TextField(max_length=50)


 class Project(models.Model):
     data_sets = models.ManyToManyField(
         DataSet,
         through='ProjectDataSet',
     )
     name = models.TextField(max_length=50)


 class ProjectDataSet(models.Model):
     """
     Cross table of data set and project
     """
     data_set = models.ForeignKey(DataSet, on_delete=models.PROTECT)
     project = models.ForeignKey(Project, on_delete=models.PROTECT)

     class Meta:
         unique_together = (('data_set', 'project'),)
 }}}

 when i want to change field project_data_set in Authors model from foreign
 key field to many to many field I must delete a unique_together, cause it
 can't be on many to many field.
 Then my model should be like:

 {{{
 class Authors(models.Model):

     project_data_set = models.ManyToManyField(
         ProjectDataSet,
     )

     state = models.IntegerField()
     start_date = models.DateField()
 }}}

 But when I want to do a migrations.
 1. python3 manage.py makemigrations
 2. python3 manage.py migrate
 I have error:
 {{{ValueError: Found wrong number (0) of constraints for
 app_authors(project_data_set, state, start_date)}}}
 The database is on production, so I can't delete previous initial
 migrations, and this error isn't depending on database, cause I delete it
 and error is still the same.
 My solve is to first delete unique_together, then do a makemigrations and
 then migrate. After that change the field from foreign key to many to many
 field, then do a makemigrations and then migrate.
 But in this way I have 2 migrations instead of one.

 I added attachment with this project, download it and then do
 makemigrations and then migrate to see this error.

--

Comment (by Simon Charette):

 I agree that you'll loose data but `Alter(Index|Unique)Together` should
 always be sorted before `RemoveField`

 
https://github.com/django/django/blob/b502061027b90499f2e20210f944292cecd74d24/django/db/migrations/autodetector.py#L910
 
https://github.com/django/django/blob/b502061027b90499f2e20210f944292cecd74d24/django/db/migrations/autodetector.py#L424-L430

 So something's broken here in a few different ways and I suspect it's due
 to the fact the same field name `project_data_set` is reused for the many-
 to-many field.

 If you start from

 {{{#!python
 class Authors(models.Model):
     project_data_set = models.ForeignKey(
         ProjectDataSet,
         on_delete=models.PROTECT
     )
     state = models.IntegerField()
     start_date = models.DateField()

     class Meta:
          unique_together = (('project_data_set', 'state', 'start_date'),)
 }}}

 And generate `makemigrations` for

 {{{#!python
 class Authors(models.Model):
     project_data_set = models.ManyToManyField(ProjectDataSet)
     state = models.IntegerField()
     start_date = models.DateField()
 }}}

 You'll get two migrations with the following operations

 {{{#!python
 # 0002
 operations = [
     migrations.AddField(
         model_name='authors',
         name='project_data_set',
         field=models.ManyToManyField(to='ticket_31788.ProjectDataSet'),
     ),
     migrations.AlterUniqueTogether(
         name='authors',
         unique_together=set(),
     ),
     migrations.RemoveField(
         model_name='authors',
         name='project_data_set',
     ),
 ]

 # 0003
 operations = [
     migrations.AddField(
         model_name='authors',
         name='project_data_set',
         field=models.ManyToManyField(to='ticket_31788.ProjectDataSet'),
     ),
 ]
 }}}

 If you change the name of the field to something else like
 `project_data_sets` every work as expected

 {{{#!python
 operations = [
     migrations.AddField(
         model_name='authors',
         name='project_data_sets',
         field=models.ManyToManyField(to='ticket_31788.ProjectDataSet'),
     ),
     migrations.AlterUniqueTogether(
          name='authors',
          unique_together=set(),
     ),
     migrations.RemoveField(
         model_name='authors',
         name='project_data_set',
     ),
 ]
 }}}

-- 
Ticket URL: <https://code.djangoproject.com/ticket/31788#comment:4>
Django <https://code.djangoproject.com/>
The Web framework for perfectionists with deadlines.

-- 
You received this message because you are subscribed to the Google Groups 
"Django updates" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to [email protected].
To view this discussion on the web visit 
https://groups.google.com/d/msgid/django-updates/066.7a1081cb097139edfb0a4eaa8ad12188%40djangoproject.com.

Reply via email to