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:
parent
f6ce4fc374
commit
07fe648b9c
|
|
@ -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:
|
||||||
|
|
|
||||||
31
asyncron/templates/asyncron/admin-gather-error-message.html
Normal file
31
asyncron/templates/asyncron/admin-gather-error-message.html
Normal 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 %}
|
||||||
56
asyncron/templates/asyncron/codearea-widget.html
Normal file
56
asyncron/templates/asyncron/codearea-widget.html
Normal 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
22
asyncron/widgets.py
Normal 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
|
||||||
|
}
|
||||||
|
})
|
||||||
Loading…
Reference in New Issue
Block a user