Altering the default Django messages tag - python

By default, messages.success outputs class='success'. Either I need to overwrite this, remove it or append my tags to it to meet my needs. I've been unsuccessful in finding a way to overwrite and append to it. Here I've attempted to use extra_tags...
views.py
messages.success(request, 'Item Saved', extra_tags='html_safe alert alert-')
detail.html
I've tried adding alert before the {{ message.tags }}.
{% if messages %}
<ul class='messages'>
{% for message in messages %}
<li{% if message.tags %} class='{{ message.tags }}' role='alert'{% endif %}>{% if 'html_safe' in message.tags %}{{ message|safe }}{% else %}{{ message }}{% endif %}</li>
{% endfor %}
</ul>
{% endif %}
Bootstrap Alerts expects class='alert alert-success'
<div class="alert alert-success" role="alert">
<strong>Well done!</strong> You successfully read this important alert message.
</div>
HTML page source result
When all the code above is executed, the source code outputs the following. The only problem now is the space between alert- and success.
<ul class='messages'>
<li class='html_safe alert alert- success' role='alert'>Item Saved</li>
</ul>
End Goal! - Can anyone see a hacky or proper workaround here?
<li class="html_safe alert alert-success" role="alert">Item Saved</li>

I don't see why there is a reason to remove it. Unused CSS classes are not a terrible thing. Change it to:
messages.success(request, 'Item Saved', extra_tags='html_safe alert alert-success')
and don't worry about the extra success class. Have you checked Django's source code for this? Sometimes it gives hints as to extra kwargs that can be passed. The end result will be:
<li class="html_safe alert alert-success success" role="alert">Item Saved</li>
It seems there are also ways to configure the default message tags.
For example, in your settings.py:
from django.contrib.messages import constants as messages
MESSAGE_TAGS = {
messages.SUCCESS: 'alert alert-success',
}
But some Django projects and documentation may assume that the tag here is success, the default, so only change this if you are confident it is what you need. Otherwise, just add the Bootstrap class as an extra tag. Really, you shouldn't override Django's default just to suit Bootstrap. Either change Bootstrap's success class during customization to success, or just use both.

Related

Django FormView dont redirect on successfull POST

I'm currently writing a Chat-Application. In order to take User-Input, I use a Form and thus a FormView and a custom Form, following the Django-Docs. I have to set a success_url, but neither want to be redirected to another page on a successful POST, nor want i to reload the chat-page. How Do i go about that?
I've read the following Post: Django FormView, Return without redirecting, but I dont really understand the Point of the Answer. Should I really avoid not redirecting? And if not, can I really achieve this with JQuery-Ajax? Doesn't that conflict with Django's success_url?
Thanks for your Answers!
After successful post operation, you can simply redirect URL to success url like:
success_url = '%s?success=1' % reverse('index_page')
return HttpResponseRedirect(success_url)
and in your html template check
{% if success == 1 %}
<h3> Data uploaded successfully</h3>
{% endif %}
or
You can use django messaging and override the get_success_url method like:
from django.contrib import messages
def get_success_url(self):
messages.add_message(self.request, messages.INFO, 'form submission success')
return reverse('index_page'))
and on template do like this:
{% if messages %}
<ul class="messages">
{% for message in messages %}
<li{% if message.tags %} class="{{ message.tags }}"{% endif %}>{{ message }}</li>
{% endfor %}
</ul>
{% endif %}

Pelican -- 'articles_page' is undefined

