django.db.utils.IntegrityError: could not create unique index - DETAIL: Key (player)=(Lonergan) is duplicated. - without unique constraint in model

django.db.utils.IntegrityError: could not create unique index - DETAIL: Key (player)=(Lonergan) is duplicated. - without unique constraint in model

在几次离线提交(包括几次更新模型和部署到生产环境)之后,错误消息是:(正在开发中但在生产环境中失败)

django.db.utils.IntegrityError: could not create unique index - DETAIL:  Key (player)=(Lonergan) is duplicated.

table 中有超过 100 名玩家重名,并且从未将现场玩家设置为唯一。为什么会这样?

class Player(models.Model):
    player = models.CharField(max_length=50)
    team = models.ForeignKey(Team, related_name='players', on_delete=models.PROTECT)
    position = models.CharField(max_length=5)
    cost = models.FloatField()
    selection = models.FloatField()
    form = models.FloatField()
    points = models.IntegerField()
    lastgwscrape = models.DateTimeField(null=True)
    lastapiupdate = models.DateTimeField(null=True)
    currentgwdt = models.DateTimeField(null=True)
    apiid = models.IntegerField(null=True)

apiid 字段之前被定义为唯一的,并且作为使此迁移工作的测试被删除。

class APIPlayerGW(models.Model):
    player = models.ForeignKey(Player, related_name='apigws', on_delete=models.CASCADE)
    gwid = models.IntegerField()
    points = models.IntegerField()
    minutesplayed = models.IntegerField()
    goalsscored = models.IntegerField()
    assists = models.IntegerField()
    cleansheets = models.IntegerField()
    goalsconceded = models.IntegerField()
    owngoals = models.IntegerField()
    penaltiessaved = models.IntegerField()
    penaltiesmissed = models.IntegerField()
    yellowcards = models.IntegerField()
    redcards = models.IntegerField()
    saves = models.IntegerField()
    bonuspoints = models.IntegerField()
    bonuspointsystem = models.IntegerField()
    influence = models.FloatField()
    creativity = models.FloatField()
    threat = models.FloatField()
    ictindex = models.FloatField()
    datetime = models.DateTimeField(default=timezone.now)
    season = models.CharField(max_length=10)

    # class Meta:
    #     unique_together = ('player','gwid','season') # Double gameweeks are lumped together

上面的 table 有 unique_together 组合被注释掉作为使这个迁移工作的测试。我该怎么做才能使此迁移成功?

尝试在 unique_together 中使用 player__id 而不是 player,因为它是 ForeignKey,因此仅使用 id 更有意义。如果它仍然不起作用,那么理想情况下两个玩家的 id 是相同的。

迁移文件与迁移不同步,Django 正在尝试使用旧的迁移文件。 @Abdul Aziz Barkat 的评论将我引向了正确的方向。可以通过以下步骤重置 Django 迁移来解决此问题:

  1. 离线注释掉 Models.py 中的所有更改以反映生产数据库的状态
  2. 投入生产
  3. 运行 python manage.py migrate --fake app_name zero
  4. 导航到您的生产应用中的迁移文件夹,并使用 find . -path "*.py" -not -name "__init__.py" -deletefind . -path "*.pyc" -delete
  5. 删除旧的迁移文件
  6. 创建新的初始迁移python manage.py makemigrations
  7. 因为数据库表已经存在 运行 python manage.py migrate --fake-initial

我收到一条错误消息 运行 --fake-initial 标志,但是用 --fake 标志替换它是有效的:python manage.py migrate --fake

根据您的评论:

The migration files had come out of sync with the migrations and Django was trying to use an old migration file.

我假设发生了类似以下的事情(或者可能还有其他情况,比如一些迁移文件被手动编辑等):

  1. 您的项目在某些版本控制中。
  2. 有些人在本地生成迁移并推送到版本控制(他们的名字以相同的前缀开头,例如 0003),不幸的是没有检测到,也许后来在这些之上生成了更多迁移一个。

我会建议以下 3 种方法来解决此类问题:

1。使用 --merge

合并迁移

通常,解决此类情况的第一件事就是尝试使用 --merge flag [Django docs] 来合并冲突。如果冲突不是很复杂,在大多数情况下,Django 将能够通过此命令为您修复它。

2。回滚迁移并重新生成它们

下一个应该尝试的解决方案(而不是直接在生产服务器上进行迁移)是通过回滚到与生产服务器状态一致的某个先前迁移来在本地修复迁移。假设生产正常迁移到 0005,我们将执行如下操作(在开发服务器上):

  1. 迁移回 0005:

     python manage.py migrate <app_label> 0005
    
  2. 删除 0005 之后的所有迁移,或者有选择地删除一些迁移,或者重命名一些迁移,以便它们按顺序排列。

  3. 再次生成迁移:

     python manage.py makemigrations
    
  4. 将这些迁移推送到生产环境并迁移。

3。手动编辑迁移

如果这些步骤不可行,那么可以参考 Writing database migrations.

上的文档,尝试自己实际编辑迁移文件来解决问题。

注意:尽管您的解决方案(在生产环境中修复迁移)效果很好,但很多人不允许这样做,如果我们以后这样做也会导致问题有多个生产服务器,因为在这种情况下它们之间的迁移可能不一致。