Django多对多(m2m)同一模型关系

79
我希望创建一个多对多的关系,以及一个用户类对象之间的关系。
我有类似以下的东西:
class MyUser(models.Model):
    ...
    blocked_users = models.ManyToManyField(MyUser, blank=True, null=True)

我的问题是我是否可以在类本身内部使用类引用。或者在ManyToManyField中必须使用"self"而不是"MyUser"?或者是否有另一种(更好的)方法来实现这个?


定义同一模型上的多对多有什么用处?请问有人知道吗? - user9260670
1
例如,如果您有一种类似树形结构的数据。主产品有子产品等等。 - Ron
2
“在同一模型上定义多对多有什么用处?”为了让每个用户都拥有自己的已屏蔽(或已关注)其他用户列表。 - user1600649
1
最佳使用案例是模型的树形结构。 - Ron
6个回答

108

从技术上讲,“MyUser”或“self”都可以使用,只要在两种情况下它是一个字符串。但是,文档总是使用“self”。使用“self”不仅更明确地说明实际发生的情况,而且不受类名更改的影响。例如,如果您稍后将“MyUser”更改为“SomethingElse”,则还需要更新对“MyUser”的任何引用。问题在于,由于它是一个字符串,因此您的IDE不会警告您出现错误,因此很容易错过。使用“self”无论现在或未来类的名称是什么都可以工作。


1
定义同一模型上的多对多有什么用处?请问有人知道吗? - user9260670
9
一个例子:一个文章模型,你希望指定“相关文章”。 - BigglesZX
一个个人的例子是一个代理商,其中包含一个字段,指定了招募他们的代理商。 - John R Perry

59
class MyUser(models.Model):
    ...
    blocked_users = models.ManyToManyField("self", blank=True)

"self" 不起作用。请使用 blocked_users = models.ManyToManyField("MyUser", blank=True, null=True)。 - Kriss
5
我没有说"MyUser",我说的是"self": https://docs.djangoproject.com/en/dev/ref/models/fields/#django.db.models.ManyToManyField.symmetrical - Goin
16
null=True 对 ManyToManyFields 没有影响。 - asgaines

23

如果您使用.clear()或.add()方法来处理相关对象,并且不希望关系另一侧的对象更新其关系字段中的数据,请不要忘记使用symmetrical=False。

some_field = models.ManyToManyField('self', symmetrical=False)

参考: Django文档: ManyToManyField.symmetrical


这个建议真的救了我!每次我给一个实体添加“父级”关系时,都会出现循环依赖问题,昨天整天我都在试图调试它! - Dre

6
我认为应该使用类名而不是self,因为这样使用self的话。
parent = models.ManyToManyField('self', null=True, blank=True)

当我添加父级元素时:

user1.parent.add(user2)

我在数据库中有两条记录,如下所示:

enter image description here

可以使用类名来进行操作,例如:

parent = models.ManyToManyField('User', null=True, blank=True)

我在数据库中有一条记录,如下所示:

enter image description here

请注意,我使用 uuid 作为主键,并且我使用的是 Django 3.1。

编辑: 如 @shinra-tensei 在此答案中的评论中所解释的那样,如果我们使用 self,则必须将 symmetrical 设置为 False。有关详细信息,请参见Django 文档:ManyToManyField.symmetrical


你使用的是哪个数据库适配器?可能存在一个错误... - Ron
@Ron postgres和mysql都可以。 - sahama

3
如果你使用 self 或者 MyUser,两种情况下都会出现 NameError 错误。你应该将 "self" 写成字符串形式。请参考下面的示例:
class MyUser(models.Model):
    ...
    blocked_users = models.ManyToManyField("self", blank=True, null=True)

如果关系不是对称的,不要忘记将 symmetrical 属性设置为 False

更多细节请查看: https://docs.djangoproject.com/en/3.0/ref/models/fields/#django.db.models.ManyToManyField


0

在ManyToManyField中不要使用'self',否则会导致对象相互链接,当使用Django表单提交时

class Tag(models.Model):
    ...
    subTag = models.ManyToManyField("self", blank=True)

 ...
 aTagForm.save()

结果为:

 a.subTag == b
 b.subTag == a

有关它的任何建议吗?我从来没有遇到过问题。 - Ron
我在我的演示中发现了这个问题,最终我使用ManyToManyField(“Tag”,blank = True)来解决它。 - ruandao
7
如果您不希望对象相互关联,请在创建字段时使用参数 symmetrical = False,它存在的目的是允许您使用 'self' 代替模型名称。具体请参考:https://docs.djangoproject.com/en/2.2/ref/models/fields/#django.db.models.ManyToManyField.symmetrical - Shinra tensei

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接