Stylized HTML fields in Django admin - django

I Have TextField to store HTML text. I want to colorize HTML tags. I use TinyMCE but didn't need an HTML editor like WordPress just simple to colorize like IDE, Search a lot but didn't find anything useful, So if you can help I appreciate it.
My field:
I want output like this but changeable:

Using a code editor in Django admin Textarea
The following is implemented with CodeMirror but you can use any other library (Monaco, etc).
Final result:
For all the libraries, we need to add some library specific javascript and stylesheet plus some custom code for integration.
Django has pretty flexible templating to enable this. We can override the entire template or specific blocks. Detailed documentation can be fond here.
To integration CodeMirror, we can override the admin/base.html 's extrahead block. We will include the required js/css, one extra css for theming and some js to find and apply CodeMirror.
Here, I have create a admin/base.html inside my project's configured template directory. This will apply to all the apps and models. There are ways to target an app or model individually - check the office docs.
This works for JSONField data. You will have to tweak a bit to format
html or any other language.
Inspect the page and find id of the textarea you want to target
<!-- tested with Django 3.2.9 -->
{% extends "admin/base.html" %}
{% block extrahead %}
{{ block.super }}
<script src="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.65.0/codemirror.min.js" crossorigin="anonymous"
referrerpolicy="no-referrer"></script>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.65.0/codemirror.min.css"
crossorigin="anonymous" referrerpolicy="no-referrer" />
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.65.0/theme/oceanic-next.min.css"
crossorigin="anonymous" referrerpolicy="no-referrer" />
<script src="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.65.0/mode/javascript/javascript.min.js"
crossorigin="anonymous" referrerpolicy="no-referrer"></script>
<script type="text/javascript">
window.addEventListener('load', (event) => {
const target = document.getElementById("id_data");
target.value = JSON.stringify(JSON.parse(target.value), null, ' ');
const config = {
mode: 'javascript',
theme: 'oceanic-next',
lineNumbers: true,
lineWrapping: true
}
const jsonCodeMirror = CodeMirror.fromTextArea(target, config);
jsonCodeMirror.setSize("100%", 600);
});
</script>
{% endblock %}
Multiple textarea inputs can be targeted as well with get element with classname and then looping over the result set and applying the same logic.
For html formatting,
you can use this https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.65.0/mode/htmlmixed/htmlmixed.min.js instead of javascript. Delete the json stringify statement. Change mode to htmlmixed.

Don't need to override any template
solved by integrating django-ace field and in this we have support for CSS, python, HTML etc also.
Simply use this in django forms and provide the form in django model admin class
admin file
from appname import forms
#admin.register(models.Table)
class TableModelAdmin(admin.ModelAdmin):
form = forms.TableModelForm
form file
from django import forms
from django_ace import AceWidget
class TableModelForm(forms.ModelForm):
class Meta:
model = models.Table
fields = '__all__'
widgets = {
'columns': AceWidget(
mode='json',
theme='None',
width='1200px',
height='500px',
fontsize='18px',
showprintmargin=False,
),
}

Related

Google analytics for Django app only shows root domain instead of page titles

