Pyramid - Include user object in every request and rendering? - pyramid

In my website I have a user panel which displays info about user (or a custom message if he is not logged in). I'd like to know how can I do that a user object is accessible within every template? (like Django's CONTEXT_PROCESSORS).
I know I can add_request_method() to my config object but as far as I understand it will only make user's object available in request but I will have to add it to returned context manually each time - not good.
Maybe I should add user's object to session?
What is the proper way to do it?

What you need in Pyramid is an authentication policy. This can be as simple as adding these lines to your __init__.py:
from pyramid.authentication import AuthTktAuthenticationPolicy
def main(global_config, **settings):
...
authentication_policy = AuthTktAuthenticationPolicy('some_key')
config = Configurator(settings=settings,
authentication_policy=authentication_policy)
...
Once an authentication policy is in place, you can use the 'forget' and 'remember' functions from pyramid.security to allow users to log in and out. Here's a simplified view function that handles logins:
from pyramid.security import remember, forget
def sign_in(request):
username = request.POST.get('username')
password = request.POST.get('password')
if username and User.verify_password(username, password):
headers = remember(request, username)
else:
headers = forget(request)
return HTTPFound(location=request.route_url('index'),
headers=headers)
There are a few things you'll have to change there - maybe your User object doesn't have a 'verify_password' method; maybe you want to redirect to a different page on successful login, etc. But it's something to get started with.
Then in my base template I can add:
<%
from pyramid.security import authenticated_userid
user_id = authenticated_userid(request)
%>
to make the user_id available for use in the template and then:
% if user_id:
<p>Welcome ${user_id}</p>
% else:
<p>Please login here</p>
% endif
Note I'm using Mako syntax - if you're using a different templating engine you should check their syntax for imports, conditionals, etc.
I've given a basic outline of the different pieces and how they slot together, but Pyramid has powerful authentication features, and I encourage you to read a little about how it works:
The Pyramid documentation: http://docs.pylonsproject.org/projects/pyramid/en/latest/api/authentication.html
This excellent demo of Pyramid's authentication policies:
https://github.com/mmerickel/pyramid_auth_demo

I know you already mentioned the add_request_method() solution, but that's what I ended up with after following this tutorial on adding a user object to the request.
I might not be doing anything that out there with my pyramid apps, but basically every view and template I have has a request object as part of it, so within any template I can do <% request.user %> and have my user object and everything part of that available. I even have some helper methods and relationships that just come through and are available automagically as request.user.
What example case do you have where the request object isn't available?

Related

Working outside of request context. Flask, SqlAlchemy

I am using Flask and SqlAlchemy, the case is that I have a main database and then I have several databases where I get other information. But the credentials are not defined as fixed, so I have to obtain them from a table in the main database, depending on the plant where the user is. For this I use SQLALCHEMY_BINDS. the problem is that when I try to pass the connection string to the Bind I told myself that the function that returns it is out of context. Here a portion of the code
def scadaConnect():
idplanta_session=str(session['idPlanta'])
usernamequery = db.session.query(Scada.usernameScada).filter(Scada.idPlanta=='5')
hostquery = db.session.query(Scada.hostScada).filter(Scada.idPlanta=='5')
passwordquery = db.session.query(Scada.passScada).filter(Scada.idPlanta=='5')
nombredbquery = db.session.query(Scada.nombrebdScada).filter(Scada.idPlanta=='5')
nombredb = str(nombredbquery[0])[2:len(nombredbquery[0])-4]
host = str(hostquery[0])[2:len(hostquery[0])-4]
password = str(passwordquery[0])[2:len(passwordquery[0])-4]
username = str(usernamequery[0])[2:len(usernamequery[0])-4]
connexion = 'mysql+pymysql://'+username+":"+password+"#"+host+"/"+nombredb+"'"
def retorno():
return str(connexion)
from config import SQLALCHEMY_BINDS
SQLALCHEMY_BINDS['scada']= scadaConnect()
The error is as follows
RuntimeError: Working outside of request context.
This typically means that you attempted to use functionality that needed an active HTTP request. Consult the documentation on testing for information about how to avoid this problem.
session (first line in scadaConnect()) is only available in a request context a.k.a. view. It lives in the session cookie on the client and is sent to the server (and available to your view) only when the browser makes a request.
You will have to move the call to scadaConnect() to a view if you want to use session.

Displaying a form and saving the submited data

