#23030 closed Bug (fixed)
Geo model table rename (in migrations) with spatialite UNIQUE constraint failed
Reported by: | Lucio Asnaghi | Owned by: | Claude Paroz |
---|---|---|---|
Component: | GIS | Version: | 1.7-rc-1 |
Severity: | Release blocker | Keywords: | gis spatialite sqlite geometry |
Cc: | Triage Stage: | Accepted | |
Has patch: | yes | Needs documentation: | no |
Needs tests: | no | Patch needs improvement: | no |
Easy pickings: | no | UI/UX: | no |
Description
I explain my working test:
- create a virtualenv
- install django 1.7c1
- create a project
- create an app and include to INSTALLED_APPS
- change the database to use 'django.contrib.gis.db.backends.spatialite'
- now go to models.py of the created app and type:
from django.db import models from django.contrib.gis.db import models as geomodels from django.contrib.contenttypes.models import ContentType from django.contrib.contenttypes.fields import GenericForeignKey class Institute(models.Model): pass class Company(models.Model): pass class Motivation(models.Model): pass class Manumission(geomodels.Model): institute = models.ForeignKey(Institute, blank=True, null=True) company = models.ForeignKey(Company, blank=True, null=True) motivation = models.ForeignKey(Motivation, blank=True, null=True) elements = geomodels.GeometryField(null=True, blank=True) objects = geomodels.GeoManager()
- then go into a shell and enter the virtualenv and issue:
spatialite db.sqlite3 "SELECT InitSpatialMetaData();" ./manage.py makemigrations appname ./manage.py syncdb --noinput
The result is this:
SpatiaLite version ..: 3.0.1 Supported Extensions: - 'VirtualShape' [direct Shapefile access] - 'VirtualDbf' [direct DBF access] - 'VirtualXL' [direct XLS access] - 'VirtualText' [direct CSV/TXT access] - 'VirtualNetwork' [Dijkstra shortest path] - 'RTree' [Spatial Index - R*Tree] - 'MbrCache' [Spatial Index - MBR cache] - 'VirtualSpatialIndex' [R*Tree metahandler] - 'VirtualFDO' [FDO-OGR interoperability] - 'SpatiaLite' [Spatial SQL - OGC] PROJ.4 version ......: Rel. 4.8.0, 6 March 2012 GEOS version ........: 3.4.2-CAPI-1.8.2 r3921 the SPATIAL_REF_SYS table already contains some row(s) InitSpatiaMetaData ()error:"table spatial_ref_sys already exists" 0 Migrations for 'gis': 0001_initial.py: - Create model Company - Create model Institute - Create model Manumission - Create model Motivation - Add field motivation to manumission Operations to perform: Apply all migrations: admin, contenttypes, gis, auth, sessions Running migrations: Applying contenttypes.0001_initial... OK Applying auth.0001_initial... OK Applying admin.0001_initial... OK Applying gis.0001_initial...Traceback (most recent call last): File "./manage.py", line 10, in <module> execute_from_command_line(sys.argv) File "/home/x/djangofault/lib/python2.7/site-packages/django/core/management/__init__.py", line 385, in execute_from_command_line utility.execute() File "/home/x/djangofault/lib/python2.7/site-packages/django/core/management/__init__.py", line 377, in execute self.fetch_command(subcommand).run_from_argv(self.argv) File "/home/x/djangofault/lib/python2.7/site-packages/django/core/management/base.py", line 288, in run_from_argv self.execute(*args, **options.__dict__) File "/home/x/djangofault/lib/python2.7/site-packages/django/core/management/base.py", line 337, in execute output = self.handle(*args, **options) File "/home/x/djangofault/lib/python2.7/site-packages/django/core/management/base.py", line 532, in handle return self.handle_noargs(**options) File "/home/x/djangofault/lib/python2.7/site-packages/django/core/management/commands/syncdb.py", line 27, in handle_noargs call_command("migrate", **options) File "/home/x/djangofault/lib/python2.7/site-packages/django/core/management/__init__.py", line 115, in call_command return klass.execute(*args, **defaults) File "/home/x/djangofault/lib/python2.7/site-packages/django/core/management/base.py", line 337, in execute output = self.handle(*args, **options) File "/home/x/djangofault/lib/python2.7/site-packages/django/core/management/commands/migrate.py", line 160, in handle executor.migrate(targets, plan, fake=options.get("fake", False)) File "/home/x/djangofault/lib/python2.7/site-packages/django/db/migrations/executor.py", line 62, in migrate self.apply_migration(migration, fake=fake) File "/home/x/djangofault/lib/python2.7/site-packages/django/db/migrations/executor.py", line 96, in apply_migration migration.apply(project_state, schema_editor) File "/home/x/djangofault/lib/python2.7/site-packages/django/db/migrations/migration.py", line 107, in apply operation.database_forwards(self.app_label, schema_editor, project_state, new_state) File "/home/x/djangofault/lib/python2.7/site-packages/django/db/migrations/operations/fields.py", line 37, in database_forwards field, File "/home/x/djangofault/lib/python2.7/site-packages/django/contrib/gis/db/backends/spatialite/schema.py", line 82, in add_field super(SpatialiteSchemaEditor, self).add_field(model, field) File "/home/x/djangofault/lib/python2.7/site-packages/django/db/backends/sqlite3/schema.py", line 143, in add_field self._remake_table(model, create_fields=[field]) File "/home/x/djangofault/lib/python2.7/site-packages/django/db/backends/sqlite3/schema.py", line 125, in _remake_table self.alter_db_table(model, temp_model._meta.db_table, model._meta.db_table) File "/home/x/djangofault/lib/python2.7/site-packages/django/contrib/gis/db/backends/spatialite/schema.py", line 95, in alter_db_table "new_table": self.quote_name(new_db_table), File "/home/x/djangofault/lib/python2.7/site-packages/django/db/backends/schema.py", line 98, in execute cursor.execute(sql, params) File "/home/x/djangofault/lib/python2.7/site-packages/django/db/backends/utils.py", line 81, in execute return super(CursorDebugWrapper, self).execute(sql, params) File "/home/x/djangofault/lib/python2.7/site-packages/django/db/backends/utils.py", line 65, in execute return self.cursor.execute(sql, params) File "/home/x/djangofault/lib/python2.7/site-packages/django/db/utils.py", line 94, in __exit__ six.reraise(dj_exc_type, dj_exc_value, traceback) File "/home/x/djangofault/lib/python2.7/site-packages/django/db/backends/utils.py", line 65, in execute return self.cursor.execute(sql, params) File "/home/x/djangofault/lib/python2.7/site-packages/django/db/backends/sqlite3/base.py", line 485, in execute return Database.Cursor.execute(self, query, params) django.db.utils.IntegrityError: UNIQUE constraint failed: geometry_columns.f_table_name, geometry_columns.f_geometry_column
In fact the migration created seems strange (why the 3rd foreign key is issued separately, so a table rename is needed):
# -*- coding: utf-8 -*- from __future__ import unicode_literals from django.db import models, migrations import django.contrib.gis.db.models.fields class Migration(migrations.Migration): dependencies = [ ] operations = [ migrations.CreateModel( name='Company', fields=[ ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), ], options={ }, bases=(models.Model,), ), migrations.CreateModel( name='Institute', fields=[ ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), ], options={ }, bases=(models.Model,), ), migrations.CreateModel( name='Manumission', fields=[ ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), ('elements', django.contrib.gis.db.models.fields.GeometryField(srid=4326, null=True, blank=True)), ('company', models.ForeignKey(blank=True, to='gis.Company', null=True)), ('institute', models.ForeignKey(blank=True, to='gis.Institute', null=True)), ], options={ }, bases=(models.Model,), ), migrations.CreateModel( name='Motivation', fields=[ ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), ], options={ }, bases=(models.Model,), ), migrations.AddField( model_name='manumission', name='motivation', field=models.ForeignKey(blank=True, to='gis.Motivation', null=True), preserve_default=True, ), ]
Also, base=(models.Model,) of 'Manumission' model does not respect django.contrib.gis.db.Model.
Attachments (2)
Change History (13)
comment:1 by , 10 years ago
comment:2 by , 10 years ago
It seems that geometry_columns spatialite table is populated with 2 tables
Applying gis.0001_initial... SELECT AddGeometryColumn('gis_first', 'objs', 4326, 'GEOMETRY', 2, 0) SELECT CreateSpatialIndex("gis_first", "objs") SELECT AddGeometryColumn('gis_first__new', 'objs', 4326, 'GEOMETRY', 2, 0) SELECT CreateSpatialIndex("gis_first__new", "objs") UPDATE geometry_columns SET f_table_name = "gis_first" WHERE f_table_name = "gis_first__new"
then the update is obviously failing cause in the geometry_columns table there are already 2 entries...
comment:3 by , 10 years ago
Summary: | Migration code not working with GeometryField and more than 2 foreign keys → Geo model table rename (in migrations) with spatialite UNIQUE constraint failed |
---|
comment:4 by , 10 years ago
Has patch: | set |
---|
comment:5 by , 10 years ago
Needs tests: | set |
---|---|
Triage Stage: | Unreviewed → Accepted |
Could you add a regression test?
comment:6 by , 10 years ago
well, actually my patch don't work as expected, there are some edge cases that make it not viable solution.
regarding regression test, i'm not that practical with writing django test actually, but i can see what i can do.
comment:7 by , 10 years ago
last patch will work correctly with migrations without leaving the database in a inconsistent state.
i don't know if DatabaseSchema.alter_db_table is called also outside of migrations, so i don't know if it will work in any corner case.
will try to write a test for this
by , 10 years ago
Attachment: | spatialite_migrations_regression.patch added |
---|
It's possible to showcase the error with this test
comment:8 by , 10 years ago
Owner: | changed from | to
---|---|
Status: | new → assigned |
comment:10 by , 10 years ago
Resolution: | → fixed |
---|---|
Status: | assigned → closed |
Test case should be shortened to: