Jinja2 failsafe template rendering - python

I'm using Jinja2 to create email notification messages, mostly error notifications.
Sometimes I get invalid or incomplete data about the error details. After all, it is an error condition when this happens. E.g. a dict item might be missing or value type could be incorrect. In such case the rendering fails, but the error notification has to be sent.
I can do following in the exception handler:
To send the template as-is. At least the type of error event will be known to the recipient, but important data will be missing
To send a backup template with just raw data. That would mean to maintain two versions of each message.
What I really want is to render the template on a "best-effort" basis skipping all errors. Is there a way to do that in Jinja2?

Use defaults everywhere it's necessary. Also handles undefined variables:
default(value, default_value=u'', boolean=False):
If the value is undefined it will return the passed default value, otherwise the value of the variable:
{{ my_variable|default('my_variable is not defined') }}
This will output the value of my_variable if the variable was defined, otherwise 'my_variable is not defined'. If you want to use default with variables that evaluate to false you have to set the second parameter to true:
{{ ''|default('the string was empty', true) }}
Or you could use none if you're creating more complex structures based on the data:
{% if p is not none %}
{{ p.User['first_name'] }}
{% else %}
NONE
{%endif %}

Related

Django messages repeating itself instead of updating

classinfo = EventType.objects.all()
length = EventType.objects.all().count()
for i in range(length):
messages.success(request, classinfo[i])
So I'm using this to print out a list of all events that are located in EventType.
Image
This is what it looks like. However, when I add another item to the list using the Class Add feature, it repeats the original list again and then adds the additional event. But if I add an additional another event after this it will add it to the list properly. How can I go about fixing this so it doesn't repeat the initial list when I add an event?
How it looks right now when I add an event
This is what the HTML looks like:
{% for message in messages %}
<li>
{{ message }}
</li>
{% endfor %}
By default Django uses the FallbackStorage class, which means that it will first use CookieStorage to store your messages. As long as your cookie does not exceed 2KB, then it will store all your messages in a cookie. What you are doing is creating messages when you run your request, all these messages are stored in a cookie. You then add an event and make another request. The view will then loop through all your EventType's again and add it to the cookie, along with the original content of the cookie from the first request. I don't know why when you add an EventType a third time it shows the list properly, perhaps you are at the size limit of the cookie and it drops its existing contents.
Do you really need to store messages? Messages are used as a logging mechanism. You are using messages to iterate over a model, which is unnecessary. Simply pass classinfo into your context dictionary and iterate over it instead of messages:
view.py:
classinfo = EventType.objects.all()
template.html:
{% for event in classinfo %}
<li>{{ event }}</li>
{% endfor %}
Better yet, simply use Django's ListView, this is exactly what it is for:
class EventListView(ListView):
model = EventType

Django - Checking if objects exists and raising error if it does

I need to check if an object exists in a table. If the object doesn't exist I want to save it, if it does exist I want to refresh the page and tell the user that the identifier (quote number) already exists. I have this example code:
def some_method(request):
if request.method == 'POST':
form = SomeForm(request.POST)
if form.is_valid:
quote = request.POST.get('quote')
if SomeModel.objects.get(quote_number = quote).exists():
refresh with error #not sure how to do
else:
save object #I can do this part
The problem I am running into is that when I check if the object exists (and it does) then the code will raise an error saying the object doesn't exist before it hits the if. Which means on the webpage is full of coding information rather than the refresh with a message for the user.
I want to be able to have a little pop up message or something for the user to enter a new quote number rather than having the developer error page show up.
Am I going about this the correct way?
The problem is get returns an object, but exists only works with querysets. A Model.DoesNotExist error will be raised when you use get and the object does not exist.
You should use filter instead of get:
qs = SomeModel.objects.filter(quote_number = quote)
if qs.exists():
...
else:
...
I would do something like this:
from django.http import HttpResponseRedirect
from django.contrib import messages
...
try:
some_object = SomeModel.objects.get(quote_number=quote)
message.warning(request, 'some message')
return HttpResponseRedirect('some-url')
except SomeModel.DoesNotExist:
some_object = SomeModel.objects.create(quote_number=quote)
...
And in the template you can display the message just like so:
{% if messages %}
{% for message in messages %}
{{message}}
{% endfor %}
{% endif %}
You can use get_or_create. It returns a tuple with object retrieved (or created) and a boolean value. If boolean value is True when it's a new object, and if it's False when object already exists and you can throw desired error.

Django Pagination KeyError 'source'

