当有多个 model 通过 ManyToManyField 关联到同一个目标 model 时,如果这个 ManyToManyField 需要额外的字段,则需要单独定义一个中间表。
这时中间表除了指向的源表是不一样的,其他字段包括目标表都是一样的,比如:  
# base model
class Created(models.Model):
    created = models.DateTimeField(auto_now_add=True)
    class Meta:
        abstract = True
        required_db_vendor = "postgresql"
class CreatedModified(Created):
    last = models.DateTimeField(auto_now=True)
    class Meta(Created.Meta):
        abstract = True
class Argument(CreatedModified):
    ...
class ArgumentThrough(CreatedModified):
	# argument 中间表基类
    argument = models.ForeignKey(
        to=Argument,
        on_delete=models.PROTECT
    )
    value = models.JSONField(blank=True)
    def clean(self):
        argument_value_validator(self.argument, self.value)
    class Meta(CreatedModified.Meta):
        abstract = True
class Inventory(CreatedModified):
    ...
    class Meta(CreatedModified.Meta):
        abstract = True
class Host(Inventory):
    inventory_variables = models.ManyToManyField(
        to=Argument,
        through="HostArgument",
        blank=True,
        help_text=_("Variables for host")
    )
class HostArgument(ArgumentThrough):
    # Host 到 Argument 的中间表
    host = models.ForeignKey(
        to=Host,
        on_delete=models.CASCADE
    )
class Group(Inventory):
    inventory_variables = models.ManyToManyField(
        to=Argument,
        through="GroupArgument",
        blank=True,
        help_text=_("Variables for group")
    )
class GroupArgument(ArgumentThrough):
    # Group 到 Argument 的中间表
    group = models.ForeignKey(
        to=Group,
        on_delete=models.CASCADE
    )
可以看到 Host 和 Group 的两个关联 Argument 的中间表除了指向的源表以外,其他字段几乎是一模一样的,所以可以改成这样:
class Host(Inventory):
    ...
class Group(Inventory):
    ...
 
 
 def add_related_field__argument(cls):
    through_cls_dict = {
        cls.__name__.lower(): models.ForeignKey(
            to=cls, on_delete=models.CASCADE
        ),
        "__module__": cls.__module__
    }
    cls.add_to_class(
        "inventory_variables", models.ManyToManyField(
            to=Argument,
            through=type(
                f"{cls.__name__}Argument", (ArgumentThrough,), through_cls_dict
            ),
            blank=True,
        )
    )
for model in (Host, Group):
    add_related_field__argument(model)
用这种方法,就可以不用写“重复”的代码了。
不知道你们是怎么解决这类问题的,希望我能抛砖引玉。