Can I use the django debug_toolbar on the admin pages? - django

(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)

Related

Stylized HTML fields in Django admin

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,
),
}

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'> })

Recursive app dependencies django

Django's INSTALLED_APPS is a list in the settings file, which ultimately the devops girl/guy of the wsgi-application is responsible for.
However, when creating an app, I often use templates and templatetags of other apps, for example django-bootstrap4. Consider a simple bootstrap4-with-butter app, that only proivides this template:
bs4wb/templates/bs4wb/base.html
{% extends "bootstrap4/bootstrap4.html" %}
{% block bootstrap4_extra_head %}
<script> confirm('with butter?!'); </script>
{% endblock bootstrap4_extra_head %}
It is not enough for the devops to install and add to INSTALLED_APPS
bs4wb, they also need to do the same for django-bootstrap4, and moreover, he/she also needs to keep track of the version I used whenever I upgrade from django-bootstrap4 to django-bootstrap5` or something. That is, I need to document an extra step which can change.
How can I specify recursive INSTALLED_APPS? Is there something like an app-union? For example (obviously ugly syntax, sorry):
export('bs4wb', app_by_union(['bootstrap4', 'bs4wb']) )
which would insert bootstrap4 and bs4wb next to each other whenever bs4wb is added to INSTALLED_APPS?
Or should I just raise an exception if bootstrap4 was not added to installed apps, stating that it should be added.
As an app developer you usually use django's system check framework to ensure the environment fits your requirements.
In your case you'll check whether another app is included in INSTALLED_APPS and add an error, if not.
It might look like that (untested):
from django.conf import settings
from django.core.checks import Error, register
#register()
def bootstrap4_check(app_configs, **kwargs):
errors = []
if not 'django-bootstrap4' in settings.INSTALLED_APPS:
errors.append(
Error('django-bootstrap4 needs to be in INSTALLED_APPS')
)
return errors
If your requirements change with new versions: adjust the checks.

How to import dajaxice?

I'm a nooby to django and I tried many hours to get a simple example of dajaxice running, but I don't seem to find the right way to look for the files.
I did and redid installation and tried to find answers in the numerous similar questions on stackoverflow like this one and this one.
I put {% dajaxice_js_import %} in the header of myapp_index.html which prints out as:
<script src="/static/dajaxice/dajaxice.core.js"
type="text/javascript" charset="utf-8"></script>
but it cannot find this file:
ImproperlyConfigured: The storage backend of the staticfiles finder doesn't have a valid location.
And the get fails:
GET /static/dajaxice/dajaxice.core.js HTTP/1.1" 500 59
Strangely enough dajax loads:
<script type="text/javascript"
src="{% static /static/dajax/jquery.dajax.core.js" %}"></script>
Here's my folder structure:
myproject
----manage.py
----myproject
--------settings.py
--------urls.py
----myapp
--------ajax.py
--------urls.py
--------templates
------------myapp_index.html
I also haven't really understood why we need two urls.py files, but somehow it seems to access myapp_index.html if I put
from django.views.generic.simple import direct_to_template
and then
url(r'^$', direct_to_template, {'template': 'myapp_index.html'}),
in myapp's url patterns.
I also tried uncountable filenames in
python manage.py findstatic dajaxice.core.js
but somehow it doesn't find dajaxice, even though dajaxice is installed and accepted in the settings.py file among the INSTALLED_APPS.
Also python manage.py collectstatic fails for the same reason, but if I understood correctly, I don't event have to make it run as long as I'm on a development server.
I guess I have some basic misunderstanding of the underlying structure. :(
I'm using the prepacked latest ubuntu packages:
django: 1.4.5,
dajaxice: 0.5.5
Thanks in advance for any hint!
here is the template file:
{% load static %}
{% load dajaxice_templatetags %}
<html>
<head>
<title>My base template</title>
{% dajaxice_js_import %}
<script type="text/javascript" src="{% static "/static/dajax/jquery.dajax.core.js" %}"></script>
<script type="text/javascript">
function my_js_callback(data){
alert(data.message);
}
Dajax;
Dajaxice;
</script>
</head>
...
<button onclick="Dajaxice.myproject.myapp.sayhello(my_js_callback);">Click here!</button>
I get no Django error, the page shows, but I get this in Firebug:
"NetworkError: 500 Internal Server Error - http://localhost:8000/static/dajaxice/dajaxice.core.js"
and this:
ReferenceError: Dajaxice is not defined
Dajaxice;
It seems that you've messed up your urls.conf. It should contain something like:
url(dajaxice_config.dajaxice_url, include('dajaxice.urls')),
Does it?
Also, the STATICFILES_FINDERS section of your settings.py file should include:
'dajaxice.finders.DajaxiceFinder',

Django: Using Different Templates for Production

In my base.html template which is imported in every Django template, I have a block with the Google Analytics script:
<!-- GOOGLE ANALYTICS -->
<script type="text/javascript">
bla bla...
</script>
I would like to have this script only included when in production, but not during development. Is there a solution for this comparable to the solution in setting.py?
import socket
if socket.gethostname() == 'my-laptop':
DEBUG = TEMPLATE_DEBUG = True
else:
DEBUG = TEMPLATE_DEBUG = False
Anybody who knows of a template tag, or should I do my own one?
You could add your DEBUG variable to a context processor and just put an IF around that block.
http://docs.djangoproject.com/en/dev/ref/templates/api/#subclassing-context-requestcontext
from django.conf import settings
def debug_context(request):
return {'DEBUG': settings.DEBUG}
Then in your template:
{% if DEBUG %}
STUFF
{% endif %}
Alternatively you could make the context processor return anything you want to key off of, anything in your settings file or otherwise.
In settings.py,Check debug is True, also add:
INTERNAL_IPS = (
'127.0.0.1',
'localhost',
)
Then you can use things in your template like this:
{% if debug %}
<span>This is debug</span>
{% else %}
<span>This is production</span>
{% endif %}
If want to change to production, set debug to False.
Ref:
http://www.djangobook.com/en/2.0/chapter09.html
Haven't seen such a template tag yet.
I'm more inclined to use different setting.py files for production and dev, and include analytics code as described in this answer.
This solution allows for a simpler template, and gives you the option to use different analytics code in different deployments of the same app.
The template is probably not the best place to handle this.
usually, you'd write a context preprocessor that will process the request and add a flag to your context (such as PRODUCTION = True). Then you can use a simple if statement in the template, or better yet, write a simple custom tag that will hide that implementation detail from the template.
See also this answer.
I do the following on lots of sites and it seems to work really well.
Then in my static media file directory I have a copy of base.css and base_dev.css. I do all my development in the base_dev.css file then merge it into base.css before I launch.