Django 双向多对多 - 如何防止在第二个模型上创建表?
我有两个模型,它们之间有一个共享的多对多关系,并且我使用了 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 个回答
在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
表的创建。
你不需要在关系的两边都放一个 ManyToManyField
。Django会为你处理这个问题。
你可能想要的结构更像这样:
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
。
我也找到了解决办法,这个方法对我来说非常有效:
class Test1(models.Model):
tests2 = models.ManyToManyField('Test2', blank=True)
class Test2(models.Model):
tests1 = models.ManyToManyField('Test1', through=Test1.tests2.through, blank=True)