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.