Reputation: 2671
I have this model:
class Task(MPTTModel, TimeStampedModel, StartFinishModel):
name = models.CharField(max_length=256)
start_after_end = models.ManyToManyField('self',
related_name='s2e_relation',
blank=True)
#s2e means that task can't start before related tasks end
How can I get all tasks, which can't start before this task ends and all tasks, which have to end before this task can start.
Let's say 1 -> 2 -> 3
I have id 2
. I want to find 3
and with another query 1
.
To find 3 I can use Task.objects.get(id = 2).start_after_end.all()
How can I find 1
?
UPDATE 1
Actually find out bigger problem - if I select 2 related to 1
- 1 immidiately changes relation to 1 is related to 2
Upvotes: 1
Views: 314
Reputation: 476614
If you make a relation with 'self'
, by default Django will make the relation symmetrical. This means that if an object a
has as friend b
, then b
automatically has as friend a
. So this means that the relation here will look like:
# symmetrical relation
1 <---> 2 <---> 3
It thus means that for:
task1.start_after_end.all() # ==> [2]
task2.start_after_end.all() # ==> [1, 3]
task3.start_after_end.all() # ==> [2]
So that here both 1
and 3
will be seen as Task2.start_after_end.all()
s. You probably do not want that: you want the relation to be non-symetrical.
So you should edit the field to:
class Task(MPTTModel, TimeStampedModel, StartFinishModel):
name = models.CharField(max_length=256)
start_after_end = models.ManyToManyField(
'self',
related_name='s2e_relation',
symmetrical=False,
blank=True
)
Now the above will look like:
# asymmetrical relation
1 ---> 2 ---> 3
Note that non-symmetrical relations are not anti-symmetrical relations. It is still possible to add task1
to the start_after_end
of task2
, but it will not do this automatically in case you add task2
to the start_after_end
of task1
.
So now if that is done, it means that for some_task
, we get the next tasks with:
my_task.start_after_end.all() # the next ones
and we get the ones before with the related_name
:
my_task.s2e_relation.all() # the previous ones
So for example:
task1 = Task.objects.create(name='task1')
task2 = Task.objects.create(name='task2')
task3 = Task.objects.create(name='task3')
task1.start_after_end.add(task2)
task2.start_after_end.add(task3)
Then if we trigger the relations, we get:
task1.start_after_end.all() # ==> [2]
task2.start_after_end.all() # ==> [3]
task3.start_after_end.all() # ==> []
task1.s2e_relation.all() # ==> []
task2.s2e_relation.all() # ==> [1]
task3.s2e_relation.all() # ==> [2]
Upvotes: 4
Reputation: 712
Idea is to use related_name='s2e_relation'
passed in ManyToManyField
You can do so by:
Task.objects.get(id=2).s2e_relation.all()
Upvotes: 0