I've created my own theme for Pelican and I've been using it for a while to build my site. I've decided to start blogging again so I'm only now adding the blog features to the site.
I've created my own blog.html template to render the content in the way I want. I started by copying and pasting the code from the 'simple' theme that comes with Pelican to get me started, but even though it is unchanged I'm getting an 'articles_page' is undefined error when I try to build.
Where is the article_page variable set from? I tried adding to my pelicanconf.py file but it didn't help.
{% extends 'base.html' %}
{% block title %}{{ page.title }} — Ricky White{% endblock title %}
{% block content %}
<section class="wrapper">
<div class="container">
<div class="row">
<div class="col">
<ol id="post-list">
{% for article in articles_page.object_list %}
<li><article class="hentry">
<header> <h2 class="entry-title">{{ article.title }}</h2> </header>
<footer class="post-info">
<time class="published" datetime="{{ article.date.isoformat() }}"> {{ article.locale_date }} </time>
<address class="vcard author">By
{% for author in article.authors %}
<a class="url fn" href="{{ SITEURL }}/{{ author.url }}">{{ author }}</a>
{% endfor %}
</address>
</footer><!-- /.post-info -->
<div class="entry-content"> {{ article.summary }} </div><!-- /.entry-content -->
</article></li>
{% endfor %}
</ol><!-- /#posts-list -->
{% if articles_page.has_other_pages() %}
{% include 'pagination.html' %}
{% endif %}
</div>
</div>
</div>
</section>
{% endblock content %}
You must have referenced your template from one of the articles using:
Template: blog
If you remove that reference and add the following lines to your pelicanconf.py, Pelican will generate blog.html directly from your template file:
DIRECT_TEMPLATES = ['index', 'blog']
PAGINATED_DIRECT_TEMPLATES = ['blog']
(Do not forget to empty your output folder before running pelican. Tested on Pelican 3.7.1)
For the sake of future visitors who might come here looking for an answer like I did:
The problem can have a good number of very diverse reasons. In my case it was not a problem with the configuration of the pelican tooling, but rather an error in the metadata of some of my content pages. I had not included the correct category, date or tag fields. You'd never guess that from the error message now, would you?
I found this question looking for the same error.
In my case the reason was an issue which has been closed but not merged in the current release of the Attila theme. More precisely: the error is caused by a template in the templates folder of the theme which has a wrong reference inside it. In the specific case, inside the page template there was a wrong reference to article.
Changing the template manually fixed the issue:
--- a/attila-1.3/templates/page.html
+++ b/attila-1.3/templates/page.html
## -21,8 +21,8 ##
{% else %}
{% set selected_cover = SITEURL+"/"+HEADER_COVER %}
{% endif %}
-{% elif article.color %}
- {% set selected_color = article.color %}
+{% elif page.color %}
+ {% set selected_color = page.color %}
{% elif HEADER_COLOR %}
{% set selected_color = HEADER_COLOR %}
{% endif %}
I hope this helps debugging similar errors.
The page variable articles_page is set in only one place: Writer._get_localcontext and there is a guard condition:
if paginated and template_name in self.settings['PAGINATED_TEMPLATES']:
# ... code ...
paginated_kwargs.update(
{'%s_paginator' % key: paginator,
'%s_page' % key: page, # <-- Creates `article_page`
'%s_previous_page' % key: previous_page,
'%s_next_page' % key: next_page})
If this problem crops up, the easiest solution is to make sure the guard condition evaluates to True. Most likely, the problem is that template_name is not in PAGINATED_TEMPLATES in your configuration file. I opened writers.py, added a print(f"template_name is {template_name}") and got my answer (I didn't have author : None in my PAGINATED_TEMPLATES dictionary).

How do you declare python variables within flask templates?

I've got some basic experience building websites using a LAMP stack. I've also got some experience with data processing using Python. I'm trying to get a grip on the mongodb-flask-python thing, so I fired everything up using this boilerplate: https://github.com/hansonkd/FlaskBootstrapSecurity
All is well.
To experiment, I tried declaring a variable and printing it...
I get this error message:
TemplateSyntaxError: Encountered unknown tag 'x'. Jinja was looking for the following tags: 'endblock'. The innermost block that needs to be closed is 'block'.
Here's my main index.html page
{% extends "base.html" %}
{% block content %}
<div class="row">
<div class="col-xs-12">
Hello World, at {{ now }}, {{ now|time_ago }}
</div>
</div>
<div class="row-center">
<div class="col">
{% x = [0,1,2,3,4,5] %}
{% for number in x}
<li> {% print(number) %}
{% endfor %}
</div>
</div>
{% endblock %}
I love learning new things, but man, can I ever get hung up for hours on the simplest of things... any help would be greatly appreciated!!!
Flask uses Jinja as its default templating engine.
The templating language is python-esque, but is not python. This is different from something like a phtml file, which is php interspersed with html.
Check the jinja documentation for more of what you can do, but here's how you set a variable within a template:
{% set x = [0,1,2,3,4,5] %}
http://jinja.pocoo.org/docs/2.9/templates/#assignments
Try this:
{% set x = [0,1,2,3,4,5] %}
See Jinja docs.

How to display flashing message without reloading the page in Flask?

I'm working on a web application using Flask in Python.
I have small function in my application that calculates some values in the background and displays the result on the web page via a flashing message.
Everything is displaying and working fine but it requires page reloading to get the flashing message.
I want to display messages without reloading page.
I heard that I can do that with js, but I'm not familiar with js.
If you have any ideas or suggestion I would appreciate.
There is my code that could build a better picture of what I'm doing.
This is the renderer between my app and the main html file
{% macro render_field(field) %}
<dt> {{ field.label }}
<dd> {{ field(**kwargs)|safe }}
{% if field.errors %}
<ul class=errors>
{% for error in field.errors %}
<li>{{ error }}</li>
{% endfor %}
</ul>
{% endif %}
</dd>
{% endmacro %}
This is the html file were I want to display flashing messages:
<div class="container-fluid" style="min-height:100%">
{% with messages = get_flashed_messages() %}
{% if messages %}
{% for message in messages %}
<div class="alert alert-warning alert-dismissible" role="alert">
<button type="button" class="close" data-dismiss="alert" aria-label="Close">
<span aria-hidden="true">×</span>
</button>
{{message}}
</div>
{% endfor %}
{% endif %}
{% endwith %}
</div>
Here's what Flask Web Development: Developing Web Applications with Python (pp. 46-48) has to say of Message Flashing:
Sometimes it is useful to give the user a status update after a request is completed. This
could be a confirmation message, a warning, or an error. A typical example is when you
submit a login form to a website with a mistake and the server responds by rendering
the login form again with a message above it that informs you that your username or
password is invalid.
Flask includes this functionality as a core feature. Example 4-6 shows how the flash()
function can be used for this purpose.
Example 4-6. hello.py: Flashed messages
#app.route('/', methods=['GET', 'POST'])
def index():
form = Nameform()
if form.validate_on_submit():
old_name = session.get('name')
if old_name is not None and old_name != form.name.data:
flash('Looks like you have changed your name!')
session['name'] = form.name.data
form.name.data = ''
return redirect(url_for('index'))
return render_template('index.html', form=form, name=session.get('name'))
form = form, name = session.get('name'))
In this example, each time a name is submitted it is compared against the name stored
in the user session, which would have been put there during a previous submission of
the same form. If the two names are different, the flash() function is invoked with a
message to be displayed on the next response sent back to the client.
Calling flash() is not enough to get messages displayed; the templates used by the
application need to render these messages. The best place to render flashed messages is
the base template, because that will enable these messages in all pages. Flask makes a
get_flashed_messages() function available to templates to retrieve the messages and
render them, as shown in Example 4-7.
Example 4-7. templates/base.html: Flash message rendering
{% block content %}
<div class="container">
{% for message in get_flashed_messages() %}
<div class="alert alert-warning">
<button type="button" class="close" data-dismiss="alert">×</button>
{{ message }}
</div>
{% endfor %}
{% block page_content %}{% endblock %}
</div>
{% endblock %}
In this example, messages are rendered using Bootstrap’s alert CSS styles for warning
messages (one is shown in Figure 4-4).
Figure 4-4. Flashed message
A loop is used because there could be multiple messages queued for display, one for
each time flash() was called in the previous request cycle. Messages that are retrieved from get_flashed_messages() will not be returned the next time this function is called,
so flashed messages appear only once and are then discarded.
This is not possible via Python without reloading the page. You must do this in javascript. I suggest CSS styling with display: none and display: block. Here is an example.
1) Python Code, this should go in your app.py or flask.py file.
app.route('/flash/<message>')
def flash(message):
return render_template('flash.html', msg=message)
This will render the HTML page named flash.html. The URL passed in will also have another argument, <message> this is the message that will flash. A URL like this, localhost:80/flash/Hello%20World! will flash the message "Hello World!" on your screen.
There is also another way to pass a message in, this is will arguments. The code for that is like so.
app.route('/flash')
def flash():
message = request.args.get("msg")
return render_template("flash.html", ,msg=message)
This uses the flask's request arguments. So a URL like this, localhost:80/flash?msg=Hello%20World! will give a flashing message saying "Hello World!". If you want to use this method be sure to have the import statement, from flask import request in your import statements.
2) Html Code, this is a separate file named, flash.html in your templates folder.
<body>
<h1 id="header">{{ message }}</h1>
<script>
var heading = $("#header");
setInterval(function() {
if (heading.style.display == "block") { heading.style.display = "none"; }
else if (heading.style.display == "none") { heading.style.display = "block"; }
}, 1000);
</script>
</body>
The 1000 in the setInterval is milliseconds. So the heading will blink every 2 seconds.
You may want to consider using Toastr instead. I ran into the same roadblock with Flask's Flash feature, and Toastr is pure JS. You can use it just like a console log line in your code
toastr.info("Here's a message to briefly show to your user");

