Relación de muchos a muchos entre 3 tablas en Django

Relación de muchos a muchos entre 3 tablas en Django

Dispongo de los siguientes modelos:

class Project(models.Model):
    project_name = models.CharField(max_length=50)
    project_users = models.ManyToManyField('Users.UserAccount', related_name='project_users', blank=True)
    ....

class UserAccount(AbstractBaseUser, PermissionsMixin):
    username = models.CharField(max_length=30, unique=True)
    ....

class Discipline(models.Model):
    name = models.CharField(unique=True, max_length=27, choices=discipline_choices)

La tabla DB para project_users se ve así:

*--------*----------------*---------------------*
|   ID   |   project_id   |   user_account_id   |
*--------*----------------*---------------------*

Quiero tener una relación adicional en el campo/tabla project_users con el modelo Discipline. ¿Es eso posible en Django? He estado mirando relaciones intermediarias-manytomany en Django, pero eso no es exactamente lo que quiero. En mi aplicación, hay una cantidad determinada de disciplinas, y lo que estoy tratando de lograr es brindarle a cada usuario múltiples disciplinas en un solo proyecto. Así que algo como esto:

*--------*----------------*---------------------*-------------------*
|   ID   |   project_id   |   user_account_id   |   discipline_id   |
*--------*----------------*---------------------*-------------------*

¿Es esto posible?

Mostrar la mejor respuesta

he intentado agregar disciplines = models.ForeignKey(Discipline, on_delete=models.CASCADE) a UserAccount

Puedes hacer un modo que se refiera a los tres modelos, eso es de hecho lo que hace un ManyToManyField detrás de las cortinas: crear un modelo en el medio.

class UserProjectDiscipline(models.Model):
    user_account = models.ForeignKey(
        settings.AUTH_USER_MODEL,
        on_delete=models.CASCADE
    )
    project = models.ForeignKey(
        Project,
        on_delete=models.CASCADE
    )
    discipline = models.ForeignKey(
        Discipline,
        on_delete=models.CASCADE
    )

Ahora puede acceder a los UserProjectDiscipline de un UserAccount, Project y/o Discipline con myuser.userprojectdiscipline_set.all(), myproject.userprojectdiscipline_set.all() y mydiscipline.userprojectdiscipline_set.all().

Además, puede definir ManyToManyFields que abarquen esta tabla, de modo que sea fácil obtener los Projects relacionados de un Discipline, etc. con:

class Project(models.Model):
    project_name = models.CharField(max_length=50)
    users = models.ManyToManyField(
        'Users.UserAccount',
        through='UserProjectDiscipline',
        related_name='projects',
        blank=True
    )
    disciplines = models.ManyToManyField(
        'Discipline',
        through='UserProjectDiscipline',
        related_name='projects',
        blank=True
    )
    # …

class UserAccount(AbstractBaseUser, PermissionsMixin):
    username = models.CharField(max_length=30, unique=True)
    disciplines = models.ManyToManyField(
        'Discipline',
        through='UserProjectDiscipline',
        related_name='users',
        blank=True
    ) 
    # …

Estos luego harán JOINs en la tabla del UserProjectDiscipline para recuperar usuarios de manera eficiente para un proyecto dado, etc. Si necesita objetos para el UserProjectDiscipline, aún puede usar para ejemplo myproject.userprojectdiscipline_set.all().

Si desea dar a un usuario myuser tres disciplinas para un proyecto determinado myproject, agréguelas con:

UserProjectDiscipline.objects.bulk_create([
    UserProjectDiscipline(user_account=myuser, project=my_project, discipline=mydiscipline1),
    UserProjectDiscipline(user_account=myuser, project=my_project, discipline=mydiscipline2),
    UserProjectDiscipline(user_account=myuser, project=my_project, discipline=mydiscipline3)
])