You can add this file to your tamplates/admin/app_list.html and make your django admin customizable by the user.
It will store the result in the local storage.
| {% load i18n %} | |
| {% if app_list %} | |
| {% for app in app_list %} | |
| <div | |
| id="django-app-{{ app.app_label }}" | |
| class="app-{{ app.app_label }} module{% if app.app_url in request.path|urlencode %} current-app{% endif %}" | |
| draggable="true" | |
| style="cursor: move;" | |
| > | |
| <table> | |
| <caption> | |
| <a href="{{ app.app_url }}" class="section" title="{% blocktranslate with name=app.name %}Models in the {{ name }} application{% endblocktranslate %}">{{ app.name }}</a> | |
| </caption> | |
| {% for model in app.models %} | |
| {% with model_name=model.object_name|lower %} | |
| <tr class="model-{{ model_name }}{% if model.admin_url in request.path|urlencode %} current-model{% endif %}"> | |
| <th scope="row" id="{{ app.app_label }}-{{ model_name }}"> | |
| {% if model.admin_url %} | |
| <a href="{{ model.admin_url }}" {% if model.admin_url in request.path|urlencode %} aria-current="page" {% endif %}>{{ model.name }}</a> | |
| {% else %} | |
| {{ model.name }} | |
| {% endif %} | |
| </th> | |
| {% if model.add_url %} | |
| <td><a href="{{ model.add_url }}" class="addlink" aria-describedby="{{ app.app_label }}-{{ model_name }}">{% translate 'Add' %}</a></td> | |
| {% else %} | |
| <td></td> | |
| {% endif %} | |
| {% if model.admin_url and show_changelinks %} | |
| {% if model.view_only %} | |
| <td><a href="{{ model.admin_url }}" class="viewlink" aria-describedby="{{ app.app_label }}-{{ model_name }}">{% translate 'View' %}</a></td> | |
| {% else %} | |
| <td><a href="{{ model.admin_url }}" class="changelink" aria-describedby="{{ app.app_label }}-{{ model_name }}">{% translate 'Change' %}</a></td> | |
| {% endif %} | |
| {% elif show_changelinks %} | |
| <td></td> | |
| {% endif %} | |
| </tr> | |
| {% endwith %} | |
| {% endfor %} | |
| </table> | |
| </div> | |
| {% endfor %} | |
| {% else %} | |
| <p>{% translate 'You don’t have permission to view or edit anything.' %}</p> | |
| {% endif %} | |
| <script type="text/javascript"> | |
| try { | |
| let apps = JSON.parse(localStorage.getItem('apps')); | |
| if (apps) { | |
| let appList = document.getElementById("content-main"); | |
| // sort the divs according to the apps array | |
| for (let i = 0; i < apps.length; i++) { | |
| let app = apps[i]; | |
| let appDiv = document.getElementById(app); | |
| if (appDiv) { | |
| appList.appendChild(appDiv); | |
| } | |
| } | |
| } | |
| } catch (e) { | |
| console.error(e); | |
| localStorage.removeItem('apps'); | |
| } | |
| let draggedItem = null; | |
| const sortableList = document.getElementById("content-main"); | |
| document.querySelectorAll('[id^="django-app"]').forEach((appDiv) => { | |
| appDiv.addEventListener('dragstart', (event) => { | |
| draggedItem = event.target; | |
| setTimeout(() => { | |
| event.target.style.opacity = "50%"; | |
| }, 0); | |
| }); | |
| appDiv.addEventListener('dragover', (event) => { | |
| event.preventDefault(); | |
| const afterElement = | |
| getDragAfterElement(sortableList, event.clientY); | |
| if (afterElement == null) { | |
| sortableList.appendChild(draggedItem) | |
| } else { | |
| sortableList.insertBefore(draggedItem, afterElement) | |
| } | |
| setTimeout(() => { | |
| let apps = []; | |
| for (let i = 0; i < sortableList.children.length; i++) { | |
| if (sortableList.children[i].id) { | |
| apps.push( sortableList.children[i].id); | |
| } | |
| } | |
| localStorage.setItem('apps', JSON.stringify(apps)); | |
| }, 0); | |
| } | |
| ); | |
| appDiv.addEventListener('dragend', (event) => { | |
| event.preventDefault(); | |
| setTimeout(() => { | |
| event.target.style.opacity = ""; | |
| draggedItem = null; | |
| }, 0); | |
| }); | |
| }); | |
| const getDragAfterElement = ( | |
| container, y | |
| ) => { | |
| const draggableElements = [ | |
| ...container.querySelectorAll( | |
| "div:not(.dragging)" | |
| ),]; | |
| return draggableElements.reduce( | |
| (closest, child) => { | |
| const box = | |
| child.getBoundingClientRect(); | |
| const offset = | |
| y - box.top - box.height / 2; | |
| if ( | |
| offset < 0 && | |
| offset > closest.offset) { | |
| return { | |
| offset: offset, | |
| element: child, | |
| }; | |
| } else { | |
| return closest; | |
| } | |
| }, | |
| { | |
| offset: Number.NEGATIVE_INFINITY, | |
| } | |
| ).element; | |
| }; | |
| </script> |
You can add this file to your tamplates/admin/app_list.html and make your django admin customizable by the user.
It will store the result in the local storage.