Say, you want your
Tag model to contain a
subtags field that has
Tag instances. And you need to make a through table.
class Tag(models.Model) name = models.Charfield(max_length=100) subtags = models.ManyToManyField('self', through='ToSubTag', related_name='main_tag', # through_fields='tag', blank=True, symmetrical=False) class ToSubTag(models.Model): tag = models.ForeignKey(Tag, related_name='maintag_sub', on_delete=models.CASCADE, verbose_name='Тема') subtag = models.ForeignKey(Tag, db_index=True, related_name='subtag', on_delete=models.CASCADE, verbose_name='Подтема') level = models.PositiveIntegerField()
Breaking it down:
'self'is used to establish a relation to the same model.
through='ToSubTag'is used to establish a table with additional information about this
Subtagrelation. In my example, it is
level = models.PositiveIntegerField().
related_name = 'main_tag'is used to refer to the main
Tagmodel from a sub
Tagmodel. In the ToSubTag model they are mandatory for both relations to Tag, because if there are two of them.
through_fields = 'tag'is used to let Django know which
ToSubTagfield is assigned to the main Tag model (because
Tag– tag and subtag); BUT when I use it and make an Inline in admin.py, I get an error:
The intermediary model 'testapp.ToSubTag' has no field 'a'.That’s why it is commented. Instead of it, I use
fk_nameproperty in the inline (see it below).
symmetrical = Falseis used because this particular relation has a strict hierarchy: Tag -> Subtag. Otherwise if you added a subTag to mainTag.subtags, then this mainTag would be added to subTag.subtags. This property has a good explanation in the docs – if you add a secondPerson to firstPerson.friends, then the firstPerson would be added to the secondPerson.friends, because they can only be friends mutually.
class SubTagsToTag(admin.TabularInline): model = Tag.subtags.through fk_name = "tag" @admin.register(Tag) class TagAdmin(admin.ModelAdmin): inlines = [ SubTagsToTag ]
Everything here is pretty standard, except for:
fk_name = "tag"is basically the same as
through_fields = 'tag'.