I want to use a RecaptchaField() in my WTForms, such as:
class MyForm(Form):
name = StringField('Your name', [InputRequired(message='Please enter your name!')])
recaptcha = RecaptchaField() # RecaptchaField as provided by Flask-WTF
submit = SubmitField('Send')
Since I am using CherryPy, I am not sure whether or not I should use Flask-WTF, because Flask is a whole framework itself. I am wondering if I can use the Recaptcha functionality of Flask-WTF within my CherryPy solution. I tried the following:
from wtforms import StringField
from wtforms.validators import InputReqired
from flask.ext.wtf import Form
from flask.ext.wtf.recaptcha import RecaptchaField
# ...
form = MyForm() # Somewhere in my code
as seen in this Example here. I get the following Exception:
RuntimeError: working outside of application context
It means I have to properly set up a Flask app considering the right context... This is where I am starting to wonder if I am doing the right approach. Is there no other way than set up a separate Flask app inside my CherryPy app??
My answer is mostly relevant to CherryPy and reCaptcha parts of the question. In my opinion, usage of WTForms and other similar libraries leads to a design problem when, speaking in terms of MVC-like design, you scatter the view and the controller into your own code and WTForms code/configuration. Things are simple when you manage one thing in a single place. Thus I suggest to use template engine like Jinja2 for the view (you can create a macro for a repetitive form element) and use input validation library like voluptuous in the controller (you can use same schema for form and API validation).
If you can't avoid WTForms, just grab validateRecaptcha and turn it into WTForms custom validator.
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import urllib
import json
import cherrypy
import voluptuous as volu
config = {
'global' : {
'server.socket_host' : '127.0.0.1',
'server.socket_port' : 8080,
'server.thread_pool' : 8
},
'/' : {
'recaptcha' : {
# By default, all keys work on localhost
'siteKey' : '6LeYIbsSAAAAACRPIllxA7wvXjIE411PfdB2gt2J',
'secret' : '6LeYIbsSAAAAAJezaIq3Ft_hSTo0YtyeFG-JgRtu'
}
}
}
def validateRecaptcha(value):
'''https://developers.google.com/recaptcha/docs/verify'''
if 'g-recaptcha-response' not in cherrypy.request.params:
raise volu.Invalid('Recaptcha response is missing')
payload = urllib.urlencode({
'secret' : cherrypy.request.config['recaptcha']['secret'],
'remoteip' : cherrypy.request.headers['remote-addr'],
'response' : cherrypy.request.params['g-recaptcha-response']
})
url = 'https://www.google.com/recaptcha/api/siteverify'
response = json.load(urllib.urlopen(url, payload))
if not response['success']:
cherrypy.log(str(response))
raise volu.Invalid(response['error-codes'])
class App:
#cherrypy.expose
def index(self, **kwargs):
form = dict(form = {'value': ''}, errors = '')
if cherrypy.request.method == 'POST':
schema = volu.Schema({
'value' : volu.All(unicode, volu.Length(min = 8, max = 16)),
'g-recaptcha-response' : validateRecaptcha,
}, required = True, extra = True)
try:
kwargs = schema(kwargs)
except volu.MultipleInvalid as ex:
form = dict(form = kwargs, errors = {e.path[0] for e in ex.errors})
else:
raise cherrypy.HTTPRedirect('#success')
return '''<!DOCTYPE html>
<html>
<head>
<title>reCAPTCHA demo</title>
<script src="https://www.google.com/recaptcha/api.js" type="text/javascript"></script>
</head>
<body>
<form action="/" method="POST">
<div style='border: 1px red solid'>{errors}</div>
<div>Name</div>
<input type="text" name="value" value="{form[value]}"/>
<br/>
<div class="g-recaptcha" data-sitekey="{0}"></div>
<br/>
<input type="submit" value="Submit"/>
</form>
</body>
</html>
'''.format(cherrypy.request.config['recaptcha']['siteKey'], **form)
if __name__ == '__main__':
cherrypy.quickstart(App(), '/', config)
Related
I am trying to do a simple POST operation using FastAPI. I have created a basic structure using BaseModel, which has only two attributes, namely name and roll.
import uvicorn
from fastapi import FastAPI
from pydantic import BaseModel
class Item(BaseModel):
name: str
roll: int
app = FastAPI()
#app.post("/")
async def create_item(item: Item):
return item
if __name__ == '__main__':
uvicorn.run(app, port=8080, host='0.0.0.0')
I would like to post these data using this POST operation -
{"name":"XYZ", "roll":51}.
I know about the automatic documentation at http://localhost:8080/docs provided by Swagger UI (OpenAPI), which we can use to post data. But I wouldn't want to use it. What I would like is to directly post the data using the URL http://localhost:8080/ and would like to see the result in the browser itself, instead of seeing the result in Swaggger UI.
You would need to use a Javascript interface/library such as Fetch API, which allows you to send data in JSON format (example is given below). For submiting Form data instead, have a look at this answer, while for posting both Files and Form/JSON data, have a look at this answer.
For the frontend, you could use Jinja2Templates to render and return a TemplateResponse that includes your HTML/JS code, etc. You can use an HTML form to submit your data and then have the form-data converted into JSON, as described here. Otherwise, you could post your JSON data directly, as shown hereāfor example, body: JSON.stringify({name: "foo", roll: 1}).
app.py
from fastapi import FastAPI, Request
from fastapi.templating import Jinja2Templates
from pydantic import BaseModel
app = FastAPI()
templates = Jinja2Templates(directory="templates")
class Item(BaseModel):
name: str
roll: int
#app.post("/")
async def create_item(item: Item):
return item
#app.get("/")
async def index(request: Request):
return templates.TemplateResponse("index.html", {"request": request})
templates/index.html
<!DOCTYPE html>
<html>
<body>
<h1>Post JSON Data</h1>
<form method="post" id="myForm">
name : <input type="text" name="name" value="foo">
roll : <input type="number" name="roll" value="1">
<input type="button" value="Submit" onclick="submitForm()">
</form>
<div id="responseArea"></div>
<script>
function submitForm() {
var formElement = document.getElementById('myForm');
var data = new FormData(formElement);
fetch('/', {
method: 'POST',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json'
},
body: JSON.stringify(Object.fromEntries(data))
})
.then(resp => resp.text()) // or, resp.json(), etc.
.then(data => {
document.getElementById("responseArea").innerHTML = data;
})
.catch(error => {
console.error(error);
});
}
</script>
</body>
</html>
I have a super simple flask form for a service that I'm trying to integrate with the Stripe API:
from flask_wtf import FlaskForm
import wtforms as wtf
class ServiceForm(FlaskForm):
name = wtf.StringField('The service name')
submit_ = wtf.SubmitField('Submit >')
I'm serving this form along with my stripe key as follows:
#app.route('/index', methods=['GET'])
def index() -> str:
form = ServiceForm()
render_template('index.html', form=form, key='some stripe key',
service='some service', service_price=2500)
and the index.html file looks like this:
<form id="service-form" action="{{ url_for('pay_for_service', service=service) }}" method="POST" novalidate>
{{ form.csrf_token }}
{{ form.name }}
{{ form.submit_(class_="some-button" id="service-form-submit-button") }}
<script
src="https://checkout.stripe.com/checkout.js"
class="stripe-button"
data-key="{{ key }}"
data-amount="{{ service_price }}"
data-locale="auto">
</script>
</form>
The action of this form points to the following route:
#app.route('/pay+for+service/<string:service>', methods=['POST'])
def pay_for_service(service: str) -> str:
form = ServiceForm()
if form.validate_on_submit():
# ... do some validation
# now launch the Stripe dialog box to input credit card details.
which I'll use to validate the form submission before the Stripe payment dialog box is launched.
Basically, I want the stripe-button in the script to be embedded in the form.submit_ button, which has a custom some-button class. Then I want the Stripe payment pop-up to show after I've validated the form. How do I do this please? I think it's pretty easy but been scratching my head for ages!
Thanks for any help, and stay safe :-)
It's most easily done with an ajax call. Set the button action to call "submit_form" which will be:
function submit_form(Elem) {
$.ajax({
type: 'POST',
url: "{{ url_for('pay_for_service', service=service) }}",
data: $("#service-form").serialize(),
success: function(data) {
if (data['status'] == 'success') {
// call stripe
} else {
alert(data['data']['message'])
};
}
});
}
You won't need two view functions, a single one will do:
#app.route('/index', methods=['GET'])
def index() -> str:
form = ServiceForm()
if form.validate_on_submit():
return json.dumps({'status': 'success', 'data': {'message': 'ok'}})
elif request.method == 'POST':
message = str(form.errors)
return json.dumps({'status': 'fail', 'data': {'message': message }})
render_template('index.html', form=form, key='some stripe key',
service='some service', service_price=2500)
This is not possible with the "simple" integration approach. It's possible to run your own validation and trigger the Legacy Checkout modal programmatically using the "custom" integration approach, as seen in this example.
var handler = StripeCheckout.configure({ ... })
// later ...
handler.open({...})
However, note that this is all using an old deprecated version of Checkout. You should strongly consider using the new Checkout. Among other significant advantages, you can to any validation you like server-side before creating the checkout session.
For example, if I have the following code in index.html:
<div id='1'></div>
<div id='2'></div>
<div id='3'></div>
And, I have the following code in Python:
from flask import *
#app.route("/")
def index():
return render_template("index.html")
#app.route('/experts')
def route1():
return render_template("experts.html", data=data)
So, among the three div blocks. When I click on any one of them. I want the program to know which one I click on, and pass the value of id (1,2,3) into the data variable in python so that I can use it on "expert.html".
What are some good ways I can achieve it? Thank you in advanced!
Instead of divs, you can use buttons. That way, ajax can be utilized in the front end to retrieve the id of the button clicked and pass it to the backend:
"index.html":
<html>
<body>
<button id='1'>Button1</button>
<button id='2'>Button2</button>
<button id='3'>Button3</button>
</body>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.1.1/jquery.min.js"></script>
<script>
$(document).ready(function() {
$('button').click(function(event) {
var the_id = event.target.id;
$.ajax({
url: "/get_id",
type: "get",
data: {the_id: the_id},
success: function(response) {
window.location.replace('/experts');
},
error: function(xhr) {
//Do Something to handle error
}
});
});
</script>
</html>
Then, a route to receive the id can be created, the results from the ajax can be stored in flask.session, and a "success" object can be passed back to the ajax in the index.html template. From the jquery in the template, the app can be redirected to /expert:
import flask
app = flask.Flask(__name__)
app.secret_key = 'SOME_SECRET_KEY'
#app.route('/get_id')
def expert():
button_id = flask.request.args.get('the_id')
flask.session['button_id'] = button_id
return flask.jsonify({'success':True})
#app.route('/experts', methods=['GET'])
def experts():
return render_template("experts.html", data=flask.session['button_id'])
I am very new to Flask. I have a mysql database and a template. Let's say I have 3 images
<div><img src= pathOfImage id="profile1"/></div>
<div><img src= pathOfImage id="profile2"/></div>
<div><img src= pathOfImage id="profile3"/></div>
The id of each image (profile1,profile2,profile3) is the primary key of the some tables in the database. What I want to do is to find the values of the corresponding attributes of that tuple by using the primary key. Then, load that those values to the template from the tuples.
And, I have the following code in Python:
from flask import *
#app.route("/")
def index():
return render_template("index.html")
#app.route('/experts')
def route1():
return render_template("experts.html", data=data)
The snippet of HTML code I gave above is in expert.html. I almost of SQL query that was not listed above, data on the second parameter in render_template in route1() is the SQL tuple, which generate all these images and the ID.
I have tried to put a button next to the images, and give id to the button instead. Then, pass the id to the python script as a variable using Ajax, and get the SQL tuple.
However, this isn't the hard part. The hard part is making the new route and loading the content. I have tried make a new route using "app.route" and pass the data into the second parameter of render_template. But, it didn't redirect to a new profile, and the method was called before I even click on the profile.
previously, I used button to retrieve the id:
<html>
<body>
<button id='1'>Button1</button>
<button id='2'>Button2</button>
<button id='3'>Button3</button>
</body>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.1.1/jquery.min.js"</script>
<script>
$(document).ready(function() {
$('button').click(function(event) {
var the_id = event.target.id;
$.ajax({
url: "/get_id",
type: "get",
data: {the_id: the_id},
success: function(response) {
},
error: function(xhr) {
}
});
})});
</script>
and, I used these to generate a new template:
import flask
from flask import *
from flaskext.mysql import MySQL
#app.route("/")
def index():
return render_template("index.html")
#app.route('/experts')
def route1():
return render_template("experts.html", data=data)
#app.route('/get_id')
#app.route('/profile')
def route2():
button_id '"' + flask.request.args.get('the_id') + '"'
//some code here to get the tuples that I need "client_info" and
//"skill_info" variable below
return render_template("profile.html", client_info=client_info,
skill_info=skill_info)
Hope someone would give a fresh start. Thanks in advance!
Instead of passing info by ajax you can pass it through the route url.
experts.html
<div><img src="pathOfImage"></div>
<div><img src="pathOfImage"></div>
<div><img src="pathOfImage"></div>
flask routes
import flask
from flask import *
from flaskext.mysql import MySQL
#app.route("/")
def index():
return render_template("index.html")
#app.route('/experts')
def route1():
return render_template("experts.html", data=data)
#app.route('/profile/<id>')
def route2(id):
# id variable contains user profile id string as per user click
# depending on id set variables client_info and skill_info and render
return render_template("profile.html", client_info=client_info, skill_info=skill_info)
{% for profile in profiles %}
<img src="{{profile.img}}">
{% endfor %}
...I guess? maybe?
I'm trying to host a website off a Beaglebone Black using lighttpd and web.py. Any post request creates Error 413 - Request Entity Too Large. I am very new to web development, so I apologize if I've used the wrong terminology. To be clear, I am not attempting to upload any file of any kind. I created a simple page, test, to illustrate the error. My code:
First, Monitor.py:
#!/usr/bin/env python
import web
import Model
from Account import Account
from Forgot import Forgot
from Login import Login
from Reset import Reset
from Subscribe import Subscribe
from Success import Success
from Status import Status
from Test import Test
urls = ('/', 'Status',
'/test', 'Test',
'/account', 'Account',
'/forgot', 'Forgot',
'/login', 'Login',
'/reset/(\d+)', 'Reset',
'/success', 'Success',
'/subscribe', 'Subscribe',
)
render = web.template.render('templates')
app = web.application(urls, globals())
web.config.debug = False
session = Model.create_session(app)
if __name__ == '__main__':
app.run()
Now, Test.py:
#!/usr/bin/env python
import web
import Model
render = web.template.render('templates')
class Test:
def GET(self):
return render.test()
def POST(self):
i = web.input()
raise web.seeother('/test')
Now, the html file, located in the templates folder:
$def with()
<html>
<head>
<link rel="stylesheet" href="static/stylesheet.css" type="text/css" media="screen" charset="utf-8"/>
<title>Account Page</title>
</head>
<body>
div>
<form action="/test" method="POST">
<table class="std-element">
<tr>
<th>
<input type="text" name="example_text">
</th>
</tr>
</table>
</form>
</div>
</body>
</html>
The relevant portion of the model file:
session = None
def create_session(app):
global session
session = web.session.Session(app, web.session.DiskStore('sessions'), initializer={'username':default_username})
session['username'] = default_username
return session
def get_session():
global session
return session
And, finally, the lighttpd.config file:
server.modules = (
"mod_access",
"mod_accesslog",
"mod_alias",
"mod_compress",
)
server.document-root = "/var/www/"
server.upload-dirs = ( "/var/cache/lighttpd/uploads" )
server.errorlog = "/var/log/lighttpd/error.log"
server.pid-file = "/var/run/lighttpd.pid"
server.username = "www-data"
server.groupname = "www-data"
server.port = 80
server.breakagelog = "/var/log/lighttpd/breakage.log"
server.max-request-size=100000000
server.uploads-dirs=("/mnt")
server.network-backend="write"
## Use ipv6 if available
#include_shell "/usr/share/lighttpd/use-ipv6.pl"
compress.cache-dir = "/var/cache/lighttpd/compress/"
compress.filetype = ( "application/x-javascript", "text/css", "text/html", "text/plain" )
include_shell "/usr/share/lighttpd/create-mime.assign.pl"
include_shell "/usr/share/lighttpd/include-conf-enabled.pl"
server.max-request-size=100000000 is way too large. It is measured in KB. Internally, lighttpd left-shifts that number 10 bits, which, in your version of lighttpd, results in any POST being compared with 0 and rejected because it is too large. This has been fixed in lighttpd 1.4.40, but you should probably reduce that limit anyway. Having a 100 TB (!) request size limit is probably not going to protect your Beaglebone. Try this: server.max-request-size=1024 will set a 1 MB limit on POST requests. (Again, that config directive is in KB)