Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save yahkun/3abe47ce70eafa39eec751fa156bd886 to your computer and use it in GitHub Desktop.
Save yahkun/3abe47ce70eafa39eec751fa156bd886 to your computer and use it in GitHub Desktop.
Drag-drop sort: Design & Implementation

Drag-drop sort: Design & Implementation

[TOC]

References

Data Models

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

Front-end/Server Interaction

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).

Server Implementation

  • 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()

Terms

position-spec

A structure like below:

{
    "before": "some-task-id",
    "after": "some-task-id"
}

before and after mustn't be all missing.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment