djangodjango-modelsormm2mdbm

Django makemigrations error on many to many relationship


What i want:

Store information about running of group of people.

What i did:

from django.db import models
from django.contrib.auth.models import User
from datetime import timedelta


class Route(models.Model):
    name = models.CharField(max_length=50)


class Run(models.Model):
    date = models.DateField()
    type = models.ForeignKey(Route, on_delete=models.PROTECT)
    runners = models.ManyToManyField(User, through='RunnerResult', through_fields=["user", "run"])


class RunnerResult(models.Model):
    user = models.ForeignKey(User, on_delete=models.PROTECT)
    run = models.ForeignKey('Run', on_delete=models.PROTECT)
    result = models.DurationField(default=timedelta())

Problem:

When i do makemigrations i have the following error:

SystemCheckError: System check identified some issues:

ERRORS:
run.Run.runners: (fields.E339) 'RunnerResult.run' is not a foreign key to 'User'.
        HINT: Did you mean one of the following foreign keys to 'User': user?
run.Run.runners: (fields.E339) 'RunnerResult.user' is not a foreign key to 'Run'.
        HINT: Did you mean one of the following foreign keys to 'Run': run?

Tried to swap through_fields and models between each other and some other actions. I'm starting to think of my misunderstanding of M2M relationship.


Solution

  • You specified the through_fields in the wrong order. You first should specify the relation that refers to the source, and then then one to the target, so:

    class Run(models.Model):
        date = models.DateField()
        type = models.ForeignKey(Route, on_delete=models.PROTECT)
        runners = models.ManyToManyField(
            User,
            through='RunnerResult',
            through_fields=('run', 'user')
        )

    Since there is however only one ForeignKey to Run, and one to User, you do not need to specify the through_fields=… parameter [Django-doc]. So you can implement this as:

    class Run(models.Model):
        date = models.DateField()
        type = models.ForeignKey(Route, on_delete=models.PROTECT)
        runners = models.ManyToManyField(
            User,
            through='RunnerResult'
            # no through_fields
        )

    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.