Syntax Highliting and more

Moved template looking code in base.admin related to error messages, into an actual template file.
New feature: Codearea widget
Every JSONField will be highlighted in django-admin with json highlighter
This commit is contained in:
Oracle 2025-08-11 15:23:12 +02:00
parent f6ce4fc374
commit 07fe648b9c
4 changed files with 136 additions and 31 deletions

View File

@ -1,48 +1,44 @@
from django.contrib import admin, messages from django.contrib import admin, messages
from django.utils.safestring import mark_safe from django.template import loader
from django.utils.html import escape from django.forms.fields import JSONField
from ..widgets import Codearea
import traceback import traceback
class BaseModelAdmin( admin.ModelAdmin ): class BaseModelAdmin( admin.ModelAdmin ):
def formfield_for_dbfield( self, db_field, **kwargs ):
formfield = super().formfield_for_dbfield(db_field, **kwargs)
if isinstance( formfield, JSONField ):
formfield.widget = Codearea( language = "json" )
return formfield
def explain_gather_results( self, request, results, fails_to_show = 2 ): def explain_gather_results( self, request, results, fails_to_show = 2 ):
failed = 0 failed = 0
for id, e in results.items(): for id, e in results.items():
if isinstance(e, BaseException): if isinstance(e, BaseException):
failed += 1 failed += 1
if failed <= fails_to_show: if failed <= fails_to_show:
traceback_message = None
if request.user.is_superuser: if request.user.is_superuser:
traceback_message = ''.join(traceback.TracebackException.from_exception(e).format()) traceback_message = ''.join(traceback.TracebackException.from_exception(e).format())
self.message_user( request, mark_safe(f"""
Error For <b>{id}</b>:
<style>
button.tb {{
background: none;
border: none;
cursor: pointer;
color: var(--link-fg);
text-decoration: none;
}}
button.tb:hover {{ text-decoration: underline; }}
pre.summary {{ white-space: pre-wrap; }}
pre.tb {{
display: none;
background: #060d0a;
white-space: pre-wrap;
border-radius: 5px;
font-size: 10px;
line-height: 1.5em;
padding: 10px;
}}
pre.tb.shown {{ display: block;}}
</style>
<pre class="summary">{escape(e)}</pre>
<button class='tb' onclick="event.target.parentElement.querySelector('pre.tb').classList.toggle('shown')">[TraceBack]</button>
<pre class='tb'>{escape(traceback_message)}</pre>
"""), messages.ERROR
)
else: self.message_user( request, f"Error For {id}: {escape(e)}", messages.ERROR)
#NOT sure if there is a clean way to get the model
#object_change_url = reverse(
# f'admin:{self._meta.model_name}_{self._meta.app_label}_change',
# kwargs={'object_id': self.pk}
#)
self.message_user(
request,
loader.get_template("asyncron/admin-gather-error-message.html").render({
"id": id, "e": e,
"traceback_message": traceback_message
}),
messages.ERROR
)
if failed == 0: self.message_user( request, f"All {len(results)} Succeeded!", messages.SUCCESS ) if failed == 0: self.message_user( request, f"All {len(results)} Succeeded!", messages.SUCCESS )
elif failed <= fails_to_show: elif failed <= fails_to_show:

View File

@ -0,0 +1,31 @@
{% autoescape on %}
Error For Object {{ id }}:
{% if traceback_message %}
<style>
button.tb {
background: none;
border: none;
cursor: pointer;
color: var(--link-fg);
text-decoration: none;
}
button.tb:hover { text-decoration: underline; }
pre.summary { white-space: pre-wrap; }
pre.tb {
display: none;
background: #060d0a;
white-space: pre-wrap;
border-radius: 5px;
font-size: 10px;
line-height: 1.5em;
padding: 10px;
}
pre.tb.shown { display: block; }
</style>
<pre class="summary">{{ e }}</pre>
<button class='tb' onclick="event.target.parentElement.querySelector('pre.tb').classList.toggle('shown')">
[TraceBack]
</button>
<pre class='tb'>{{ traceback_message }}</pre>
{% endif %}
{% endautoescape %}

View File

@ -0,0 +1,56 @@
{% load static %}
<link rel="stylesheet" href="{% static 'highlight/styles/atom-one-dark.min.css' %}">
<script src="{% static 'highlight/highlight.min.js' %}"></script>
<div id="codearea_{{ target }}">
{{ textarea }}
<pre>{{ value }}</pre>
</div>
<style media="screen">
#codearea_{{ target }} {
position: relative;
}
#codearea_{{ target }} textarea {
background: #282c34;
color: transparent;
caret-color: white;
outline: none;
}
#codearea_{{ target }} textarea::selection {
background: hsl(221deg 13% 28%);
color: transparent;
text-shadow: none;
}
#codearea_{{ target }} > pre {
position: absolute; pointer-events: none;
left: 0; right: 0; top: 0; bottom: 0;
white-space: wrap;
opacity: 1;
background: transparent;
}
#codearea_{{ target }} > * {
font-family: var(--font-family-monospace);
margin: 0;
padding: 5px;
font-size: 1em;
border-radius: 3px;
}
</style>
{{ hljs_config | json_script:"hlsj-config" }}
<script>
(function(){
const codearea = document.getElementById("codearea_{{ target }}");
const textarea = codearea.querySelector('textarea');
const pre = codearea.querySelector('pre');
const config = JSON.parse(document.getElementById('hlsj-config').textContent);
textarea.addEventListener('input', (e) => {
pre.innerText = e.target.value;
delete pre.dataset.highlighted;
hljs.highlightElement( pre, config )
});
hljs.highlightElement( pre, config )
})()
</script>

22
asyncron/widgets.py Normal file
View File

@ -0,0 +1,22 @@
from django.template import loader
from django.forms import Textarea
class Codearea( Textarea ):
def __init__( self, *args, language = "auto", **kwargs ):
self.language = language
super().__init__( *args, **kwargs )
def render( self, name, value, attrs = None, renderer = None ):
print( self )
print(f"RENDERING: {name=} {value=} {attrs=} {renderer=}")
return loader.get_template("asyncron/codearea-widget.html").render({
"textarea": super().render( name, value, attrs, renderer ),
"value": value,
"target": name,
"hljs_config": {
"language": self.language
}
})