method="delete" from html form in FLASK - python

I have an HTML form:
{% set delete_urls = url_for('store_add') ~ store_id ~ "?__METHOD_OVERRIDE__=DELETE" %}
<form action="{{delete_urls}}" name="delete" method="post" id="{{form_id}}" style="display:none">
and in the views:
class StoreAdd(MethodView):
#login_required
def delete(self,store_id):
store_selected = request.args['store_id']
qstr = "DELETE FROM store WHERE store_id=%d AND cust_id=%d"%(store_id,self.cust_id)
h = pgexec(qstr,False,True)
h.process()
flash("deleted the store:%d"%(store_selected))
return redirect(url_for('store_add'))
store_add = StoreAdd.as_view('store_add')
app.add_url_rule('/storeadd/',
defaults={'store_id': 0},
view_func=store_add,
methods=["GET","PUT"])
app.add_url_rule('/storeadd/',
view_func=store_add,
methods=["POST"])
app.add_url_rule('/storeadd/<int:store_id>',
view_func=store_add,
methods=['DELETE','PUT','GET'])
Of course implemented the routing:
from werkzeug import url_decode
from flask import flash
class MethodRewriteMiddleware(object):
def __init__(self, app):
self.app = app
def __call__(self, environ, start_response):
if 'METHOD_OVERRIDE' in environ.get('QUERY_STRING', ''):
args = url_decode(environ['QUERY_STRING'])
method = args.get('__METHOD_OVERRIDE__')
if method in ['GET', 'POST', 'PUT', 'DELETE']:
method = method.encode('ascii', 'replace')
environ['REQUEST_METHOD'] = method
return self.app(environ, start_response)
But still on submission of the delete form it cannot access the delete method?
What is going wrong?
Edit:
The problem with delete is as follows.
When I submit the form it seems it tries to "POST" to url:
/storeadd/13?__METHOD_OVERRIDE__=DELETE
But the POST url rule says it can only be: /storeadd. Thus it gives 405 ERROR Page.
Thus the override which should happen never happens.

Have you applied this middleware on your flask application?
app.wsgi_app = MethodRewriteMiddleware(app.wsgi_app)

Related

How to print output using only a POST Method?

How can I print something like this:
{
username = admin
email = admin#localhost
id=42
}
With only using a method = ['POST'] and without using render_template?
PS: I already made it run with ['GET']
Here's my code:
from flask import Flask, jsonify, request
app = Flask(__name__)
#app.route('/', methods=['POST'])
def index():
if request.method == 'POST':
return jsonify(username="admin",
email="admin#localhost",
id="42")
else:
if request.method == 'POST':
return jsonify(username="admin",
email="admin#localhost",
id="42")
if __name__ == "__main__":
app.run()
And what I get is a 405 Method error.
Hey make sure your trailing stashes in your html are correct.
you may refer to : Flask - POST Error 405 Method Not Allowed and flask documentation : http://flask.pocoo.org/docs/0.10/quickstart/
this
<form action="/" method="post">
and this is same same but different
<form action="" method="post">
Accessing it without a trailing slash will cause Flask to redirect to the canonical URL with the trailing slash.
Given your error 405, I am suspecting that this is your problem. GET is fine, because you will just be redirected.
Try returning the form (as biobirdman said) on a GET request. Not sure why you need the request.method == 'POST' conditional statement. The parameter methods=['POST'] in the route should suffice.
Try this:
from flask import Flask, jsonify, request
app = Flask(__name__)
#app.route('/', methods=['POST'])
def index():
return jsonify(username="admin", email="admin#localhost", id="42")
#app.route('/', methods=['GET'])
def form():
return "<form action='/' method='POST'>" \
"<input type='submit'>" \
"</form>"
if __name__ == "__main__":
app.run()

Google App Engine: 404 Resource not found

