Django 双向多对多 - 如何防止在第二个模型上创建表?

17 投票
4 回答
9907 浏览
提问于 2025-04-16 11:07

我有两个模型,它们之间有一个共享的多对多关系,并且我使用了 db_table 字段来指定表名。但是我该如何阻止 syncdb 在第二个模型中尝试创建这个共享的表呢?

class Model1(models.Model):
    othermodels = ManyToManyField('Model2', db_table='model1_model2', related_name='model1_model2')

class Model2(models.model):
    othermodels = ManyToManyField('Model1', db_table='model1_model2', related_name='model2_model1')

在我的开发环境中一切运行得很好,因为在我逐步构建的过程中,有些表是分开创建的。但是从一个空的数据库开始,syncdb 报错了: _mysql_exceptions.OperationalError: (1050, "表 'model1_model2' 已经存在")

我是不是在第二个模型的字段中漏掉了什么标志,以防止重复创建表?还是说我根本就搞错了?

4 个回答

2

在Django中,多对多关系默认是双向的。也就是说,你只需要在一个模型上定义它,而不需要在两个模型上都定义(通常你也不需要给这个表起个名字):

class Model1(models.Model):
    othermodels = ManyToManyField('Model2')

class Model2(models.model):
    ...

就这样。现在 syncdb 会很高兴。想了解更多信息,可以查看:Django文档

唯一的缺点是,如果你使用后台管理系统,你只能在 Model1 中访问 othermodels

编辑

所以,如果你想在后台管理系统中同时访问两个模型的多对多关系,目前的官方解决方案是对第二个模型使用inlinemodel。几天前我也遇到了同样的问题/需求。我对inlinemodel的解决方案并不太满意(如果有很多条目,数据库查询会很重,不能使用*filter_horizontal*小部件等等)。

我找到的解决方案(适用于Django 1.2+和 syncdb)是这样的:

class Model1(models.Model):
    othermodels = models.ManyToManyField('Model2', through='Model1Model2')

class Model2(models.Model):
    othermodels = models.ManyToManyField('Model1', through='Model1Model2')

class Model1Model2(models.Model):
    model1 = models.ForeignKey(Model1)
    model2 = models.ForeignKey(Model2)

    class Meta:
        db_table = 'app_model1_model2'
        auto_created = Model1

想了解更多信息,请查看票据897

不幸的是,如果你使用South,你需要在每个自动生成的迁移文件中删除 app_model1_model2 表的创建。

6

你不需要在关系的两边都放一个 ManyToManyFieldDjango会为你处理这个问题

你可能想要的结构更像这样:

class Model1(models.Model):
    name = models.CharField(max_length=128)
    ...

class Model2(models.Model):
    name = models.CharField(max_length=128)
    othermodels = models.ManyToManyField(Model1, through='Model1Model2')
    ...        

class Membership(models.Model):
    class Meta:
        db_table = 'model1_model2'
    model1 = models.ForeignKey(Model1)        
    model2 = models.ForeignKey(Model2)

当你在使用你的模型时,Model1的一个实例会自动有一个 othermodels_set 字段,这是Django帮你加上的。而 Model2 的实例则会有 othermodels

28

我也找到了解决办法,这个方法对我来说非常有效:

class Test1(models.Model):
    tests2 = models.ManyToManyField('Test2', blank=True)

class Test2(models.Model):
    tests1 = models.ManyToManyField('Test1', through=Test1.tests2.through, blank=True)

撰写回答