Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to set related_name with a ManyToManyField using a through table and pointing to self

I have a task class, that can have sub-tasks, so it's a cyclic relationship. I'm passing it through a linker model/table like so:

class Task(models.Model):
    title = models.CharField(max_length=100)
    description = models.TextField(blank=True)
    completed = models.BooleanField(default=False)
    project = models.ForeignKey('Project', related_name="tasks")
    dependancy = models.ManyToManyField('self', through='Dependancy', null=True, 
        blank=True, through_fields=('task', 'sub_task'), symmetrical=False)
    def __str__(self):
        return self.title

class Dependancy(models.Model):
    task = models.ForeignKey(Task)
    sub_task = models.ForeignKey(Task)

But I get this error:

ERRORS:
gantt_charts.dependency.sub_task: (fields.E303) Reverse query name for 'dependency.sub_task' clashes with field name 'Task.dependency'.
        HINT: Rename field 'Task.dependency', or add/change a related_name argument to the definition for field 'dependency.sub_task'.
gantt_charts.dependency.sub_task: (fields.E304) Reverse accessor for 'dependency.sub_task' clashes with reverse accessor for 'dependency.task'.
        HINT: Add or change a related_name argument to the definition for 'dependency.sub_task' or 'dependency.task'.


gantt_charts.dependency.task: (fields.E303) Reverse query name for 'dependency.task' clashes with field name 'Task.dependency'.
        HINT: Rename field 'Task.dependency', or add/change a related_name argument to the definition for field 'dependency.task'.
gantt_charts.dependency.task: (fields.E304) Reverse accessor for 'dependency.task' clashes with reverse accessor for 'dependency.sub_task'.
        HINT: Add or change a related_name argument to the definition for 'dependency.task' or 'dependency.sub_task'.

Obviously I need to set the related name on the Dependency.sub_task and Dependency.task field, and following the solution here is to name them something like task_task and task_sub_task, but that sounds wrong, unintuitive and confusing.

What would be a clear and concise name for them? It'd be easier if I wasn't getting confused over what related_names were, when using a linking table.

like image 257
AncientSwordRage Avatar asked Sep 28 '22 22:09

AncientSwordRage


1 Answers

Given a Task instance, how can you access all the Dependencies that have that as their task? Or their sub_task? That's the purpose of related_name—it provides the name for the attribute that Django will create on Task to point to that group of things.

You're seeing that error because Django automatically uses the name <model>_set, and since you have two ForeignKeys pointing to the same model, the default name will conflict.

Now, it might be that you never need to directly access Dependencies that way. If that's the case you can add related_name='+' to both fields and the reverse attribute won't be created at all (and your errors will disappear).

If you do want to access them, the name is up to you. I prefer longer-but-more-descriptive names to make their purpose clear. I might model the problem like this:

class Task(models.Model): 
    subtasks = models.ManyToManyField('self', 
                                      through='Dependancy',
                                      symmetrical=False,
                                      through_fields=('supertask', 'subtask'),
                                      related_name='supertasks')

class Dependancy(models.Model):
    supertask = models.ForeignKey(Task, related_name='dependencies_as_supertask')
    subtask = models.ForeignKey(Task, related_name='dependencies_as_subtask')

    class Meta:
        unique_together = ('supertask', 'subtask')


>>> task = Task.objects.get()

>>> # all the Tasks that are supertasks of this one
>>> task.supertasks

>>> # all the Tasks that are subtasks of this one
>>> task.subtasks

>>> # all the Dependencies with this Task as the supertask
>>> task.dependencies_as_supertask

>>> # all the Dependencies with this Task as the subtask
>>> task.dependencies_as_subtask
like image 89
Kevin Christopher Henry Avatar answered Oct 03 '22 03:10

Kevin Christopher Henry