Say, you want your Tag
model to contain a subtags
field that has Tag
instances. And you need to make a through table.
models.py
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 thisTag
toSubtag
relation. In my example, it islevel = models.PositiveIntegerField()
.related_name = 'main_tag'
is used to refer to the mainTag
model from a subTag
model. 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 whichToSubTag
field is assigned to the main Tag model (becauseToSubTag
has twoForeignKey
relations toTag
– 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 usefk_name
property in the inline (see it below).symmetrical = False
is 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.
admin.py
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 asthrough_fields = 'tag'
.