pythondjango

(fields.E331) Field specifies a many-to-many relation through model, which has not been installed


When I run makemigrations I get the error

teams.Team.members: (fields.E331) Field specifies a many-to-many relation through model 'TeamMember', which has not been installed.

from django.db import models
from django.conf import settings

from common import TimestampMixin
from users.models import User


class Team(models.Model, TimestampMixin):
    name = models.CharField(max_length=100)
    owner = models.ForeignKey(
        User,
        related_name='owned_teams',
        on_delete=models.CASCADE
    )
    members = models.ManyToManyField(
        User,
        through='TeamMember',
        related_name='teams'
    )

    def __str__(self):
        return self.name


class TeamMember(models.Model, TimestampMixin):
    user = models.ForeignKey(
        User,
        on_delete=models.CASCADE
    )
    team = models.ForeignKey(
        Team,
        on_delete=models.CASCADE
    )

    def __str__(self):
        return f"{self.user} in {self.team}"

I don't get why it's happening because the 'teams' app is installed and both Team and TeamMember is in the same file. Any ideas?


Solution

  • I can not reproduce this without the TimestampMixin. Usually mixins are listed first, otherwise they will often fail to do what one expects these to do: override certain methods, because the Method Resolution Order (MRO) [python-doc], will push these more down in the list of items to override.

    The correct way would thus:

    class TimestampMixin(models.Model):
        created_at = models.DateTimeField(auto_now_add=True)
        updated_at = models.DateTimeField(auto_now=True)
    
        class Meta:
            abstract = True

    and then use it with:

    class Team(TimestampMixin, models.Model):
        # …
    
    
    class TeamMember(TimestampMixin, models.Model):
        # …

    Note: It is normally better to make use of the settings.AUTH_USER_MODEL [Django-doc] to refer to the user model, than to use the User model [Django-doc] directly. For more information you can see the referencing the User model section of the documentation [Django-doc].