Django ManyToMany与"自我"的关系没有落后关系

Odi*_*aeb 13 django orm django-related-manager

这里有关于向后关系的几个问题,但我要么太愚蠢了解它们,要么认为它们不适合我的情况.

我有模特儿

class MyModel(models.Model)
    stuff = models.ManyToManyField('self', related_name = 'combined+')
Run Code Online (Sandbox Code Playgroud)

我创建了表单,我将所存储的信息组合在一起.它将对象关系存储在数据库中,如下所示:

表:

id:from_stuff_id:to_stuff_id
1:original_object_id:first_related_object
2:original_object_id:second_related_object
3:first_related_object:original_object_id
4:second_related_object:original_object_id
Run Code Online (Sandbox Code Playgroud)

所以当我显示对象first_related_object并检查与之关系时

myobject.stuff.all()
Run Code Online (Sandbox Code Playgroud)

然后我得到"original_object".但我不需要它.我希望它不会显示出这样的倒退关系.

EDIT1

所以我很难解释自己.

也许这段代码可以更好地说明我想要的东西.

myobjectone = MyModel.objects.get(pk = 1)
myobjecttwo = MyModel.objects.get(pk = 2)
myobjectthree = MyModel.objects.get(pk = 3)
myobjectone.stuff.add(myobjecttwo)
myobjectone.stuff.add(myobjectthree)
myobjectone.stuff.all()
[myobjecttwo, myobjectthree] <-- this i want
myobjecttwo.stuff.all()
[myobjectone]<-- this i do not want
myobjectthree.stuff.all()
[myobjectone]<-- this i do not want
Run Code Online (Sandbox Code Playgroud)

现在唯一的问题是 - 如果我甚至应该使用stuff.all()如果我不想要它们产生的结果并且应该编写我自己的经理/方法来获得排除后向关系的对象列表.

/ EDIT1

edit2回应亨利佛罗伦萨:

好的 - 我用空基测试它看起来像对称= False确实有数据库级别差异.我认为.

我创建了对称= False的空表,然后创建添加关系没有产生向后关系.如果我创建没有symmetrical = False的空表.然后他们是.在创建表之后,设置symmetrical = False没有区别.所以我猜这些差异在于数据库级别.

/ edit2那么我应该在这做什么?

写我自己的经理什么的?

艾伦

Hen*_*nce 23

我对这个问题的理解是ManyToMany关系应该是单向的,因为如果以下是真的:

             --------------           ----------------
             | mymodelone |---------->|  mymodeltwo  |
             --------------     |     ----------------
                                |
                                |     ----------------
                                ----->| mymodelthree |
                                      ----------------
Run Code Online (Sandbox Code Playgroud)

那么在另一个方向上不应该存在隐含的关系:

             --------------           ----------------
             | mymodelone |<-----/----|  mymodeltwo  |
             --------------           ----------------

             --------------           ----------------
             | mymodelone |<-----/----| mymodelthree |
             --------------           ----------------
Run Code Online (Sandbox Code Playgroud)

ManyToMany字段有一个symmetrical属性,默认情况下True,请参见:here.

要创建一个新的应用程序来演示非对称的ManyToMany字段:

创建一个新应用:

$ python ./manage.py startapp stuff
Run Code Online (Sandbox Code Playgroud)

添加东西应用程序settings.py:

...
INSTALLED_APPS = (
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'stuff'
)
....
Run Code Online (Sandbox Code Playgroud)

编辑`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
Run Code Online (Sandbox Code Playgroud)

同步数据库:

$ 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)
Run Code Online (Sandbox Code Playgroud)

然后在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()
[]
>>> 
Run Code Online (Sandbox Code Playgroud)

编辑 - 现有模型上的ManyToMany关系

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
Run Code Online (Sandbox Code Playgroud)

创建新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>]
Run Code Online (Sandbox Code Playgroud)

正如所料,这些东西ManyToManyField正在形成对称的关系.如果我们然后设置ManyToManyFieldsymmetrical = 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>]
Run Code Online (Sandbox Code Playgroud)

由此可以看出,新ManyToMany的关系m6m7不是对称的,但现有的一个,之间m4m5仍然是对称的,当创建这些对象的模型说明.

编辑 - 使用对称外键的其他数据库约束

向读者致歉这个答案的长度,我们似乎在某种程度上探索了这个问题.

在sql中,通过创建一个包含关系唯一的所有信息的表来建模多对多关系 - 通常只是两个表的主键值.

所以对于我们来说MyModel,django会创建两个表:

             -----------------           -----------------------
             | stuff_mymodel |---------->| stuff_mymodel_stuff |
             -----------------           -----------------------
                      ^                              |
                      |                              |
                      --------------------------------
Run Code Online (Sandbox Code Playgroud)

图中显示的链接由架构中的主键或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)
Run Code Online (Sandbox Code Playgroud)

并显示为Django manage.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;
Run Code Online (Sandbox Code Playgroud)

如果django ManyToManyField是对称的,那么这个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)
Run Code Online (Sandbox Code Playgroud)

链接m4 -> m5是对称的而其他链接不对称.在Django源代码中,我们可以找到负责在sql中创建"镜像"条目的代码,如果symmetricalTrue:

    # 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
Run Code Online (Sandbox Code Playgroud)

目前这是github上的第605行:https://github.com/django/django/blob/master/django/db/models/fields/related.py

希望这能解答您的所有问题.