I am trying to build a basic blog model using Google App Engine in Python. However, something's wrong with my code I suppose, and I am getting a 404 error when I try to display all the posted blog entries on a single page. Here's the python code:
import os
import re
import webapp2
import jinja2
from string import letters
from google.appengine.ext import db
template_dir = os.path.join(os.path.dirname(__file__), 'templates')
jinja_env = jinja2.Environment(loader = jinja2.FileSystemLoader(template_dir), autoescape=True)
class Handler(webapp2.RequestHandler):
def write(self, *a, **kw):
self.response.out.write(*a, **kw)
def render_str(self, template, **params):
t = jinja_env.get_template(template)
return t.render(params)
def render(self, template, **kw):
self.write(self.render_str(template, **kw))
def post_key(name = "dad"):
return db.Key.from_path('blog', name)
class Blogger(db.Model):
name = db.StringProperty()
content = db.TextProperty()
created = db.DateTimeProperty(auto_now_add = True)
def render(self):
self._render_text = self.content.replace('\n', '<br>')
return render_str("post.html", p = self)
class MainPage(Handler):
def get(self):
self.response.write("Visit our blog")
class BlogHandler(Handler):
def get(self):
posts = db.GqlQuery("SELECT * FROM Blogger order by created desc")
self.render("frontblog.html", posts = posts)
class SubmitHandler(Handler):
def get(self):
self.render("temp.html")
def post(self):
name = self.request.get("name")
content = self.request.get("content")
if name and content:
a = Blogger(name = name, content = content, parent = post_key())
a.put()
self.redirect('/blog/%s' % str(a.key().id()))
else:
error = "Fill in both the columns!"
self.render("temp.html", name = name, content = content, error = error)
class DisplayPost(Handler):
def get(self, post_id):
po = Blogger.get_by_id(int(post_id))
if po:
self.render("perma.html", po = po)
else:
self.response.write("404 Error")
app = webapp2.WSGIApplication([('/', MainPage),
('/blog', BlogHandler),
('/blog/submit', SubmitHandler),
('/blog/([0-9]+)', DisplayPost)], debug=True)
After posting my content, it gets redirected to a permalink. However, this is the error I am getting on submitting my post:
404 Not Found
The resource could not be found
Here's the frontblog.html source code, in case that would help:
<!DOCTYPE html>
<html>
<head>
<title>CS 253 Blog</title>
</head>
<body>
<a href="/blog">
CS 253 Blog
</a>
<div id="content">
{% block content %}
{%for post in posts%}
{{post.render() | safe}}
<br></br>
{%endfor%}
{% endblock %}
</div>
</body>
</html>
So basically, I am not being redirected to the permalink page. What seems to be the problem?
When you create your post, you're giving it a parent (not sure why). But when you get it, you do so by the ID only, and don't take into account the parent ID. In the datastore, a key is actually a path consisting of all the parent kinds and IDs/names and then those of the current entity, and to get an object you need to pass the full path.
Possible solutions here:
Drop the parent key, since it isn't doing anything here as you're always setting it to the same value;
Use it when you get the object: Blogger.get_by_id(post_id, parent=parent_key()) - obviously this only works if the parent is always the same;
Use the full stringified key in the path, rather than just the ID, and do Blogger.get(key) - you'll also need to change the route regex to accept alphanumeric chars, eg '/blog/(\w+)', and change the redirect to '/blog/%s' % a.key().

Flask-WTF SelectField with CSRF protection enabled

