You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Django+SpatiaLite含PolygonField模型迁移IntegrityError问题求解

解决Django含PolygonField模型迁移的IntegrityError错误

修改包含PolygonField的Django模型(对应数据库表)时,迁移操作终止并抛出以下错误:

django.db.utils.IntegrityError: The row in table 'geometry_columns_statistics' with primary key 'new__mc_sitio_benfeitoria' has an invalid foreign key: geometry_columns_statistics.f_table_name contains a value 'new__mc_sitio_benfeitoria' that does not have a corresponding value in geometry_columns.f_table_name.

我的应用名为MC_SITIO,相关模型代码如下:

class ImovelBase(ModelGis):
    """Base para Imóveis"""

    descricao        = CharField(max_length=255, blank=False, null=True, verbose_name='descrição')
    administrador    = ForeignKey(Pessoa, on_delete=SET_NULL, blank=True, null=True)
    RIP              = CharField(max_length=255, blank=True, null=True, help_text='se for imóvel da União')
    codigo           = CharField(max_length=255, blank=True, null=True, verbose_name='código, matricula, anúncio, ou outro documento')
    endereco         = ForeignKey(Endereco, on_delete=SET_NULL, blank=True, null=True, verbose_name = 'endereço')
    poligono         = PolygonField(srid=EPSG_SRID_PADRAO, blank=True, null=True, verbose_name='polígono')
    # area_total       = UnumCharField(valid_unit=m**2, max_length=255, null=True, blank=True, verbose_name='área total', validators = [MinValueValidator(0.0)])
    area_total       = SympyCharField(max_length=255, null=True, blank=True, verbose_name='área total')
    VUP              = DecimalField(null=True, blank=True, verbose_name='valor unitário padrão (VUP)', max_digits=11, decimal_places=2, validators = [MinValueValidator(0.0)])
    indice_fiscal    = DecimalField(null=True, blank=True, verbose_name='índice fiscal', max_digits=11, decimal_places=2, validators = [MinValueValidator(0.0)])
    em_condominio    = BooleanField(default=False, blank=True, verbose_name='em condomínio')
    observacoes      = TextField(null=True, blank=True, verbose_name='observações')

    class Meta:
        abstract = True

    def __str__(self):
        return "{0}".format(self.descricao)

    def area(self):
        if self.poligono:
            return self.poligono.transform(get_SRID_UTM_WGS84(self.poligono.centroid), clone=True).area*m**2
        return "S/N"

    @property
    def popupConteudo(self):
        return '<a href="/terreno-{}.html" target="_blank">{}</a>'.format(self.id, self.descricao)

class TerrenoBase(ImovelBase):
    """Base para Terreno, tombo"""

    relevo           = CharField(choices=RelevoClasse.choices, max_length=255, blank=True, null=True, verbose_name='classe do relevo')
    forma_regular    = BooleanField(default=False, blank=True, verbose_name='forma regular?')
    esquinas_qtd     = PositiveSmallIntegerField(blank=True, null=True, verbose_name="quantidade de esquinas")
    # testada          = UnumCharField(valid_unit=m, max_length=255, null=True, blank=True)
    testada          = SympyCharField(max_length=255, null=True, blank=True)
    testadas_qtd     = PositiveSmallIntegerField(blank=True, null=True, verbose_name="quantidade de testadas")
    # gabarito         = UnumCharField(valid_unit=m, max_length=255, null=True, blank=True)
    gabarito         = SympyCharField(max_length=255, null=True, blank=True)
    macrozoneamento  = CharField(max_length=255, null=True, blank=True)
    zoneamento       = CharField(max_length=255, null=True, blank=True)
    tombamentos      = ManyToManyField(Referencia, blank=True, verbose_name='tombamento(s)')

    class Meta:
        abstract = True

class Terreno(TerrenoBase):
    """Terreno, tombo"""

    pass

class Benfeitoria(ImovelBase):
    """Benfeitoria, casa, prédio, galpão, ponte, edificação, ou qualquer outra construção permanente sobre um terreno"""

    terreno       = ForeignKey(Terreno, on_delete=SET_NULL, blank=False, null=True)
    tipob         = PositiveSmallIntegerField(choices=BenfeitoriaTipo.choices, blank=False, null=True, verbose_name='tipo da benfeitoria')
    pav_desc      = CharField(max_length=255, null=True, blank=True, verbose_name='descrição dos pavimentos', help_text='T: térreo, PT: pav. tipo, LJ: loja, C: cobertura, etc')
    rr_desc       = CharField(choices=RossHeidecke.choices, max_length=255, null=True, blank=True, verbose_name='Critério de Ross-Heidecke')
    vida_util     = CharField(choices=BenfeitoriaVidaUtilPadrao.choices, max_length=255, blank=True, null=True, verbose_name='vida útil padrão')

解决步骤

这个错误源于PostGIS的geometry_columns_statistics表残留了迁移临时表(带new__前缀)的记录,临时表已被迁移操作删除,导致外键约束失效。按以下步骤修复:

  • 连接目标数据库:用psql、pgAdmin等工具连接到Django项目对应的PostgreSQL数据库。
  • 清理无效统计记录:执行SQL删除无效条目:
    DELETE FROM geometry_columns_statistics WHERE f_table_name = 'new__mc_sitio_benfeitoria';
    
  • 重新执行迁移:回到项目目录,运行迁移命令:
    python manage.py migrate
    
  • 预防后续问题(可选):若频繁出现此类问题,可在迁移前手动清理临时表相关统计记录,或检查Django与PostGIS版本兼容性,避免已知迁移bug。

内容的提问来源于stack exchange,提问作者Memória de Cálculo

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.16 15:01:08