#27860: Changing a CharField to a ForeignKey explodes when migrating in 
PostgreSQL
-------------------------------+--------------------------------------
     Reporter:  Daniel Quinn   |                    Owner:  nobody
         Type:  Uncategorized  |                   Status:  new
    Component:  Migrations     |                  Version:  1.10
     Severity:  Normal         |               Resolution:
     Keywords:  PostgreSQL     |             Triage Stage:  Unreviewed
    Has patch:  0              |      Needs documentation:  0
  Needs tests:  0              |  Patch needs improvement:  0
Easy pickings:  0              |                    UI/UX:  0
-------------------------------+--------------------------------------

Old description:

> If I have a model that looks like this:
>
> {{{
> class MyModel(models.Model):
>     my_field = models.CharField(max_length=128, db_index=True)
> }}}
>
> `makemigrations` will create a migration that looks like this:
>
> {{{
>         migrations.CreateModel(
>             name='MyModel',
>             fields=[
>                 ('id', models.AutoField(auto_created=True,
> primary_key=True, serialize=False, verbose_name='ID')),
>                 ('my_field', models.CharField(db_index=True,
> max_length=128)),
>             ],
>         ),
> }}}
>
> However, if I later change the field to a `ForeignKey`:
>
> {{{
> class MyModel(models.Model):
>     my_field = models.ForeignKey("alpha.MyOtherModel", blank=True)
> }}}
>
> `makemigrations` will create this:
>
> {{{
>         migrations.AlterField(
>             model_name='mymodel',
>             name='my_field',
>             field=models.ForeignKey(blank=True,
> on_delete=django.db.models.deletion.CASCADE, to='alpha.MyOtherModel'),
>         ),
> }}}
>
> ...which explodes in PostgreSQL (but not SQLite or MySQL) with this:
>
> {{{
> psycopg2.ProgrammingError: operator class "varchar_pattern_ops" does not
> accept data type integer
> }}}
>
> The fix (at least for my case) was to manually break up the `AlterField`
> into separate `RemoveField` and `AddField` steps like this:
>
> {{{
>         migrations.RemoveField(
>             model_name='mymodel',
>             name='my_field',
>         ),
>         migrations.AddField(
>             model_name='mymodel',
>             name='my_field',
>             field=models.ForeignKey(blank=True,
> on_delete=django.db.models.deletion.CASCADE, to='alpha.MyOtherModel'),
>         ),
> }}}
>
> I ran into this on my own GPL project, and the issue history wherein we
> found and fixed the problem is here:
> https://github.com/danielquinn/paperless/issues/183

New description:

 If I have a model that looks like this:

 {{{
 class MyModel(models.Model):
     my_field = models.CharField(max_length=128, db_index=True)
 }}}

 `makemigrations` will create a migration that looks like this:

 {{{
         migrations.CreateModel(
             name='MyModel',
             fields=[
                 ('id', models.AutoField(auto_created=True,
 primary_key=True, serialize=False, verbose_name='ID')),
                 ('my_field', models.CharField(db_index=True,
 max_length=128)),
             ],
         ),
 }}}

 However, if I later change the field to a `ForeignKey`:

 {{{
 class MyOtherModel(models.Model):
     stuff = models.CharField(max_length=128)

 class MyModel(models.Model):
     my_field = models.ForeignKey("alpha.MyOtherModel", blank=True)
 }}}

 `makemigrations` will create this:

 {{{
         migrations.AlterField(
             model_name='mymodel',
             name='my_field',
             field=models.ForeignKey(blank=True,
 on_delete=django.db.models.deletion.CASCADE, to='alpha.MyOtherModel'),
         ),
 }}}

 ...which explodes in PostgreSQL (but not SQLite or MySQL) with this:

 {{{
 psycopg2.ProgrammingError: operator class "varchar_pattern_ops" does not
 accept data type integer
 }}}

 The fix (at least for my case) was to manually break up the `AlterField`
 into separate `RemoveField` and `AddField` steps like this:

 {{{
         migrations.RemoveField(
             model_name='mymodel',
             name='my_field',
         ),
         migrations.AddField(
             model_name='mymodel',
             name='my_field',
             field=models.ForeignKey(blank=True,
 on_delete=django.db.models.deletion.CASCADE, to='alpha.MyOtherModel'),
         ),
 }}}

 I ran into this on my own GPL project, and the issue history wherein we
 found and fixed the problem is here:
 https://github.com/danielquinn/paperless/issues/183

--

Comment (by Daniel Quinn):

 `alpha.MyOtherModel` is using a default numeric key.  Sorry, I should have
 included that in the ticket and so I've modified it to include the
 `MyOtherModel` class.

 The problem is that migrations is doing an `alter` for a field that
 changed not just from a `CharField` to a `ForeignKeyField` but rather to a
 foreign key of a different type.  SQLite and MySQL somehow didn't have a
 problem with this, but PostgreSQL exploded.

 Perhaps a solution would be a warning stage not unlike the prompt you get
 when trying to add a field with no `default=` value?  Something like:

 > It looks like you've changed a CharField to a ForeignKey of a different
 type.  This will generate an AddField and RemoveField.  Is that cool with
 you?

--
Ticket URL: <https://code.djangoproject.com/ticket/27860#comment:2>
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 post to this group, send email to [email protected].
To view this discussion on the web visit 
https://groups.google.com/d/msgid/django-updates/069.18b9e982b8a05c8f4e7f808c245de013%40djangoproject.com.
For more options, visit https://groups.google.com/d/optout.

Reply via email to