I've got my first Django app in production and would like to setup Google Analytics to see pageviews/timespent/etc by page title for the order flow. I've added the GA javascript to the of my base.html template with the hope that it would track each page with page title.
However, when I look at Google Analytics, I only see page views by my root domain 'mysite.com', and I cannot get get page views by '/app/pagetitle1', '/app/pagetitle2', '/app/pagetitle3', etc. 'app' is the Django app that the root domain gets redirected to 'mysite.com/app'. I'm assuming that Google Analytics would show entire path after root domain, if it were to work.
It seems like there is probably something simple I've overlooked, and would appreciate any advice.
Here's the GA tag I put in base.html right after per GA instructions:
<!-- Global site tag (gtag.js) - Google Analytics -->
<script async src="https://www.googletagmanager.com/gtag/js?id=XXX"></script>
<script>
window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments);}
gtag('js', new Date());
gtag('config', 'XXX');
</script>
Each template extends base.html as follows:
{% extends "base.html" %}
{% block content %}
<section class="container container-interior">
etc - rest of <body>
I made two changes to address lack of page title and double counting.
Remove this gtag call from my base.html template
gtag('config', 'G-ID',{'send_page_view': true});
Added this to each page that I wanted to track using a block tag.
gtag('event', 'page_view', { page_title: '', page_path: ', send_to: '<G-ID'> })

Can I use the django debug_toolbar on the admin pages?

(Note: the answer is a qualified 'yes' - it should work if not for my requirejs configuration issue - see my update at the end)
I'm looking into customizing the admin for a number of Models where savvy end-users are expected to do the maintenance.
I've seen a number of SO questions, such as How to override and extend basic Django admin templates?, on how to achieve this.
I expect that knowing which template files are being used by the admin at any particular point is key to customizing efficiently. So, I re-enabled the Django Debug Toolbar (hopefully wo requirejs side-effects this time).
The Django Debug Toolbar works and shows up in my apps' pages. But it doesn't show up on the admin pages. Is that to be expected?
Django (1.8.11)
django-debug-toolbar (1.4)
Why it's not working/Update:
I think I know what is happening. When looking at Firebug to see what CSS is involved with an admin page, I noticed that it was requesting debug toolbar CSS:
http://localhost:8000/static/debug_toolbar/css/toolbar.css
Which got me to think of requireJS incompatibility again. Sure enough, in the console, I see this error.
TypeError: $ is undefined
http://localhost:8000/static/debug_toolbar/js/toolbar.js
Line 297
So, again a requireJS-DJT glitch.
FYI, my Debug toolbar workaround for requireJS was (from https://github.com/django-debug-toolbar/django-debug-toolbar/issues/605):
settings.py
DEBUG_TOOLBAR_CONFIG = {
"JQUERY_URL": None,
}
and in my app's base template:
(this is the part that is missing from the admin pages)
{% block requirejs %}
//as per DJDT recommendations, make sure jquery loads before requireJS
<script type="text/javascript" src="/static/websec/external/jquery-2.1.1.min"></script>
<script type="text/javascript" src="{{STATIC_URL}}websec/external/require.js"></script>
<script>
//defines the requirejs configuration in general.
{% include "websec/requirejs_config.html" %}
</script>
It's generally considered bad practice to use the built-in admin backend for end-users.
Try checking foy <body></body> tags in the pages. Without these it will not load.
Then try adding INTERNAL_IPS = ('127.0.0.1') in settings.py
To make it ONLY load for users inside the admin panel, you could add a tag to your custom admin pages and change settings.DEBUG_TOOLBAR_CONFIG['INSERT_BEFORE']
Docs: here
Last to force it to show everywhere, you can try adding this to the settings file:
def show_toolbar(request):
return True
DEBUG_TOOLBAR_CONFIG = {
"SHOW_TOOLBAR_CALLBACK" : show_toolbar,
}
to stop debug toolbar from checking if it should appear or not, it always will Only use for testing/development purposes. All other users can see it as well.
Documentation: here.
A quick overview over what I had to do to get debug_toolbar and admin working together.
You want to load jquery on its own, even though requireJS is configured to handle it as well.
From https://github.com/django-debug-toolbar/django-debug-toolbar/issues/605 :
settings.py
DEBUG_TOOLBAR_CONFIG = {
"JQUERY_URL": None,
}
Now, set up the admin to load jquery outside of requireJS, so debug toolbar can find it:
I was planning to customize that admin anyway, so no big deal creating a base_site.html override.
/myproj/templates/admin/base_site.html
{% block extrahead %}
*************** add this (with proper path) ************************
<script type="text/javascript" src="{{STATIC_URL}}websec/external/jquery-2.1.1.min.js"></script>
*******************************************************************
<script type="text/javascript" src="{{STATIC_URL}}websec/external/require.js"></script>
<script>
{% include "websec/requirejs_config.html" %}
require(['bootstrap'], function(bootstrap){
});
</script>
{% endblock %}
This is similar to what I had to do to my site's ancestor template to get debug_toolbar and requireJS to cohabit:
/myproj/templates/websec/__base.html
{% block requirejs %}
<script type="text/javascript" src="/static/websec/external/jquery-2.1.1.min.js"></script>
<script type="text/javascript" src="{{STATIC_URL}}websec/external/require.js"></script>
<script>
//defines the requirejs configuration in general.
{% include "websec/requirejs_config.html" %}
</script>
{% endblock requirejs %}
Unrelated, but a nice hack to get rid of debug_toolbar during unit testing:
settings.py
INSTALLED_APPS = (
'django.contrib.auth',
#... more stuff
'debug_toolbar',
'myapp',
'djcelery',
'crispy_forms',
)
#hack, but debug toolbar totally collapses performance on some tests
pgm = os.path.basename(sys.argv[0])
if pgm.startswith("test") or pgm.startswith("nosetests"):
li = [app for app in INSTALLED_APPS if not app == "debug_toolbar"]
INSTALLED_APPS = tuple(li)

TinyMCE Select text area by parent form ID

I was looking at http://archive.tinymce.com/wiki.php/Configuration3x:editor_selector
Is there a way to select a textbox only if it is under a <form> with a specific id?
For example:
<form id="edit_page">
<textarea></textarea>
<form>
I know I can add a class to the text area to get it to work, but I'm hoping for this solution for my use case. As I'm using Active_admin and I don't want to have to redefine all the default forms for it.
If you are okay with using the tinyMCE jquery plugin, you can select the elements using the jquery selector($) and then call the tinymce function over that.
Codepen example is here.
JS Part:
$(function() {
$('#edit_page2 textarea').tinymce({
script_url : 'https://cdnjs.cloudflare.com/ajax/libs/tinymce/4.3.4/tinymce.min.js',
theme : "modern",
mode : "textareas"
});
});
HTML Part :
<form id="edit_page1">
<textarea></textarea>
</form>
<form id="edit_page2">
<textarea></textarea>
</form>
<form id="edit_page3">
<textarea></textarea>
</form>
This will select based on the parent's id. You can use any type of css selector.
You will need to include jquery and tinyMce's jquery file
//cdnjs.cloudflare.com/ajax/libs/jquery/2.1.3/jquery.min.js
https://cdnjs.cloudflare.com/ajax/libs/tinymce/4.3.4/jquery.tinymce.min.js

How to Connect JQuery-UI Autocomplete Widget to Django-Dajax?

I have a Django app for which I am trying to build an autocomplete UI for making selections. As the user types into a text box and the the app should make search suggestions based on values retrieved from the database. I want to use Dajax/Dajaxice to handle the ajax and the jquery-ui autocomplete widget for the UI template. Can someone please explain how to get the jquery-ui autocomplete widget to call my dajax function via the autocomplete source attribute (or any other better way)?
My code is a combination of this dajax example and this jquery-ui autocomplete example.
my_app/ajax.py:
from my_app.models import MyModel
from dajax.core import Dajax
from dajaxice.decorators import dajaxice_register
from django.utils import simplejson
#dajaxice_register
def auto_filter_selections(request, symbol):
dajax = Dajax()
result = MyModel.objects.filter(symbol__startswith = symbol)
dajax.assign('#id_symbol', 'innerHTML', str(result))
return dajax.json()
template: my_app_ui.html
<head>
....
<script type="text/javascript" src="{{STATIC_URL}}jquery/jquery-1.7.2.js"></script>
<script type="text/javascript" src="{{STATIC_URL}}dajax/jquery.dajax.core.js"></script>
<script type="text/javascript" src="{{STATIC_URL}}/jquery/jquery-ui-1.10.4.custom/js/jquery-ui-1.10.4.custom.js"></script>
....
<script>
$(function() {
$("#id_symbol").autocomplete({
source: "Dajaxice.ui.auto_filter_symbols(Dajax.process,'symbol':$('#id_symbol').serialize(true)});",
minLength: 1 //We want to search for even one character
});
});
</script>
....
</head>
<body>
....
<div class="ui-widget">
<label for="id_symbol">Symbol: </label>
<input id="id_symbol">
</div>
....
</body>
If you notice above, I am using jquery-1.7.2 and jquery-ui-1.10.4. This is because the dajax documentation says it is compatible with jquery-1.7.2. Not sure if I can use a newer jquery version.
I am not sure how to get the template's javascript to call my dajax function. the jquery-ui documentation for .autocomplete says to use its source attribute but does not give a very good example. Can anyone tell me if the above is correct?
Once the dajax view function retrieves suggestions from the DB, how do I populate the .autocomplete text box with it?
I've done quite a bit of research over the past few days but there are few examples of Django-Dajax-JQueryUI applications out there.
After considerably more research and piecing various clues together I finally found the key to getting the JQuery-UI autocomplete widget to work with Dajax/Dajaxice and Django.
Synopsis:
Initialize JQuery-UI .autocomplete's source option with a separate javascript (JQuery) setter function
Connect .autocomplete widget's source option to server-side function with a Dajaxice command via a third javascript function.
Explanation:
Firstly, it is important to remember that Dajaxice provides the ajax communication channel by which your client-side javascript and the server side code transact, whereas Dajax provides a convenient way of generating the javascript (JSON) code that will perform the desired function in the client browser without having to write any javascript.
Since JQuery-UI provides us with the javascript widget (in the form of jquery) to perform our desired function (.autocomplete), we do not need Dajax and will not, according to my findings, have any success with it in this application. We ONLY need Dajaxice in order to connect the client side javascript (.autocomplete) to our server side function that does the searching and returns results to suggest to the user.
Below is my revised code that I have gotten working. Note that I have only tested it on a development platform thus far.
my_autocomplete_search_template.html with extraneous code removed:
<head>
....
<script type="text/javascript" src="{{STATIC_URL}}jquery/jquery-1.7.2.js"></script>
<script type="text/javascript" src="{{STATIC_URL}}dajax/jquery.dajax.core.js"></script>
<script type="text/javascript" src="{{STATIC_URL}}/jquery/jquery-ui-1.10.4.custom/js/jquery-ui-1.10.4.custom.js"></script>
....
<script>
//Initialize the source option of .autocomplete widget during page load
//See: http://api.jqueryui.com/autocomplete/#method-option setter example code
var local_source = ["Abc",] //Dummy string for initialization only; temporary value
$("#search_box").autocomplete("option", "source", local_source)
</script>
<script>
//.autocomplete widget is Dajaxice callback function
// *data* comes from server-side function
function search_result(data) {
$("#search_box").autocomplete({
source: data,
});
}
</script>
<script>
//This function called by user <input> tag and calls the server-side function via Dajaxice command
function dajax_filter_search_term() {
Dajaxice.my_app.auto_filter_search_term(search_result,'search_term':$('#search_box').val()});
// | | | ----------------------
// | | | |
// server-side .autcomplete serverside user-typed
// function callback func. func. argument value to search
}
</script>
....
</head>
<body>
....
<div class="ui-widget">
<label for="search_box">Search Term: </label>
<input type="text" name="search" id="search_box" onkeyup="dajax_filter_search_term()" />
</div>
....
</body>
my_app/ajax.py
from dajaxice.decorators import dajaxice_register
from django.utils import simplejson
from my_app.models import MyModel
#dajaxice_register
def auto_filter_search_term(request, search_term):
# Query DB for suggestions to present to user
q = MyModel.objects.filter(myfield__istartswith=search_term)
data = []
for item in q:
# Format returned queryset data, if needed
data_item = item.field1 + ', ' + item.field2 + ', ' + item.field3
data.append(data_item)
# Return data to callback function *search_result*
return simplejson.dumps(data)
That is it! The key is to initialize the .autocomplete source option first and to only use Dajaxice to communicate with the server-side function.
It is possible to optimize the client-side code further by getting rid of the javascript function dajax_filter_search_term() and including the Dajaxice call in the #search_box input tag like so:
<input type="text" name="search" id="search_box" onkeyup="Dajaxice.ui.auto_filter_search_term(search_result, {'search_term':$('#search_box').val()})" />
References
JQuery-UI API docs: http://api.jqueryui.com/autocomplete/#method-option:
Additional clues here:
Django, JQuery, and autocomplete

How to change a css item in a template using django admin?

I would like to know if there is some way to change a css class from a template page using django admin.
I would like to put the django tag inside of css file.
example:
body {
    background-color: {{ body.color }};
   width: {{ body.width }};
}
You could also include your CSS file using template tags. That would demand a style tag but considering the dynamic approach here it's really not much of an issue:
<html>
<head>
<style type="text/css">
{% include 'templates/mytemplate.css' %}
</style>
</head>
<body></body>
</html>
The template could then be what you described above. Then the CSS template would have access to whatever data your base template has access too.
Depending on your use case you might also do something with blocks but I'm not sure that is worth exploring at this point.
Just off the top of my head:
Create a Model to store the CSS values you want
Register the Model to show up in the Admin Screens
In your views, return these values as a dictionary
In the template, use the values as you suggested