I have a simple app that saves and displays records off of a database,
#user_endpoints.get("/user/<id>")
def get_user(request, id):
dct = User.by_id(id)
if not dct:
return response.json({"Error": "Not Found"}, status=404)
return response.json(dct.to_dict(), status=200)
When it comes to displaying a user list something like the code below was sufficient,
#user_endpoints.get("/users")
def list_users(request):
dct = User.all()
template = template_env.get_template('user_list.html')
content = template.render(title='User List', users=dct)
return response.html(content)
The above uses Jinja2 but that is not important to me (this is not a real app). I am not clear on how to display a form for creating a new user and saving the submitted data, can someone provide a simple example for that?
There is no "one way" with Sanic to do that. Sanic does not know about forms. It completely depends on what you do in the frontend, how you encode the data. Are you sending JSON or is it "form-data" encoded? Maybe something completely different?
You would certainly use "POST" instead of "GET" like you did above. You can inspect request and find your data that's been sent from the frontend and then act on it. (Although nowadays you'd start with designing and implementing a proper (REST) API - usually based on JSON - and then use that. The word "form" does not appear here. It's just data.)
You could use Sanic-WTF
There is an example with Sanic-WTF + Jinja2 - https://bitbucket.org/voron-raven/synergy/src/1b8172a0bc61c5239c5ad2a2b9f064ed50ff81dd/views.py#lines-88

insted of "autoload_server" I want to use "server_document"

I want to make bokeh embedded web app.
resources said "autoload_server" works but it does not.
session=pull_session(url=url,app_path="/random_generator")
bokeh_script=autoload_server(None,app_path="/random_generator",session_id=session.id, url=url)
I think autoload_server can not be used anymore
so instead of this, I want to use server_document
I wrote this code but still does not work
how should I write this code?
session=pull_session(url=url,app_path="/random_generator")
bokeh_script=server_document("/random_generator")
server_document is for creating and embedding new sessions from a Bokeh server. It is not useful for interacting with already existing sessions, i.e. it is not useful together with pull_session. For that, you want to use server_session, as described in the documentation. For example, in a Flask app you would have something like:
#app.route('/', methods=['GET'])
def bkapp_page():
with pull_session(url="http://localhost:5006/sliders") as session:
# update or customize that session
session.document.roots[0].children[1].title.text = "Special Sliders!"
# generate a script to load the customized session
script = server_session(session_id=session.id,
url='http://localhost:5006/sliders')
# use the script in the rendered page
return render_template("embed.html", script=script, template="Flask")

Add Django Variables in Email Template

I have a relatively simple objective: send email to Django admins when users register and activate their accounts that contain user information, like username, email, etc. I am using django-registration for handling the registration process. I then employ signals to send the emails. This process works just fine, but as soon as I try to inject the user, username or user's email into an email template I get in trouble.
Here is my signal_connectors.py:
from django.core.mail import mail_admins
from django.dispatch import receiver
from registration.signals import user_registered
from django.template.loader import render_to_string
#receiver(user_registered, dispatch_uid="common.signal_connectors.user_registered_handler")
def user_registered_handler(sender, **kwargs):
print('user registration completed...')
subject = 'registration notify'
template = 'common/email.html'
message = render_to_string(template, {'user': user})
from_email = 'from#example.com'
# send email to admins, if user has been registered
mail_admins(subject, message, fail_silently=False, connection=None, html_message=None)
print('sending email to administrators...')
The template is like so:
{{ user }} has registered at example.com
I have tried variations like {{user}}, {{user.username}}, {{request.user.username}}, etc. Also, in the message variable above, in signals_connectors.py, I have tried variations, with corresponding import statements.
I have also tried rendering templates with contexts set, and other variations that I have found on the web, eg:
finding the django-registration username variable
I keep getting something like this for an error:
global name 'user' is not defined
I think I have template context processing set up correctly, because I can use something like {{ user.username }} in another template and it works.
I have also tried correcting my import statements to include necessary objects, with no luck. I have tried most of what I can find on the web on this topic so I'm at a loss...
What am I missing?
I didn't realize this was so easy. Based on this post:
Where should signal handlers live in a django project?
I tried actually adding the user and request as arguments in the signal connector method and this worked:
. . .
#receiver(user_registered, dispatch_uid="common.signal_connectors.user_registered_handler")
def user_registered_handler(sender, user, request, **kwargs):
. . .
Then, I used this in email template:
{{ user }} has registered at example.com
I probably over-thought this one but I am surprised no one stepped up and was able to help with this...

Avoiding repetition with Flask - but is it too DRY?

