我对这个问题的理解是,多对多关系应该是一种方式,如果以下情况属实:
-------------- ----------------
| mymodelone |---------->| mymodeltwo |
-------------- | ----------------
|
| ----------------
----->| mymodelthree |
----------------
那么在另一个方向上不应该有隐含的关系:
-------------- ----------------
| mymodelone |<-----/----| mymodeltwo |
-------------- ----------------
-------------- ----------------
| mymodelone |<-----/----| mymodelthree |
-------------- ----------------
ManyToMany
fields 有一个symmetrical
属性,默认情况下是True
,请参阅:here。
要创建一个新应用程序来演示非对称多对多字段:
创建一个新应用程序:
$ python ./manage.py startapp stuff
添加东西应用程序settings.py
:
...
INSTALLED_APPS = (
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'stuff'
)
....
编辑`stuff/models.py:
from django.db import models
class MyModel(models.Model):
stuff = models.ManyToManyField('self', related_name = 'combined+', symmetrical=False, blank = True, null = True, verbose_name = "description")
def __unicode__(self):
return "MyModel%i" % self.id
同步数据库:
$ python ./manage.py syncdb
Creating tables ...
Creating table stuff_mymodel_stuff
Creating table stuff_mymodel
Installing custom SQL ...
Installing indexes ...
Installed 0 object(s) from 0 fixture(s)
然后在 django shell 中测试:
$ python ./manage.py shell
>>> from stuff.models import MyModel
>>> MyModel().save()
>>> MyModel().save()
>>> MyModel().save()
>>> MyModel.objects.all()
[<MyModel: MyModel1>, <MyModel: MyModel2>, <MyModel: MyModel3>]
>>> m1 = MyModel.objects.get(pk=1)
>>> m2 = MyModel.objects.get(pk=2)
>>> m3 = MyModel.objects.get(pk=3)
>>> m1.stuff.all()
[]
>>> m1.stuff.add(m2)
>>> m1.stuff.add(m3)
>>> m1.stuff.all()
[<MyModel: MyModel2>, <MyModel: MyModel3>]
>>> m2.stuff.all()
[]
>>> m3.stuff.all()
[]
>>>
编辑 - 现有模型上的多对多关系
模型的对称性ManyToManyField
是在将模型写入数据库时创建的,而不是在读取它们时创建的。如果我们将模型更改为:
from django.db import models
class MyModel(models.Model):
stuff = models.ManyToManyField('self', related_name = 'combined+')
def __unicode__(self):
return "MyModel%i" % self.id
创建新MyModel
实例:
>>> MyModel().save()
>>> MyModel().save()
>>> MyModel.objects.all()
[<MyModel: MyModel1>, <MyModel: MyModel2>, <MyModel: MyModel3>, <MyModel: MyModel4>, <MyModel: MyModel5>]
>>> m4 = MyModel.objects.get(pk=4)
>>> m5 = MyModel.objects.get(pk=5)
>>> m4.stuff.add(m5)
>>> m4.stuff.all()
[<MyModel: MyModel5>]
>>> m5.stuff.all()
[<MyModel: MyModel4>]
正如预期的那样,这些东西ManyToManyField
正在创建对称关系。如果我们然后将其设置ManyToManyField
为symmetrical = False
:
>>> from stuff.models import MyModel
>>> MyModel().save()
>>> MyModel().save()
>>> MyModel.objects.all()
[<MyModel: MyModel1>, <MyModel: MyModel2>, <MyModel: MyModel3>, <MyModel: MyModel4>, <MyModel: MyModel5>, <MyModel: MyModel6>, <MyModel: MyModel7>]
>>> m6 = MyModel.objects.get(pk=6)
>>> m7 = MyModel.objects.get(pk=7)
>>> m6.stuff.all()
[]
>>> m7.stuff.all()
[]
>>> m6.stuff.add(m7)
>>> m6.stuff.all()
[<MyModel: MyModel7>]
>>> m7.stuff.all()
[]
>>> m5 = MyModel.objects.get(pk=5)
>>> m4 = MyModel.objects.get(pk=4)
>>> m4.stuff.all()
[<MyModel: MyModel5>]
>>> m5.stuff.all()
[<MyModel: MyModel4>]
可以看出和之间的新ManyToMany
关系不是对称的,但是现有的 和 之间的关系仍然是对称的,正如创建这些对象时的模型所述。m6
m7
m4
m5
编辑 - 具有对称外键的附加数据库约束
向读者道歉这个答案的长度,我们似乎正在深入探索这个问题。
在 sql 中,通过创建一个表来对多对多关系进行建模,该表包含该关系唯一的所有信息——通常只是两个表的主键值。
所以对于我们的MyModel
,django 创建了两个表:
----------------- -----------------------
| stuff_mymodel |---------->| stuff_mymodel_stuff |
----------------- -----------------------
^ |
| |
--------------------------------
图中显示的链接由模式中的主键或 id 值表示:
mysql> describe stuff_mymodel;
+-------+---------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+-------+---------+------+-----+---------+----------------+
| id | int(11) | NO | PRI | NULL | auto_increment |
+-------+---------+------+-----+---------+----------------+
1 row in set (0.00 sec)
mysql> describe stuff_mymodel_stuff;
+-----------------+---------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+-----------------+---------+------+-----+---------+----------------+
| id | int(11) | NO | PRI | NULL | auto_increment |
| from_mymodel_id | int(11) | NO | MUL | NULL | |
| to_mymodel_id | int(11) | NO | MUL | NULL | |
+-----------------+---------+------+-----+---------+----------------+
3 rows in set (0.00 sec)
并显示为 Djangomanage.py
脚本的输出:
$ python ./manage.py sql stuff
BEGIN;
CREATE TABLE `stuff_mymodel_stuff` (
`id` integer AUTO_INCREMENT NOT NULL PRIMARY KEY,
`from_mymodel_id` integer NOT NULL,
`to_mymodel_id` integer NOT NULL,
UNIQUE (`from_mymodel_id`, `to_mymodel_id`)
)
;
CREATE TABLE `stuff_mymodel` (
`id` integer AUTO_INCREMENT NOT NULL PRIMARY KEY
)
;
ALTER TABLE `stuff_mymodel_stuff` ADD CONSTRAINT `from_mymodel_id_refs_id_7fa00238` FOREIGN KEY (`from_mymodel_id`) REFERENCES `stuff_mymodel` (`id`);
ALTER TABLE `stuff_mymodel_stuff` ADD CONSTRAINT `to_mymodel_id_refs_id_7fa00238` FOREIGN KEY (`to_mymodel_id`) REFERENCES `stuff_mymodel` (`id`);
COMMIT;
无论 djangoManyToManyField
是否对称,此 sql 都是相同的。唯一的区别是stuff_mymodel_stuff
表中创建的行数:
mysql> select * from stuff_mymodel_stuff;
+----+-----------------+---------------+
| id | from_mymodel_id | to_mymodel_id |
+----+-----------------+---------------+
| 1 | 1 | 2 |
| 2 | 1 | 3 |
| 3 | 4 | 5 |
| 4 | 5 | 4 |
| 5 | 6 | 7 |
+----+-----------------+---------------+
5 rows in set (0.00 sec)
链接m4 -> m5
是对称的,其他的不是。在 Django 源代码中挖掘,我们可以找到负责在 sql 中创建“镜像”条目的代码,如果symmetrical
是True
:
# If the ManyToMany relation has an intermediary model,
# the add and remove methods do not exist.
if rel.through._meta.auto_created:
def add(self, *objs):
self._add_items(self.source_field_name, self.target_field_name, *objs)
# If this is a symmetrical m2m relation to self, add the mirror entry in the m2m table
if self.symmetrical:
self._add_items(self.target_field_name, self.source_field_name, *objs)
add.alters_data = True
这是目前在 github 上的第 605 行:https ://github.com/django/django/blob/master/django/db/models/fields/related.py
希望这能回答您的所有疑问。