Django: How to get template debug info to user on syntaxerror - python

I have a page where users can submit their own template into a textarea, for now it needs to be that simple. However since it is user generated input i need to verify they don't do stuff that breaks the rest of my application and simultaneously giving useful feedback as to what they did wrong if they did something wrong.
For the purpose of giving useful feedback i want something similair looking to what django supplies me with (using django 1.4):
The above bit in particular. I'm putting the user template in a django template so i don't have to verify syntax errors and stuff myself. That looks something like this:
try:
template = get_template_from_string(user_input)
template.render(context=Context())
except:
do something to ouptut the error
The render call is needed otherwise not exceptions are thrown at all.
I have tried printing the exception and it's arguments, but that only gives very limited info. I also tried using traceback but that never gives back line numbers or anything within the template, only to where the exception is thrown in the python code. I also couldn't find anything using Google and my search through Django source code have left me wondering where the actual error page is generated...
So basically my question is; how do i get the bit of information shown in the image?
EDIT
To clarify: I want users to be able to make templates so these templates can be used while sending emails. Since the Django templating engine is already present I figured i would use that one instead of something else.
The templates themselves are made safe using this snippet and some parsing of my own for the variables. Everything works so far except the useful debug message to the user. So far all I've got on that is: "Something went wrong while parsing your template, unexpected block tag extends" (for example), which I would like to be more like the image shown above.

I just hit same issue. In my case it isn't required to have such a detail traceback. So I did it like that, using models clean method
from django.core.exceptions import ValidationError
from django.core.urlresolvers import NoReverseMatch
from django.db import models
from django.template.base import Template, TemplateSyntaxError
from django.template.context import Context
class MyTemplate(models.Model):
template = models.TextField()
def clean(self):
try:
Template(self.tempalte).render(Context({}))
except TemplateSyntaxError as tse:
raise ValidationError('TemplateSyntaxError: %s' % tse)
except NoReverseMatch as nrm:
raise ValidationError('NoReverseMatch: %s' % nrm)

Related

Django: Context processors for error pages (or request in simple_tag)

In my Django project, there are several django apps. I want to write custom error pages, and I want them to contain correct links to the application that the errors happened in, for example, if a 500-error happened in my app a, I want the error page contain a link to /a/index.html, and if a server error happened in app b, I want the page to contain the link to /b/index.html. And I want to create only one copy of each of the error page files, which means I need to get the name of the app from within the template.
To that end, I have written a custom context processor that adds the app_name var to the templates. I tested it on my normal pages, but when I went on to test it on the error pages, turns out that the context processor isn't firing.
Similarly, I have written a template tag app_aware_url which takes the name of the url pattern and tries to resolve it, but again, turns out that for the error pages the simple_tag(takes_context=True) receives a context that does not contain the request (which is needed for telling which app I am in).
Is there a way round it, or is there a better solution to my problem altogether?
(Django is 1.11)
The correct way to do this seems to be a custom error handler. The documentation on this is a bit... Light? So I had to do some experimentation to get it to work.
It turns out that that's only possible to do globally, in your base urls.py, so you can't have a custom one for a specific app, (which is sad), but you can work out the app from the request.
In your base urls.py:
from .views import ErrorHandler
handler500 = lambda request: ErrorHandler.as_view()(request)
This will now defer to views.ErrorHandler on every 500 that happens:
class ErrorHandler(TemplateView):
template_name = '500.html'
def get_app_name(self):
module = self.request.resolver_match.func.__module__
app, *path = module.split('.')
return app
def get_context_data(self, *args, **kwargs):
context = super().get_context_data(*args, **kwargs)
context['app_name'] = self.get_app_name()
return context
Here, .get_app_name() is doing the magic. It inspects the request's resolver_match - it's path through the urls machinery, and determines which app it was in based on that.
From here, the sky's the limit. I simply subclassed TemplateView for easiness' sake, but you can defer to django.views.defaults.server_error() if you want. Whatever your heart desires.
If you wanna get more hands-on, I've thrown together a repo, (commit b638f8 at the time of writing), which you can feel free to check out and much around with.
For those who may step on the same rakes:
What I ended up doing is passing the "app_name" variable from the error page views to the context. Thus the variable is in the context for non-error pages, because it is set by the context processor, and it is there for error pages because it is passed manually. Doesn't look like a good solution though.

What template is loaded by Django?