I'm having a problem when submitting a form containing a dynamically populated SelectField. For some reason when Flask tries to validate the CSRF token it always fails when the SelectField is in the form. When I remove the SelectField from the form, it validates the CSRF token successfully.
Has anyone come across this behavior?
EDIT
Form:
class AddToReportForm(Form):
selectReportField = SelectField(u'Reports',choices=[('test1','test')])
def __init__(self, *args, **kwargs):
"""
Initiates a new user form object
:param args: Python default
:param kwargs: Python default
"""
Form.__init__(self, *args, **kwargs)
def validate(self,id_list):
rv = Form.validate(self)
if not rv:
print False
#Check for the CSRF Token, if it's not there abort.
return False
print True
return True
Jinja2:
<form method=post name="test">
{{ form.hidden_tag()}}
{{ form.selectReportField }}
Add to report
</form>
Rendering:
form = AddToReportForm()
return render_template('random',title='add reports',form=form
Where are you setting SECRET_KEY? It must be available either in the Form class:
class AddToReportForm(Form):
selectReportField = SelectField(u'Reports',choices=[('test1','test')])
SECRET_KEY = "myverylongsecretkey"
def __init__(self, *args, **kwargs):
"""
Initiates a new user form object
:param args: Python default
:param kwargs: Python default
"""
Form.__init__(self, *args, **kwargs)
def validate(self,id_list):
rv = Form.validate(self)
if not rv:
print False
#Check for the CSRF Token, if it's not there abort.
return False
return True
or in the application bootstrap:
app = Flask(__name__)
app.secret_key = 'myverylongsecretkey'
or in the constructor:
form = AddToReportForm(secret_key='myverylongsecretkey')
return render_template('random',title='add reports',form=form)
I still can't see any connection between SelectField and CSRF. The validate method is little suspicious and the extra argument would trip the following testcase, but as it stands this seems to work just fine:
from flask import Flask, render_template_string
from flaskext.wtf import Form, SelectField
app = Flask(__name__)
app.debug = True
app.secret_key = 's3cr3t'
class AddToReportForm(Form):
selectReportField = SelectField(u'Reports', choices=[('test1', 'test')])
#app.route('/test', methods=['GET', 'POST'])
def test():
form = AddToReportForm()
if form.validate_on_submit():
print 'OK'
return render_template_string('''\
<form method=post name="test">
{{ form.hidden_tag()}}
{{ form.selectReportField }}
<input type="submit">
</form>
''', form=form)
app.run(host='0.0.0.0')
Recommended use:
app.secret_key = 'key here' # key user defined

Implementing breadcrumbs in Python using Flask?

I want breadcrumbs for navigating my Flask app. An option could be to use a general Python module like bread.py:
The bread object accepts a url string and grants access to the url
crumbs (parts) or url links (list of hrefs to each crumb) .
bread.py generates the breadcrumb from the url path, but I want the elements of the breadcrumb to be the title and link of the previously visited pages.
In Flask, maybe this can be done using a decorator or by extending the #route decorator.
Is there a way to have each call of a route() add the title and link of the page (defined in the function/class decorated with #route) to the breadcrumb? Are there other ways to do it? Any examples of breadcrumbs implemented for Flask?
So you're after "path/history" breadcrumbs, rather than "location" breadcrumbs to use the terminology from the wikipedia article?
If you want to have access to the user's history of visited links, then you're going to have to save them in a session. I've had a go at creating a decorator to do this.
breadcrumb.py:
import functools
import collections
import flask
BreadCrumb = collections.namedtuple('BreadCrumb', ['path', 'title'])
def breadcrumb(view_title):
def decorator(f):
#functools.wraps(f)
def decorated_function(*args, **kwargs):
# Put title into flask.g so views have access and
# don't need to repeat it
flask.g.title = view_title
# Also put previous breadcrumbs there, ready for view to use
session_crumbs = flask.session.setdefault('crumbs', [])
flask.g.breadcrumbs = []
for path, title in session_crumbs:
flask.g.breadcrumbs.append(BreadCrumb(path, title))
# Call the view
rv = f(*args, **kwargs)
# Now add the request path and title for that view
# to the list of crumbs we store in the session.
flask.session.modified = True
session_crumbs.append((flask.request.path, view_title))
# Only keep most recent crumbs (number should be configurable)
if len(session_crumbs) > 3:
session_crumbs.pop(0)
return rv
return decorated_function
return decorator
And here's a test application that demonstrates it. Note that I've just used Flask's built-in client side session, you'd probably want to use a more secure server-side session in production, such as Flask-KVsession.
#!/usr/bin/env python
import flask
from breadcrumb import breadcrumb
app = flask.Flask(__name__)
#app.route('/')
#breadcrumb('The index page')
def index():
return flask.render_template('page.html')
#app.route('/a')
#breadcrumb('Aardvark')
def pagea():
return flask.render_template('page.html')
#app.route('/b')
#breadcrumb('Banana')
def pageb():
return flask.render_template('page.html')
#app.route('/c')
#breadcrumb('Chimp')
def pagec():
return flask.render_template('page.html')
#app.route('/d')
#breadcrumb('Donkey')
def paged():
return flask.render_template('page.html')
if __name__ == '__main__':
app.secret_key = '83cf5ca3-b1ee-41bb-b7a8-7a56c906b05f'
app.debug = True
app.run()
And here's the contents of templates/page.html:
<!DOCTYPE html>
<html>
<head><title>{{ g.title }}</title></head>
<body>
<h1>{{ g.title }}</h1>
<p>Breadcrumbs:
{% for crumb in g.breadcrumbs %}
{{ crumb.title }}
{% if not loop.last %}»{% endif %}
{% endfor %}
</p>
<p>What next?</p>
<ul>
<li>Aardvark?</li>
<li>Banana?</li>
<li>Chimp?</li>
<li>Donkey?</li>
</ul>
</body>
</html>
i was trying to use the breadcrumb.py , but i was need to check:
if the new item "item = (flask.request.path, view title) " is already exist in the session crumbs, then i will delete all other items frome the index to the end, i do this for Avoid repetition in my session crumds.
flask.session.modified = True
item = (flask.request.path, view_title)
try:
if not item in session_crumbs:
session_crumbs.append(item)
else:
index = session_crumbs.index(item)
session_crumbs = session_crumbs[:index+1]
except:
pass
return rv
return decorated_function
return decorator

Bad Request Error with flask, python, HTML, unusual initialization behavior with flask.request.form

I'm writing a web-app using flask, python and HTML. My issue is that the first time I load the a webpage, I get the following error
Bad Request The browser (or proxy) sent a request that this server
could not understand.
I'm able to get the page to load eventually by "tricking" first running it without any flask.request.form calls, and then putting them back in (details below). Something must be going wrong in my initialization. I'm new to flask and using python with HTML.
Assume I'm working from a directory called example. I have a python script called test.py and an HTML template called test.html with the following directory structure:
\example\test.py
\example\templates\test.html
My python script test.py is:
import sys
import flask, flask.views
app = flask.Flask(__name__)
app.secret_key = "bacon"
class View(flask.views.MethodView):
def get(self):
result = flask.request.form['result']
return flask.render_template('test.html', result=result)
# return flask.render_template('test.html')
def post(self):
return self.get()
app.add_url_rule('/', view_func=View.as_view('main'), methods=['GET', 'POST'])
app.debug = True
app.run()
and my HTML in test.html is
<html>
<head>
</head>
<body>
<form action="/" method="post">
Enter something into the box:
<input type="text" name="result"/><br>
<input type="submit" value="Execute!"/>
</form>
</body>
</html>
Steps to reproduce the error
1: Run the test.py script, and open up the URL in a browser
Running on http://127.0.0.1:5000/
You should see the following error
Bad Request The browser (or proxy) sent a request that this server
could not understand.
2: Comment out the first 2 lines of the def get(self) function and uncomment the 3rd line of the def get(self) function so that test.py looks like this
import sys
import flask, flask.views
app = flask.Flask(__name__)
app.secret_key = "bacon"
class View(flask.views.MethodView):
def get(self):
# result = flask.request.form['result']
# return flask.render_template('test.html', result=result)
return flask.render_template('test.html')
def post(self):
return self.get()
app.add_url_rule('/', view_func=View.as_view('main'), methods=['GET', 'POST'])
app.debug = True
app.run()
3: Refresh the URL, and you will see that things work (though I ultimately want to be able to return the value of result
4: Now, switch the lines that are commented out again. I.e, uncomment the first 2 lines of the def get(self) function and comment out the 3rd line of the def get(self) function so that test.py looks like this
import sys
import flask, flask.views
app = flask.Flask(__name__)
app.secret_key = "bacon"
class View(flask.views.MethodView):
def get(self):
result = flask.request.form['result']
return flask.render_template('test.html', result=result)
# return flask.render_template('test.html')
def post(self):
return self.get()
app.add_url_rule('/', view_func=View.as_view('main'), methods=['GET', 'POST'])
app.debug = True
app.run()
5: Refresh the URL and now you see things will be working as desired.
This is just a toy example illustrating the real problem exhibiting this weird behavior of how I have to "trick" my browser into showing me this webpage. The
The issue here is that you are attempting to access POSTed variables in a method that will only handle GET requests. When you attempt to access a query string or POST parameter that is not set Flask will, by default, raise a BadRequest error (because you are asking for something that the person hitting the page did not supply).
What happens if the key does not exist in the form attribute? In that case a special KeyError is raised. You can catch it like a standard KeyError but if you don’t do that, a HTTP 400 Bad Request error page is shown instead. So for many situations you don’t have to deal with that problem.
If you need to access a variable from either request.args (GET) or request.form (POST) and you don't need it to be set use the get method to get the value if it is there (or None if it is not set.
# Will default to None
your_var = request.form.get("some_key")
# Alternately:
your_var = request.form.get("some_key", "alternate_default_value")
Here's an alternate way of structuring your code:
import sys
import flask, flask.views
app = flask.Flask(__name__)
app.secret_key = "bacon"
app.debug = True
class View(flask.views.MethodView):
def get(self):
"""Enable user to provide us with input"""
return self._default_actions()
def post(self):
"""Map user input to our program's inputs - display errors if required"""
result = flask.request.form['result']
# Alternately, if `result` is not *required*
# result = flask.request.form.get("result")
return self._default_actions(result=result)
def _default_actions(self, result=None):
"""Deal with the meat of the matter, taking in whatever params we need
to get or process our information"""
if result is None:
return flask.render_template("test.html")
else:
return flask.render_template("test.html", result=result)
app.add_url_rule('/', view_func=View.as_view('main'), methods=['GET', 'POST'])
if __name__ == "__main__":
app.run()

Categories