[TOC]
Take Project
and Task
for example:
class Project(Model):
pass
class Task(Model):
POSITION_STEP = 2**16
project = ForeignKey(Project)
position = Number(default=POSITION_STEP, allow_none=False) # position in project it belonged to
The server send tasks by position in ascending order.
The front-end don't need to care Task.position
. Front-end stores tasks in an ordered list.
While user move a task up/down, just change the ordered list, at the same time send a request telling server the task's new place, like below:
## Add a new task with postion-spec
POST /tasks/
{
"project": "project-001",
// If you are adding the first task, just ignore the "position" key
"position": {
"before": "task-A", // set null if put at end
"after": "task-B" // set null if put at top
}
// ...
}
## Update a task with postion-spec
PATCH /tasks/task-C
{
"project": "project-001",
"position": {
"before": "task-A", // set null if move to the end
"after": "task-B" // set null if move to the top
}
// ...
}
Note the front-end doesn't calculate the position(it's server's responsibility), also doesn't care the new postision responded by server(because front-end maintains the same order via its ordered list).
- When creating the first task, no postion-spec is required from client.
saving the task with position
65536
(the default value). - When put a task between two tasks:
position = (before.position + after.position) / 2
- When put a task at end:
position = after.position + Task.POSITION_STEP
- When put a task at top:
position = before.position - Task.POSITION_STEP
If the calculated position is not an integer or abs(position - after.position) < 2
,
Then all tasks' position values requires reassignment with the same order:
Fake code for reassignment:
def rearange_tasks_position(project):
def make_pos_generator(cursor=1):
while True:
yield Task.POSITION_STEP * cursor
cursor += 1
pos_generator = make_pos_generator()
for task in db.query(Task).filter_by(project=project).sort('position', 'ASC').all():
task.position = next(pos_generator)
task.save()
A structure like below:
{
"before": "some-task-id",
"after": "some-task-id"
}
before
and after
mustn't be all missing.