I installed Django template pagination tag from: https://github.com/jmcclell/django-bootstrap-pagination and while I followed all the instructions, I'm getting 'source' errors.
Apparently I'm doing something wrong.
===========================================
EDIT 3
SETTINGS.py
TEMPLATE_CONTEXT_PROCESSORS = (
"django.contrib.auth.context_processors.auth",
"django.core.context_processors.request",
)
VIEW.py:
def pagination(request):
location = Location.objects.all()
return render_to_response('pagination.html',
location,
context_instance=RequestContext(request))
TEMPLATE
{% load bootstrap_pagination %}
<h1>Location</h1>
{% for location in location %}
<h2>{{ location.name }}</h2>
{% endfor %}
{% bootstrap_paginate location %}
ERROR:
AttributeError at /pagination/
'str' object has no attribute 'paginator'
Request Method: GET
Request URL: http://127.0.0.1:8000/pagination/
Django Version: 1.5.4
Exception Type: AttributeError
Exception Value:
'str' object has no attribute 'paginator'
Error during template rendering
In template /home/jr/Documents/python/amapp/sdr/article/templates/pagination.html, error at line 7
'str' object has no attribute 'paginator'
1 {% load bootstrap_pagination %}
2
3
4
5
6
7 {% bootstrap_paginate location %}
I'm the author of the library, but you must forgive me as I haven't used Python in quite some time and I've core dumped most of my knowledge about this library.
So, #WayBehind was correct, your first mistake was using "page_obj". That was simply an example. In your case, you want to use "location"
However, you never got to see that error because you have a more pressing error which is that the library isn't playing nice with your setup. I wrote this library with Python 2.7 with the request context preprocessor. Please double check that you have the context preprocessor enabled as per the documentation and please be sure you are using Python <3.0. I know for a fact the library does not currently working on 3.x. There is a fork of the library where some other folks have been working to fix that and I am actively keeping an eye on it to pull those changes in when ready, but as of now it just doesn't work.
If you are using Python 2.x and you have the request context preprocessor enabled, I am not sure why you would be getting that error. If you can confirm those two things are true, I'll be happy to take a closer look tomorrow.
Edit:
This may or may not be an issue, but I notice that you loop through your Location object using the same variable name for the instance:
{% for location in location %}
<h2>{{ location.name }}</h2>
{% endfor %}
{% bootstrap_paginate location %}
Is it possible that Django's template scoping is such that the object you are passing to bootstra_paginate is the last instance of "location" rather than the entire set? This is an off the cuff guess as a first stab at this because otherwise things appear to be correct.
Have you followed all the steps ?
Request is in the context_processor (settings.py)?
TEMPLATE_CONTEXT_PROCESSORS = (
....
"django.core.context_processors.request",
....
)
You are using obj_list in the template, but do you have anything inside obj_list ? Maybe you have to use "location" instead of "obj_list" ? Because I think your object list is inside location (Location objects) but you are using obj_list like in the example. In the example obj_list is just a variable example for an object list.
EDIT:
Change this:
def pagination(request):
args = {}
args.update(csrf(request))
args['location'] = Location.objects.all()
return render_to_response('pagination.html', args)
for this:
from django.template import RequestContext
def pagination(request):
location = Location.objects.all()
return render_to_response('pagination.html', 'location':location,context_instance=RequestContext(request))
Using page_obj worked for me, for anyone wondering why this doesn't work when using Django 2.0.2 and django-bootstrap4 0.0.6.
I came across this by digging through the context variables in the error message that showed up from Django's DEBUG mode.

Print None object properties as empty string

I am passing an object to my Jinja2 template to be used as form values.
If a property doesn't exist, it is printing None as a string, where I would like it to just print an empty string (aka, nothing).
As there are a lot of properties for the object, I wish to avoid string coercion at the Controller level.
My current Jinja code looks like:
value="{{ my_object.my_property }}"
Try:
{{ my_object.my_property if my_object.my_property != None }}
I was able to utilize the following, which is not too long:
{{ my_object.my_property or '' }}

Django: is_authenticated and is_anonymous both return true after logout

I am using django-registration, and just set it up.
{{user.is_authenticated }}
is true, even though i went already to /accounts/logout/ and logged the user out.
{{user.is_anonymous }}
returns true also. According to django docs, those two should be different:
is_anonymous: Always returns False. This is a way of differentiating User and AnonymousUser objects. Generally, you should prefer using is_authenticated() to this method.
and
is_authenticated: Always returns True. This is a way to tell if the user has been authenticated. This does not imply any permissions, and doesn't check if the user is active - it only indicates that the user has provided a valid username and password.
I am using the standard views that come with django-registration and haven't touched them yet. In the tempalate i have the following code:
{% if user.is_authenticated %}
{% user }}
{% if user.is_anonymous %}
is anonymous
{% endif $}
{% else %}
gotta login
{% endif %}
Where would the problem be? I will be really thankful!
UPDATE:
i have noticed that on the homepage, it both is_authenticated and id_anonymous return True, while if i go to /accounts/login before loging in, only is_anonymous returns true as it should be. And also, on the homepage, i have the following view if that helps:
def home(request):
return render_jinja(request, 'index.html', blah = 'ga')
UPDATE 2:
print(request.user.is_authenticated()) gives False. Then, i have:
return render_jinja(request, 'index.html', blah = 'ga')
and in the template, user.is_authenticated returns FALSE.
UPDATE 3:
If i use render_to_response, instead of render_jinja, all is good. still don't know how to fix this though :(
It seems like you are trying to figure out two things at once; what is the correct way to use jinja templates and what is the deal with User/AnonymousUser. Maybe try to figure out these problems one at a time.
I have no experience with jinja but you may want to check and make sure that you are taking differences between jinja and django template syntax into account. The biggest difference I know of is that a method call requires explicit parenthesis. So the is_authenticated and is_anonymous calls in your example need parenthesis.
Jinja style {{ user.is_authenticated() }}
Django style {{ user.is_authenticated }}
If that does not solve the problem, try installing django-debug-toolbar and take a look at the context for your template. Check and see if user is None or an object (User or AnonymousUser).
You can also read up on AnonymousUser and see an example of checking for an authenticated user in the docs. In a nutshell, for an AnonymousUser object is_anonymous() always returns True instead of False and is_authenticated() always returns False instead of True.
Smack on the head. I read somewhere:
if user.is_authenticated:
....# Always true, since it is a method!
And so, instead of having {{user.is_authenticated}} in template, it should be {{user.is_authenticated()}}

Categories