Reputation: 1814
I have a model that represents a Session
(say a one hour class for kids) that has a number of associated Task
s that must follow an order. Easier to understand:
class Task(models.Model):
(...)
order = models.PositiveIntegerField('offset from the beginning of a Session')
session = models.ForeignKey(Session, related_name='tasks')
unique_together = ('session', 'order')
ordering = ('order', )
Say I want to update a single session's order
(imagine a common drag and drop reordering). Thus, by updating one task's order I would need to reorder the rest of the tasks so that the uniqueness does not brake.
Graphical example (let's say I want task with pk=5014
to be in the second position (order=1
))
Initial state: Final state:
| pk | order | name | | pk | order | name |
|------|-------|------| |------|-------|------|
| 5011 | 0 | A | | 5011 | 0 | A |
| 5012 | 1 | B | ----> | 5014 | 1 | D |
| 5013 | 2 | C | | 5012 | 2 | B |
| 5014 | 3 | D | | 5013 | 3 | C |
My question is how to update it with Django ORM (if possible) or what would be an elegant way to do it, since I can only think of saving the desired task with a new order (say order=50), then rearrange one by one the rest of tasks and finally reassigning the desired order to the desired task.
Is there something like a bulk_update
(I didn't find anything similar after a long research) that allows me to modify the order
fields for all tasks and then saving all at once? Or do I have to take care of this unique_together myself?
Upvotes: 3
Views: 952
Reputation: 1638
It's usually best to reuse existing solutions:
https://github.com/bfirsh/django-ordered-model
though it still boils down to 2 saves in sequence:
https://github.com/bfirsh/django-ordered-model/blob/master/ordered_model/models.py#L122-L123
See also:
https://www.djangopackages.com/grids/g/model-ordering/
Best bet would be to write a custom SQL query, something along the lines of
It's also good idea to have DB constraints as the unique_together
in your example.
Upvotes: 2
Reputation: 1814
I'll post the code I developed as a solution for my problem; although I am not quite happy (I think there must be somehow a better solution), but just in case it helps someone.
I defined this method for the model Task
:
def update_order(self, new_order):
"""
Update the order of this task and reorder the rest of tasks
of the session accordingly
"""
current_order = self.order
tasks = Task.objects.filter(session=self.session)
current_highest_order = tasks.last().order
if new_order > current_highest_order or new_order < 0:
raise ValidationError(_('New order out of range'))
operation = -1 if new_order < current_order else +1
# set temporary order higher than any other to allow reordering the rest
self.order = current_highest_order + 1
self.save()
# reassign the rest of tasks' order accordingly
tasks = Task.objects.filter(session=self.session)
for i in range(current_order, new_order, operation):
task = tasks[i + operation] if operation == -1 else tasks[i]
task.order -= operation
task.save()
# Restore a proper order value (the desired new_order) to this task
self.order = new_order
self.save()
Any comments will be really appreciated.
Upvotes: 0