#app.route('/a', methods=['GET', 'POST'])
def a():
form = data(request.form)
if form.validate_on_submit():
job = Job()
job.title = form.title.data
return redirect(url_for('users.b'))
#app.route('/b', methods=['GET', 'POST'])
def b():
#access here the job.title previous defined
I know that I can pass parameters to url_for, but isn't what i am looking for. Basically access the previous defined object attributes. How can I do that?
HTTP is a stateless connection, meaning a user being redirected to /b from /a will only be able to pass information via HTTP headers, cookies and URL parameters.
To solve this particular problem, you'll want to save the information set in job.title into a database on the host (i.e. Using SQLAlchemy in Flask). Then, you can pass the database ID of the new entry into the url_for parameter in /a so that it's redirected to some URL with this parameter set. (i.e. /b?user_id=123). Be aware there are multiple security issues you'll need to be aware of, since the user can just change the ID themselves.
There are lots of ways to do this. You may want to use the Flask-WTF addon to handle some of this for you.
Here's some snippets on handling sessions in Flask.
Related
I have to create a small web app in Flask which contains an API and also an interface and I'm facing the following problem:
This would be how i handle a GET request:
#app.route('/member/<id>', methods=['GET'])
def member_get(id):
member = cursor.execute(f"select * from members where id={id}").fetchone()
if member is not None:
return to_json(member), 200
else:
return 'Not found', 404
And I would like to create some small forms with which I could do GET,POST,PUT,DELETE operations.
This would be how I get the data from the form:
#app.route('/dashboard', methods=['POST'])
def dashboard_post():
id = request.form['get_id']
return redirect(url_for("member_get",id=id))
My question is how can I get the data from the API method without actually redirecting to that page?
More precise, can I call somehow redirect(url_for("member_get",id=id)) and get the response data directly? (if I print the return of the redirect method it only shows the request status)
I assume one solution would be using the requests module, but is there a way to do it directly in Flask?
First of all, an API should always return a response in a format that is consistent and predictable. The code for member_get returns JSON in case of success but plain text in case of failure. This is not okay. You should return JSON always, with the appropriate HTTP status code. Here you are using 404 to express Not found, this is good.
You can use the jsonify function in Flask for that. And maybe normalize the response, so that is always has the same shape, whether the member ID is found or not.
Otherwise, parsing the response from your API will be harder because it is not consistent depending on the scenario.
Second point, if I understand it right: if you want to invoke the route '/member/' from within your API, you could simply do:
return member_get(id)
You call the function that is attached to the route, not the route itself. Of course you could actually fetch the page with the requests module but this is unnecessary, since the function is available internally.
Your question is not clear, but as I understand, (1) you think you have to get your form data from one view and send it to another view to do operations. (2) you are not familiar with flask request and flask-wtf. and maybe (3) looking for a way to do this without refreshing or redirecting the page.
You don't need to separate your GET and POST methods. instead you can integrate both in one view.
#app.route('/member/<id>', methods=['GET', 'POST])
To handling data, you can use flask request.
from flask import request
and access to data in your view like this:
id = request.form.get("idField")
but you can also use Flask-WTF to simply make and handle forms.
with Flask-WTF your view would be like this:
from app.forms import SearchForm
#app.route('/your-endpoint', methods=['GET', 'POST'])
def yourView():
form = your_form()
if form.validate_on_submit():
id=form.idField.data
return render_template('test.html', form=form)
the condition form.validate_on_submit() checks if you are submitting a from or you just opened it. if you submit a form and it's data are valid based on validators defined in your form, the code runs. else just renders the template and returns the page.
To learn how to make forms with Flask-WTF I recommend reading this article:
If you don't want to refresh the page or redirect it after submitting the form, you can use AJAx on your page.
My question is can I specify the type of REST route in Jinja2, for example if I have the route:
RedirectRoute('/<id>/somthing/<key>', myFile.Handler, name='name', strict_slash=True),
class Handler(JSONHandler):
def get(): ...
def delete(): ...
def post(): ...
Delete
Is it possible to specify the delete() handler form the Jinja2 template?
If I understand what you're asking, you have an endpoint which respond to the methods GET, POST, and DELETE and you want to know if you can make the HTML resulting from Jinja send a DELETE request your endpoint.
The short answer is no. DELETE must always be performed by JavaScript and AJAX. The only methods you can specify in an HTML are GET (via a link) and POST (via a form).
Flask-login doc says we should validate next using next_is_valid(), but I can't find any such method:
Warning: You MUST validate the value of the next parameter. If you do not, your application will be vulnerable to open redirects.
#app.route('/login', methods=['GET', 'POST'])
def login():
# Here we use a class of some kind to represent and validate our
# client-side form data. For example, WTForms is a library that will
# handle this for us.
form = LoginForm()
if form.validate_on_submit():
# Login and validate the user.
login_user(user)
flask.flash('Logged in successfully.')
next = flask.request.args.get('next')
if not next_is_valid(next):
return flask.abort(400)
return flask.redirect(next or flask.url_for('index'))
return flask.render_template('login.html', form=form)
Running this I get the error:
NameError: global name 'next_is_valid' is not defined
And if I do:
from flask.ext.login import next_is_valid
>> ImportError: cannot import name next_is_valid
Where is the next_is_valid() function, and if it doesn't exist, how do I validate the next parameter?
It doesn't say you must validate next with next_is_valid, only that
You MUST validate the value of the next parameter.
next_is_valid is just an example function.
You must determine if next is valid based on your own criteria. next is the url to redirect to on a successful login. If you have any permissions to apply or restrictions on your site this is where you would want to make sure they are enforced.
For example, a user could attempt to login with the request url http://example.com/login?next=admin/delete/all/users. If the login attempt was successful and the admin permission was not checked within your login function or on the endpoint itself, well, bad things could happen. It all depends on how you structure your application and control access to individual endpoints.
In addition to the examples mentioned by other answers, next could also be next=malicioussite.url. If a malicious actor sent someone to your login link with next set to their malicious site, then after logging in the user could think they are still on your site, but instead have been directed somewhere dangerous. Make sure to validate next against redirecting to other websites.
I am working on a google app engine (gae) project in python which has the following structure:
class LoginHandler(webapp2.RequestHandler):
def get(self):
...#check User-> DB access
def post():
...#check User-> DB access
class SignupHandler(webapp2.RequestHandler):
def get(self):
...#check User-> DB access
def post():
...#check User-> DB access
class Site1Handler(webapp2.RequestHandler):
def get(self):
...#check User-> DB access
def post():
...#check User-> DB access
class Site2Handler(webapp2.RequestHandler):
def get(self):
...#check User-> DB access
def post():
...#check User-> DB access
class ...
application = webapp2.WSGIApplication([('/login', LoginHandler),
('/signup',SignupHandler),
('/site1', Site1Handler),
('/site2', Site2Handler),
...,
],
debug=True)
Every user who wants to use this application has to be logged in.
Therefore on the login-site and the signup-site a cookie value with an user_id is set.
So lets imagine this app has 100 URLs and the corresponding 100 Site...Handlers() implemented.
Than for every get()/post() call I first get the user_id from the cookie and check in the database if this user exists and if it is valid.
So if the user clicks on 20 sites the app accesses 20 times the db to validate the user.
I am sure there is a better way and I would be glad if someone could show me how to do this.
I have already seen someone inherited his own Handler from webapp2.RequestHandler
which would than look like:
class MyHandler(webapp2.RequestHandler):
def initialize(self, *a, **kw):
webapp2.RequestHandler.initialize(self, *a, **kw)
uid = self.request.cookies.get('user_id')
self.user = uid and User.all().filter('userid =', uid).get()
class LoginHandler(MyHandler):
def get(self):
...#if self.user is valid -> OK
def post():
...#if self.user is valid -> OK
...
And here it is getting confusing for me.
Consider two or more people accessing the application concurrently. Will then User1 see data of User2 because self.user is initialized with data from User2?
I also concidered using a global variable to save the current user. But here the same problem if two users access the app concurrent.
I also found the webapp2.registry functionality which seemed to me the same like a global dictionary. And here also the problem of two or more users accessing the app at the same time.
Could someone please show me how to do it right? I am very new to gae and very happy for every hint in the right direction.
(Maybe Memcached is the solution. But I am more interested in a review of this check if user is valid pattern. So what would be best practice to do this?)
Assuming that you are using NDB and validating your user by getting a User object via a key/id - it will be automatically cached in memcache as well as in current local instance's memory, so your route handlers won't be calling Datastore with every single request, this is all done automatically, no extra coding required. If for validation / getting the user object you are using a query - the result won't be automatically cached but you can always manually cache it and verify the user via cache first and if the cache doesn't exist only then query Datastore, caching the results for the next request.
See more here.
If you are using webapp2's Sessions with signed/secure cookies then the data in those cookies, including the fact that the user is validated (which you previously set when when validating the user the first time) can be trusted, as long as you use long and randomly generated secret_key, that is kept secret and thus, just like with cache, you first check whether the user is validated in the cookie and if not, you ask Datastore and save the result in the session cookie for the next request. See more here.
Either way, you don't have to repeat your validation code in every single handler like you are showing in your example. One way of fixing it would be using decorators which would make your validation reuse as simple as placing #login_required before your get method. See more info here and take a look at the webapp2_extras.appengine.users file to get an idea how to write your own, simmilar decorator.
From the Flask docs:
def index():
pass
app.add_url_rule('/', 'index', index)
It also says:
endpoint – the endpoint for the registered URL rule. Flask itself assumes the name of the view function as endpoint
Indeed, if I do app.add_url_rule('/', None, index) it all seems to work fine. (I'm not sure of the terminology and what an "endpoint" actually is.)
Some further questions:
Is there any reason to specify the second argument?
Is the second argument indeed the "endpoint"?
What are the benefits/drawbacks of (not) specifying the second argument?
What if the same name is used in another url rule? Is the first overwritten? Is this intended?
In this case, an endpoint is simply a term for a valid URL of your application. From Wikipedia:
In service-oriented architecture, an endpoint is the entry point to a service, a process, or a queue or topic destination
As for naming your URLs - yes, it is optional. There are some benefits to defining names. When using *url_for*() or redirect() for example, you can specify the url's name as a shortcut. This comes with the added benefit of allowing your to change your url structure without changing every line of code that interacts with that url, since it only references it by name.
So in your example, you could reference your index url like this:
return redirect('index')
For your last question, I'm not sure what would happen. It likely would error when trying to resolve the url with that name. Either way, there's no reason to define two different urls with the same name.
The reason for the second argument is the following:
Let's say you're designing a website and you have /login be the page where your users enter their username, password, OpenID, whatever. Upon successfully logging in, you might want to send them to /. In Flask, the canonical way to do that is:
return redirect(url_for('index'))
Where 'index' is the name of the function you defined to be the handler for /, e.g.,
#app.route('/')
def index():
return render_template('index.html')
If you instead do:
def index():
return render_template('index.html')
app.add_url_rule('/', None, index)
It will work just fine when your user requests / explicitly, but when your users successfully login they'll be faced with a horrible error message and have to visit / manually.
In short, it's the proper thing to do.
Also, you should note that
#app.route('/')
def index():
return render_template('index.html')
And
def index():
return render_template('index.html')
app.add_url_rule('/', 'index', index)
Are exactly the same and in the both of these last two snippets redirect(url_for('index')) will work perfectly.