如何在 MySQL 中拥有涉及外键字段的 unique_together 约束?

发布于 2024-11-30 19:55:18 字数 3267 浏览 1 评论 0原文

MySQL 似乎在外键的多列唯一约束方面有些问题。这是我能想到的最小示例来展示这一点(使用 MySQL/InnoDB):

models.py

from django.db import models

class Team(models.Model):
    pass

class Player(models.Model):
    team = models.ForeignKey(Team)
    number = models.PositiveIntegerField()

    class Meta:
        unique_together = ("team", "number")

运行 schemamigration --initial,south 会输出以下迁移(仅重要部分):

class Migration(SchemaMigration):                                                                                                                                                
    def forwards(self, orm):
        # Adding model 'Team'
        db.create_table('fkuniq_team', (
            ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
        ))
        db.send_create_signal('fkuniq', ['Team'])

        # Adding model 'Player'
        db.create_table('fkuniq_player', (
            ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
            ('team', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['fkuniq.Team'])),
            ('number', self.gf('django.db.models.fields.PositiveIntegerField')()),
        ))
        db.send_create_signal('fkuniq', ['Player'])

        # Adding unique constraint on 'Player', fields ['team', 'number']
        db.create_unique('fkuniq_player', ['team_id', 'number'])

在 MySQL 中:

mysql> SHOW COLUMNS FROM fkuniq_player;
+---------+------------------+------+-----+---------+----------------+
| Field   | Type             | Null | Key | Default | Extra          |
+---------+------------------+------+-----+---------+----------------+
| id      | int(11)          | NO   | PRI | NULL    | auto_increment |
| team_id | int(11)          | NO   | MUL | NULL    |                |
| number  | int(10) unsigned | NO   |     | NULL    |                |
+---------+------------------+------+-----+---------+----------------+

我认为南默默地未能创造出我想要的独特约束。在 Key 列中,我看到 id 上的主键索引和 team_id 上的外键索引,但还应该有一个 MULnumber 行中,因为上面应该有一个带有 team_idUNIQUE 索引。此外,从模型中删除 unique_together 约束会导致下一次迁移失败并出现错误:

Traceback (most recent call last):
  ...
  File "/home/aogier/uniques/../uniques/fkuniq/migrations/0002_auto__del_unique_player_number_team.py", line 12, in forwards
    db.delete_unique('fkuniq_player', ['number', 'team_id'])
  File "/home/aogier/.virtualenvs/uniques/lib/python2.7/site-packages/south/db/generic.py", line 479, in delete_unique
    raise ValueError("Cannot find a UNIQUE constraint on table %s, columns %r" % (table_name, columns))
ValueError: Cannot find a UNIQUE constraint on table fkuniq_player, columns ['number', 'team_id']

我相信它丢失了,因为当外键约束和多列 UNIQUE 时 MySQL 无法正常运行 约束条件一致。 MySQL 文档中对 ALTER TABLE 有这样的评论:http://dev.mysql.com/doc/refman/5.1/en/alter-table.html(参见中间部分,Hadi Rastgou 的评论)。

不管怎样,很抱歉问了这么长的问题:有人有办法做到这一点吗?我希望有一种干净的方式在迁移中执行此操作,即使我必须在原始 SQL 中编写特定于 MySQL 的查询。或者也许这在 MySQL 中是根本不可能做到的,在我花更多时间研究这个问题之前了解一下这一点会很有好处。

MySQL seems to be sort of broken with regards to multi-column unique constraints with foreign keys. Here's the smallest example I can come up with to show this (using MySQL/InnoDB):

models.py

from django.db import models

class Team(models.Model):
    pass

class Player(models.Model):
    team = models.ForeignKey(Team)
    number = models.PositiveIntegerField()

    class Meta:
        unique_together = ("team", "number")

Running schemamigration --initial, south spits out the following migration (important bits only):

class Migration(SchemaMigration):                                                                                                                                                
    def forwards(self, orm):
        # Adding model 'Team'
        db.create_table('fkuniq_team', (
            ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
        ))
        db.send_create_signal('fkuniq', ['Team'])

        # Adding model 'Player'
        db.create_table('fkuniq_player', (
            ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
            ('team', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['fkuniq.Team'])),
            ('number', self.gf('django.db.models.fields.PositiveIntegerField')()),
        ))
        db.send_create_signal('fkuniq', ['Player'])

        # Adding unique constraint on 'Player', fields ['team', 'number']
        db.create_unique('fkuniq_player', ['team_id', 'number'])

And in MySQL:

mysql> SHOW COLUMNS FROM fkuniq_player;
+---------+------------------+------+-----+---------+----------------+
| Field   | Type             | Null | Key | Default | Extra          |
+---------+------------------+------+-----+---------+----------------+
| id      | int(11)          | NO   | PRI | NULL    | auto_increment |
| team_id | int(11)          | NO   | MUL | NULL    |                |
| number  | int(10) unsigned | NO   |     | NULL    |                |
+---------+------------------+------+-----+---------+----------------+

I think south silently failed to create the unique constraint I wanted. In the Key column I see the primary key index on id and the foreign key index on team_id but there should also be a MUL in the number row because there should be a UNIQUE index on it with team_id. Also, removing the unique_together constraint from the model causes the next migration to fail with the error:

Traceback (most recent call last):
  ...
  File "/home/aogier/uniques/../uniques/fkuniq/migrations/0002_auto__del_unique_player_number_team.py", line 12, in forwards
    db.delete_unique('fkuniq_player', ['number', 'team_id'])
  File "/home/aogier/.virtualenvs/uniques/lib/python2.7/site-packages/south/db/generic.py", line 479, in delete_unique
    raise ValueError("Cannot find a UNIQUE constraint on table %s, columns %r" % (table_name, columns))
ValueError: Cannot find a UNIQUE constraint on table fkuniq_player, columns ['number', 'team_id']

I believe it is missing because MySQL doesn't play well when foreign key constraints and multi-column UNIQUE constraints coincide. There's a comment to that effect on the MySQL documentation for ALTER TABLE: http://dev.mysql.com/doc/refman/5.1/en/alter-table.html (see about halfway down, comment by Hadi Rastgou).

Anyways, sorry for the long question: does anyone have a way to make this work? I'd love a clean way to do this within a migration, even if I have to write in a MySQL-specific query in raw SQL. Or maybe this is just flat-out impossible to do in MySQL, which would be good to know before I spend more time working on this.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

机场等船 2024-12-07 19:55:18

啊,我自己解决了这个问题。这是南方的一个已知错误,已在他们的开发分支中修复。 http://south.aeracode.org/ticket/747

Argh, fixed this myself. It's a known bug in south, fixed in their development branch. http://south.aeracode.org/ticket/747

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文