Search code examples
pythondjangomigrationdatabase-migrationgeodjango

Geodjango: Change PolygonField to MultiPolygonField with exisiting data using a migration


I have a model with a PolygonField, which had dozens of rows. I am trying to change the field to MultiPolygonField, but data is still in polygon mode. How can I convert all existing data to the new type?

models.py:

class Region(models.Model):
    class Meta:
        verbose_name = _("region")
        verbose_name_plural = _("regions")

    polygon = models.PolygonField(_("polygon"))  # <== this is going to be MultiPolygon
    name = models.CharField(_("name"), max_length=100)

Solution

  • Option 1: Use SQL

    Assuming you are using postgis, the following SQL can convert your data by using ST_Multi:

    ALTER TABLE myapp_region
        ALTER COLUMN polygon TYPE geometry(MultiPolygon, 4326)
        USING ST_Multi(polygon);
    

    Do not execute this directly, instead run makemigrations which will create a new migration (for example: myapp/migrations/0006_yyyyyyyy.py) similar to:

    import django.contrib.gis.db.models.fields
    from django.db import migrations
    
    
    class Migration(migrations.Migration):
    
        dependencies = [
            ('myapp', '0005_zzzzzzzzzzzzz'),
        ]
    
        operations = [
            migrations.AlterField(
                model_name='region',
                name='polygon',
                field=django.contrib.gis.db.models.fields.MultiPolygonField(srid=4326),
            ),
        ]
    

    Encapsulate your AlterField opertaions with a RunSQL operation like this:

    operations = [
        migrations.RunSQL(
            "ALTER TABLE myapp_region ALTER COLUMN polygon type geometry(MultiPolygon, 4326) using ST_Multi(polygon);",
            state_operations=[
                migrations.AlterField(
                    model_name='region',
                    name='polygon',
                    field=django.contrib.gis.db.models.fields.MultiPolygonField(
                        srid=4326),
                ),
            ],
        )
    ]
    

    and now run your migration.

    Option 2: Data Migrations

    • Create a new geom = models.MultiPolygonField(null=True) field next to your current field.
    • Create and run a migration to add the new multipolygon field.
    • Create a data migration and use RunPython to copy your data:

      for o in Region.objects.all():
          o.geom = MultiPolygon([o.polygon])
          o.save()
      

      (Untested: You might be able to use Region.objects.update() instead)

    • In models.py, delete the original field and remove null=True; create a migration.
    • Optional: rename geom to polygon with another migration.