Flask & Bootstrap Multiple collapsibles, but they each only open the first

I have an HTML page displaying a database populated by emails. I have them displayed in a collapsible, and for each post the timestamp of it is what toggles it and the innards are the email itself. The HTML page is structured like this:
{% extends "base.html" %}
{% block content %}
{% for email in emails %}
<div><button class="btn" data-toggle="collapse" data-target="#demo">{{ email.timestamp }}</button>
<div id="demo" class="collapse">
{{ email.body }}
</div>
{% endfor %}
{% endblock %}
relevant portion of views.py
#app.route('/cruz')
def cruz():
u = Politician.query.get(1)
emails = u.emails.all()
return render_template('cruz.html',title='Ted Cruz',emails=emails)
which produces a webpage that looks like this: http://imgur.com/noqC40E
The problem is that no matter which of those timestamps I click, only the first collapsible opens and closes. I've tried a number of things to fix it, mostly messing around with the HTML page and the for blocks and where I place the {{ email.body }}, but nothing I do seems to work. Can anyone see where this is going wrong?
You are generating the same id attribute for your div each time:
<div id="demo" class="collapse">
You almost certainly need to generate unique ids. You could generate unique ids by adding the loop index perhaps:
<div id="demo-{{loop.index}}" class="collapse">

Categories