Let us assume I serve data to colleagues in-office with a small Flask app, and let us also assume that it is a project I am not explicitly 'paid to do' so I don't have all the time in the world to write code.
It has occurred to me in my experimentation with pet projects at home that instead of decorating every last route with #app.route('/some/local/page') that I can do the following:
from flask import Flask, render_template, url_for, redirect, abort
from collections import OrderedDict
goodURLS = OrderedDict([('/index','Home'), ##can be passed to the template
('/about', 'About'), ##to create the navigation bar
('/foo', 'Foo'),
('/bar', 'Bar'), ##hence the use of OrderedDict
('/eggs', 'Eggs'), ##to have a set order for that navibar
('/spam', 'Spam')])
app = Flask(__name__)
#app.route('/<destination>')
def goThere(destination):
availableRoutes = goodURLS.keys():
if "/" + destination in availableRoutes:
return render_template('/%s.html' % destination, goodURLS=goodURLS)
else:
abort(404)
#app.errorhandler(404)
def notFound(e):
return render_template('/notFound.html'), 404
Now all I need to do is update my one list, and both my navigation bar and route handling function are lock-step.
Alternatively, I've written a method to determine the viable file locations by using os.walk in conjunction with file.endswith('.aGivenFileExtension') to locate every file which I mean to make accessible. The user's request can then be compared against the list this function returns (which obviously changes the serveTheUser() function.
from os import path, walk
def fileFinder(directory, extension=".html"):
"""Returns a list of files with a given file extension at a given path.
By default .html files are returned.
"""
foundFilesList = []
if path.exists(directory):
for p, d, files in walk(directory):
for file in files:
if file.endswith(extension):
foundFilesList.append(file)
return foundFilesList
goodRoutes = fileFinder('./templates/someFolderWithGoodRoutes/')
The question is, Is This Bad?
There are many aspects of Flask I'm just not using (mainly because I haven't needed to know about them yet) - so maybe this is actually limiting, or redundant when compared against a built-in feature of Flask. Does my lack of explicitly decorating each route rob me of a great feature of Flask?
Additionally, is either of these methods more or less safe than the other? I really don't know much about web security - and like I said, right now this is all in-office stuff, the security of my data is assured by our IT professional and there are no incoming requests from outside the office - but in a real-world setting, would either of these be detrimental? In particular, if I am using the backend to os.walk a location on the server's local disk, I'm not asking to have it abused by some ne'er-do-well am I?
EDIT: I've offered this as a bounty, because if it is not a safe or constructive practice I'd like to avoid using it for things that I'd want to like push to Heroku or just in general publicly serve for family, etc. It just seems like decorating every viable route with app.route is a waste of time.
There isn't anything really wrong with your solution, in my opinion. The problem is that with this kind of setup the things you can do are pretty limited.
I'm not sure if you simplified your code to show here, but if all you are doing in your view function is to gather some data and then select one of a few templates to render it then you might as well render the whole thing in a single page and maybe use a Javascript tab control to divide it up in sections on the client.
If each template requires different data, then the logic that obtains and processes the data for each template will have to be in your view function, and that is going to look pretty messy because you'll have a long chain of if statements to handle each template. Between that and separate view functions per template I think the latter will be quicker, even more so if you also consider the maintenance effort.
Update: based on the conversion in the comments I stand by my answer, with some minor reservations.
I think your solution works and has no major problems. I don't see a security risk because you are validating the input that comes from the client before you use it.
You are just using Flask to serve files that can be considered static if you ignore the navigation bar at the top. You should consider compiling the Flask app into a set of static files using an extension like Frozen-Flask, then you just host the compiled files with a regular web server. And when you need to add/remove routes you can modify the Flask app and compile it again.
Another thought is that your Flask app structure will not scale well if you need to add server-side logic. Right now you don't have any logic in the server, everything is handled by jQuery in the browser, so having a single view function works just fine. If at some point you need to add server logic for these pages then you will find that this structure isn't convenient.
I hope this helps.
I assume based on your code that all the routes have a corresponding template file of the same name (destination to destination.html) and that the goodURL menu bar is changed manually. An easier method would be to try to render the template at request and return your 404 page if it doesn't exist.
from jinja2 import TemplateNotFound
from werkzeug import secure_filename
....
#app.route('/<destination>')
def goThere(destination):
destTemplate = secure_filename("%s.html" % destination)
try:
return render_template(destTemplate, goodURLS=goodURLS)
except TemplateNotFound:
abort(404)
#app.errorhandler(404)
def notFound(e):
return render_template('/notFound.html'), 404
This is adapted from the answer to Stackoverflow: How do I create a 404 page?.
Edit: Updated to make use of Werkzeug's secure_filename to clean user input.

Resources