I am currently using Python Flask and Jinja2. I have a table with some message. I want to basically have a button on the menu with the number of messages that a user has.
I've used this to make my sidebar global so it can appear on multiple pages:
app.jinja_env.globals.update(...)
This is the code I've used to get the number of messages:
def message_notification():
c.execute("SELECT count(*) FROM messages WHERE read = 0 AND receiver = ?",(session['username'],))
msgnotifcation = c.fetchone()
return msgnotifcation[0]
However I am getting this error:
RuntimeError: Working outside of request context.
Is there any other way to do this as I figured out that the problem is to do with the session['username'] bit.
You need to move this code so that it executes before the template starts to render, i.e. it will be inside the request context.
Flask provides the context_processor decorator to achieve this. http://flask.pocoo.org/docs/0.12/templating/#context-processors The values returned will be available in all your templates as though they had been returned as any other context item from a view.
#app.context_processor
def message_count():
value = ...your sql...
return dict(message_count=value)
Then in your views you can use:
{{ message_count }}
Related
Sorry if this is a noob question I am still learning. I have passed a variable from python code to a jinja2 HTML template to set up a URL, like this:
Delete
When this link is pressed it should run a query that deletes the entity with that ID. But when the link is pressed it goes to /delete/1827424298 for example, which results in a 404 error as the request handler doesn't exist.
I need to pass that ID back into my python code so it can run a method to delete the entity with that same ID. How do I go about doing this? Using webapp2 if that is important.
class DeleteRequestHandler(webapp2.RequestHandler):
def get():
template = template_env.get_template('myrequests.html')
context = {
'results': results.key.id()
}
self.response.out.write(template.render(context))
EDIT: I've added my delete handler - it is incomplete as I have yet to add the query to delete the entity. My thinking behind it so far is I can grab the results.key.id() from the jinja2 template and put it into results but I am not sure if this would work.
So I think what you're confused about is how to set up a route handler with a dynamic part to the URL. It's a shame that this is completely skipped over in the webapp2 tutorial, as it's a fundamental part of writing any web application. However, it is covered well in the guide to routing, which you should read.
At its simplest, it's just a matter of putting a regex in the route:
app = webapp2.WSGIApplication([
...
(r'/delete/(\d+)', MyDeleteHandler),
])
which will now route any URL of the form /delete/<number>/ to your deletion handler.
The ID that you pass in the URL will be the first positional argument to the handler method:
class MyDeleteHandler:
def get(self, item_id):
key = ndb.Key(MyModel, item_id) # or whatever
I'm currently working on a web app where I'm using Flask and the Flask-User extension to handle authorization. At the moment I have something like this:
#application.route('/dummy')
#roles_required('Admin')
#login_required
def dummy():
in my views.py file and something like this:
{% if current_user.is_authenticated and current_user.has_roles('Admin') %}
in my templates.
Now, there's been a few times where I've changed my mind in terms of role names and then had to search through the whole project to find and replace those usages. I wanted to define something like a ADMIN_ROLE global that can be accessed inside my views.py or any templates. I'd then just change a config file whenever I wanted to change a role's name.
I tried using my config.py setting ADMIN_ROLE = "Admin" and writing #roles_required(current_app.config['ADMIN_ROLE']) but that gives me the following error:
RuntimeError: Working outside of application context.
This typically means that you attempted to use functionality that needed
to interface with the current application object in a way. To solve
this set up an application context with app.app_context(). See the
documentation for more information.
so that should be wrong. What would be the most correct way to accomplish what I'm trying?
As it says, you can only access config from within the app context. Normally, you do this:
with current_app.app_context():
admin_role = current_app.config['ADMIN_ROLE']
Because you're doing this in a decorator, I don't believe there's anyway to do that. I would suggest just using a separate module. Now, it doesn't appear that you can use python import statements in jinja, so we'll still use app.config for that. Basically we'll create a module with the desired roles and then import that into the config file and your routes file.
roles.py
ADMIN = "Admin"
USER = "User"
config.py
from roles import ADMIN, USER
ADMIN_ROLE = ADMIN
USER_ROLE = USER
routes.py
from roles import ADMIN, USER
#application.route('/dummy')
#roles_required(ADMIN)
#login_required
def dummy():
views.py
{% if current_user.is_authenticated and current_user.has_roles(app.config['ADMIN_ROLE']) %}
I am currently developing a Flask app (have been for the past year) and I'm encountering a rather... Weird bug. I've got a few files that are always included in my Jinja2 templates (navbars), and they use the users' name and avatar. As a consequence, everytime I render a template, I pass it the user. I recently noticed an error on my prod server :
<img alt="image" class="img-circle" src="{{ user.image }}" style="width: 48px;"/>
File "/usr/local/lib/python2.7/dist-packages/jinja2/environment.py", line 397, in getattr
return getattr(obj, attribute)
jinja2.exceptions.UndefinedError: 'user' is undefined
This is in one of my navbars. The method that renders this template uses this :
#mod.route('/broken_pus', methods=['POST', 'GET'])
def view_broken_pus():
return render_template("view_broken_pus.html", user=g.user, urls_for_active_clients=DeletedURLs.objects()[0].urls_for_active_clients, other_urls=DeletedURLs.objects()[0].other_urls)
As you can see, I pass the user=g.user. I do this on every single view of my website. And it works everywhere, EXCEPT on this method, which is pretty small. I have plenty of other routes like that, with just a render template, so I don't get what's the problem.
I also get it on another method, bigger, which always worked before :
#mod.route('/users/add', methods=['GET', 'POST'])
#requires_roles("admin", "project-leader")
def add():
"""
Method adding a new user.
"""
# We do not use WTForms there since we need custom checkboxes for the role
# Instead we use basic HTML and treat the checkboxes here
if request.method == 'POST':
user = User(name=request.form.get('name'),
email=request.form.get('email'))
l = []
# big switch assignement
user.role = l
try:
user.save()
except errors.NotUniqueError:
flash(u'User %s already in database.' % user.name, 'danger')
return redirect(url_for('home'))
flash(u'User %s registered.' % user.name, 'success')
return redirect(url_for('home'))
return render_template('add_user.html', page=url_for('users.add'), user=g.user, clients=Client.objects())
When I first load the form for adding a user, it works. When I add it, for some reason, I get the error (and the user is not saved in the database).
Since this works perfectly on local, I'm starting to suspect a problem on the production server itself. We use nginx and uwsgi for the app, and I recently implemented some Celery tasks. Got any idea ?
Thanks in advance.
Check out flask source for render_template:
It just calls template.render(context), but after the call to before_render_template.send(app, template=template, context=context)
From this, I think there is some before_render_template handler, that modifies context installed.
To debug this down, I may try to call something like this:
from flask import app
#mod.route('/broken_pus', methods=['POST', 'GET'])
def view_broken_pus():
template = app.jinja_env.get_or_select_template("view_broken_pus.html")
return template.render(dict(
user=g.user,
urls_for_active_clients=DeletedURLs.objects()[0].urls_for_active_clients,
other_urls=DeletedURLs.objects()[0].other_urls,
))
If this will work, I will need to dig in who modifies context in before_render_template slot.
I suspect threading. If g is some sort of global reference then you may need to ensure that it is set up on threading.local or that threading locks are used to ensure that no thread can get hold of g.user before some 'other' thread messes with it.
See how do I make a 2.7 python context manager threadsafe for a way to handle 'globals' without sacrificing thread safety.
I am using appengine with Python and jinja2 template.
I encountered an issue during the process: I render a list of games with this code
self.templateValues = {}
self.templateValues['title'] = 'GalaStore'
self.templateValues['values'] = kw
gamesQuery = Game.all()
values = {'games' : gamesQuery.run()}
self.templateValues['gl'] = values
template = JINJA_ENVIRONMENT.get_template(template)
self.response.out.write(template.render(self.templateValues))
then I have some button-filters in my html, and everyone calls a different js function. The problem is: once I hit the filter "sort by alpha" and I call a python function "sortByAlpha" via js (via ajax), how can I update the template variables in runtime withouth calling the template.render() function again? That would cause the reloading of the entire page and I would like to avoid it.
Thank you very much!
Your AJAX request would either need to return the games object list in JSON form, so the JavaScript could update the list in the browser, or you would have a template rendering just that part of the page, and have JavaScript swap out that piece with the HTML loaded from your server.
The latter can be made reusable; have both your main view and your AJAX handler use the same template, rendering just the list of games (no the whole page):
def rendered_games_list(self, sort_by=None):
games_query = Game.all()
if sort_by:
# I winged this bit, you didn't specify the ORM used
games_query = games_query.order_by(sort_by)
template = JINJA_ENVIRONMENT.get_template(games_list_template)
return template.render(gl=games_query.run())
then use this part in your main view:
template_values = {
'title': 'GalaStore',
'values': kw,
'games_list': self.rendered_games_list()
}
template = JINJA_ENVIRONMENT.get_template(template)
self.response.out.write(template.render(self.templateValues))
and in your main template insert the rendered games list HTML as:
{{ games_list|safe }}
So your main template doesn't render the games list itself, it merely includes it.
Your AJAX handler can return the rendered_games_list() directly:
sort_order = self.request.get('sort_order', None)
self.response.out.write(self.rendered_games_list(sort_order))
I'm writing a web app which has a page for admin tasks. One of the tasks is that the admin users must be able to edit other users details. Alas, I've fallen at quite a simple roadblock.
I've set up a very simple jQuery AJAX Get request, successfully transferring a string to the server and back. This is just background, but not the issue. The issue lies in retrieving other user's objects.
At the moment, with a username I know exists, this code which is accessed in views.py, produces a 500 Internal Server Error.
#login_required
def user_edit_getuser(request):
# Like before, get the request's context.
context = RequestContext(request)
inputname = request.GET['inputNameSend']
user_obj = User.objects.get(inputname)
return HttpResponse(inputname) #later will return a JSON String
get takes keyword arguments only: the key is the field to look up.
user_obj = User.objects.get(username=inputname)
Also, you should probably deal with the possibility that the GET request has no inputNameSend key.
For JS development, you can usually see the error page in the Chrome dev tools/Firebug console in the Network tab.