I was following Django tutorial, and got stuck where it asked me to replace the default template for administrative part of the site with my own. The problem was a typo in the template's name. I suspected there must be a problem like that, but to troubleshoot the problem it'd be very helpful to see some kind of report from Django on what template it used to render a particular page. Is there any way to do this?
First if you have set DEBUG = True django automatically gives you information about where django was looking for templates (in general and especially in case it didn't find one)
You should see something like this:
second you can add the popular django plugin django-debug-toolbar. It can show you for each request what templates were used and what their context was.
see: https://django-debug-toolbar.readthedocs.io/en/stable/panels.html#template
Still, not exactly the answer, but something to get me closer. One could start Django shell, and then try this:
>>> from django.template.loader import get_template
>>> get_template('template/name').origin.name
to find out what template was actually used. This is still not enough to see though which templates were considered while resolving the template.

Django - beginner- what is the process for passing information to a view via a url?

I am working on my first django project which is also my first backend project. In the tutorials/reading I have completed, I haven't come across passing information back to django without a modelform.
My intention is to calculate a value on a page using javascript and pass it to django when a user hits a submit button on that page. The submit button will also be a link to another page. I know I could process the information in a view via the url if I knew how to pass the information back to django.
I'm aware that django uses MVC and as I have my models and views in place, I am lead to believe that this has something to do with controllers.
Basically, I would like to know how to pass information from a page to django as a user follows a link to another page. I understand that this isn't the place for long step by step tutorials on specific topics but I would appreciate any links to resources on this subject. I don't know what this process is even called so I can't search documentation for it.
EDIT:
From further reading, I think that I want to be using the submit button to GET or POST the value. In this particular case, POST is probably better. Could someone confirm that this is true?
Yes, generally POST is a better way of submitting data than GET. There is a bit of a confusion about terminology in Django. While Django is, indeed MVC, models are models, but views are in fact controllers and views are templates. Since you are going to use AJAX to submit and retrieve the data, you don't care about templates. So what you most likely want is something like this
in your urls.py as part of your urlpatterns variable
url(r'mything/$', MyView.as_view())
in your views.py
from django.views import View
from django.http import HttpResponse
class MyView(View):
def post(self, request):
data = request.POST
... do your thing ...
return HttpResponse(results)
and in your javascript
jQuery.post('/mything/', data, function() { whatever you do here })
There're many ways, you can achieve this in django. Following are the two ways, that I generally prefer :-
1) As a query string parameter in the URL
eg. http://localhost/getPatientInfo?patientId=23&name=Sachin
2) Making URL dynamic, to include the information in the view itself.
eg. http://localhost/patientInfo/23/Sachin
In case 1:-
You will have to do,
patientId = request.GET["patientId"]
name = request.GET["patientName"]
In case 2:
Your URL conf will be something like :
urls = [
url("^patientInfo/(\d+)/([^/]+)$", yourViewFunc)
]
And in your view func :-
def yourViewFunc(request, patientId, patientName):
# your logic goes here
pass
For info. related to URLConf, refer to https://docs.djangoproject.com/en/1.10/topics/http/urls/#example

Python/Django: automatically log when exceptions occur, including request info

I have created a function log_error(request, traceback), which I call in my exceptions. This writes error information to the database. Now, before I open up every one of my views and add this in an exception handler, is there a way to automatically have all exceptions raise to a function, which then calls this?
I have seen this Python error logging, which says to write your own version of sys.excepthook. This function is automatically called when there is an exception. I tried this, but my_excepthook was not called even though I copy-pasted the solution into views.py and raised an error. However, I didn't try too hard because it's not getting all the information that I need, anyway. I also need request so I can log information abut the user, url, etc.
Maybe that's asking too much?
(I'm using Django, but this does not seem like a Django-specific thing) Edit: yes, it is.
J.F Sebastian's suggestion worked. This is a Django solution.
In settings.py MIDDLEWARE_CLASSES:
(I added it as the last one, not sure if this is right or will cause errors down the line. Works for now.)
'myapp.middleware.ExceptionMiddleware',
In myapp.middleware.py:
import traceback
class ExceptionMiddleware(object):
def process_exception(self, request, exception):
log_error(traceback, request)
That's it. log_error is my function and writes to the database. It also appears from the documentation https://docs.djangoproject.com/en/dev/howto/error-reporting/ that I can get the local variables as well as the request attributes.

Suddenly all HttpResponseRedirect( reverse() ) are giving syntax errors

This one has me scratching my head. I have an app with views that do form processing (logins/signup) and then return various HttpResponseRedirect()s based on the input. All of those redirects contain reverse() lookups with the appropriate functions listed as strings. And every function has a corresponding urlpattern in urls.py.
Everything was working fine until this morning.
Now, whenever I submit a form, Django gives me a syntax error for a non-existent line:
SyntaxError at /logout/
invalid syntax (views.py, line 399)
(That file only has 354 lines)
When I scroll down to look at the traceback, the line that's highlighted is always one with a return HttpResponseRedirect( reverse('app.views.func') ).
Because of these bewildering error messages, I'm not even sure that the problem is really with the HttpResponseRedirect( reverse() )s. I haven't touched any of that code in a few days, so I'm not sure why it would suddenly start throwing out weird errors like that.
Any help debugging this would be much appreciated!
I finally figured it out after consulting the docs for the reverse() function.
When you call reverse(), django first imports your project's URLConf files, which in turn imports every single view module that is declared in your URLconf. My issue was that I was working on a new, totally unrelated view that had a syntax error (on line 399!).
So even though I wasn't viewing a page that was doing anything with the new view, my old view was still getting tripped up with the syntax error because of how reverse() works.
From the docs:
Make sure your views are all correct. As part of working out which URL
names map to which patterns, the reverse() function has to import all
of your URLconf files and examine the name of each view. This involves
importing each view function. If there are any errors whilst importing
any of your view functions, it will cause reverse() to raise an error,
even if that view function is not the one you are trying to reverse.
Make sure that any views you reference in your URLconf files exist and
can be imported correctly. Do not include lines that reference views
you haven't written yet, because those views will not be importable.

Categories

Resources