79

I'd like to create a many-to-many relationship from and to a user class object.

I have something like this:

class MyUser(models.Model):
    ...
    blocked_users = models.ManyToManyField(MyUser, blank=True, null=True)

The question is if I can use the class reference inside itself. Or do I have to use "self" insead of "MyUser" in the ManyToManyField? Or is there another (and better) way to do it?

Mel
  • 5,837
  • 10
  • 37
  • 42
Ron
  • 22,128
  • 31
  • 108
  • 206
  • what is the use defining many to many on same model?? anyone please –  Apr 10 '18 at 07:26
  • 1
    @ParaM For example if you have a tree-like structure. Master product has subproduct etc etc. – Ron Apr 10 '18 at 11:14
  • 2
    *"what is the use defining many to many on same model??"* To allow each user to have their own list of other users they have blocked (or followed). –  Oct 24 '20 at 20:17
  • 1
    Best use-case is a tree-structure for the model. – Ron Oct 26 '20 at 07:00

6 Answers6

108

Technically, I'm pretty sure "MyUser" or "self" will work, as long as it's a string in either case. You just can't pass MyUser, the actual class.

However, the docs always use "self". Using "self" is not only more explicit about what's actually happening, but it's impervious to class name changes. For example, if you later changed MyUser to SomethingElse, you would then need to update any reference to "MyUser" as well. The problem is that since it's a string, your IDE will not alert you to the error, so there's a greater chance of your missing it. Using "self" will work no matter what the class' name is now or in the future.

Chris Pratt
  • 232,153
  • 36
  • 385
  • 444
59
class MyUser(models.Model):
    ...
    blocked_users = models.ManyToManyField("self", blank=True)
Goin
  • 3,856
  • 26
  • 44
  • "self" doesn't work. Use blocked_users = models.ManyToManyField("MyUser", blank=True, null=True) – Kriss Jan 12 '14 at 21:12
  • 5
    I didn't say "MyUser", I said "self": https://docs.djangoproject.com/en/dev/ref/models/fields/#django.db.models.ManyToManyField.symmetrical – Goin Jan 13 '14 at 07:19
  • 16
    `null=True` has no effect on ManyToManyFields – asgaines Aug 16 '15 at 17:31
21

Don't forget use symmetrical=False, if you use .clear() or .add() method for related objects and don't wanna object on other side of relation update own data in relation field.

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

ref: Django Documentation: ManyToManyField.symmetrical

Snailedlt
  • 460
  • 6
  • 14
DmitryBara
  • 391
  • 3
  • 3
  • This suggestion totally saved me! I was experiencing a cyclic dependency every time I would add a 'parent' relation to an entity and I was trying to debug it all day yesterday! – Dre May 10 '22 at 13:45
6

I think it should be class name instead of self. because with using self like this

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

when i add parent:

user1.parent.add(user2)

i have 2 record in database like this: enter image description here

and with using class name liken this:

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

i have one record in database like this: enter image description here

note that i use uuid for pk and i use django 3.1

EDIT: as @shinra-tensei explained as comment in this answer we have to set symmetrical to False if we use self. documented in Django Documents: ManyToManyField.symmetrical

sahama
  • 669
  • 8
  • 16
3

If you use self or MyUser you will get a NameError in both cases. You should write "self" as string. See the example below:

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

And do not forget to set the symmetrical attribute to False if the relationship is not symmetrical.

For further details check: https://docs.djangoproject.com/en/3.0/ref/models/fields/#django.db.models.ManyToManyField

0

don't use 'self' in ManyToManyField, it will cause you object link each other, when use django form to submit it

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

 ...
 aTagForm.save()

and result:

 a.subTag == b
 b.subTag == a
ruandao
  • 410
  • 4
  • 11
  • Any suggetions about it? I never had problems. – Ron Jun 11 '19 at 06:40
  • I was found it in my demo and finally i use ManyToManyField("Tag", blank=True) to fix it – ruandao Jun 12 '19 at 07:02
  • 7
    If you don't want the objects to link each other, use the argument `symmetrical = False` when creating the field https://docs.djangoproject.com/en/2.2/ref/models/fields/#django.db.models.ManyToManyField.symmetrical , it exists to allow you to use the `'self'` name instead of the name of the model – Shinra tensei Sep 25 '19 at 08:52