forked from orbit-oss/flask
add javascript ajax example
This commit is contained in:
parent
d8bf589d48
commit
fce1885f76
21 changed files with 341 additions and 13 deletions
|
|
@ -162,5 +162,6 @@ explanation of the little bit of code above:
|
||||||
argument. Note that we can use the `$SCRIPT_ROOT` variable here that
|
argument. Note that we can use the `$SCRIPT_ROOT` variable here that
|
||||||
we set earlier.
|
we set earlier.
|
||||||
|
|
||||||
If you don't get the whole picture, download the :gh:`sourcecode
|
Check out the :gh:`example source <examples/javascript>` for a full
|
||||||
for this example <examples/jqueryexample>`.
|
application demonstrating the jQuery on this page, as well as the same
|
||||||
|
thing using ``XMLHttpRequest`` and ``fetch``.
|
||||||
|
|
|
||||||
|
|
@ -209,12 +209,6 @@ Running that should now give us three passing tests::
|
||||||
|
|
||||||
============= 3 passed in 0.23 seconds ==============
|
============= 3 passed in 0.23 seconds ==============
|
||||||
|
|
||||||
For more complex tests with headers and status codes, check out the
|
|
||||||
`MiniTwit Example`_ from the sources which contains a larger test
|
|
||||||
suite.
|
|
||||||
|
|
||||||
.. _MiniTwit Example:
|
|
||||||
https://github.com/pallets/flask/tree/master/examples/minitwit/
|
|
||||||
|
|
||||||
Other Testing Tricks
|
Other Testing Tricks
|
||||||
--------------------
|
--------------------
|
||||||
|
|
|
||||||
14
examples/javascript/.gitignore
vendored
Normal file
14
examples/javascript/.gitignore
vendored
Normal file
|
|
@ -0,0 +1,14 @@
|
||||||
|
venv/
|
||||||
|
*.pyc
|
||||||
|
__pycache__/
|
||||||
|
instance/
|
||||||
|
.cache/
|
||||||
|
.pytest_cache/
|
||||||
|
.coverage
|
||||||
|
htmlcov/
|
||||||
|
dist/
|
||||||
|
build/
|
||||||
|
*.egg-info/
|
||||||
|
.idea/
|
||||||
|
*.swp
|
||||||
|
*~
|
||||||
31
examples/javascript/LICENSE
Normal file
31
examples/javascript/LICENSE
Normal file
|
|
@ -0,0 +1,31 @@
|
||||||
|
Copyright © 2010 by the Pallets team.
|
||||||
|
|
||||||
|
Some rights reserved.
|
||||||
|
|
||||||
|
Redistribution and use in source and binary forms of the software as
|
||||||
|
well as documentation, with or without modification, are permitted
|
||||||
|
provided that the following conditions are met:
|
||||||
|
|
||||||
|
* Redistributions of source code must retain the above copyright notice,
|
||||||
|
this list of conditions and the following disclaimer.
|
||||||
|
|
||||||
|
* Redistributions in binary form must reproduce the above copyright
|
||||||
|
notice, this list of conditions and the following disclaimer in the
|
||||||
|
documentation and/or other materials provided with the distribution.
|
||||||
|
|
||||||
|
* Neither the name of the copyright holder nor the names of its
|
||||||
|
contributors may be used to endorse or promote products derived from
|
||||||
|
this software without specific prior written permission.
|
||||||
|
|
||||||
|
THIS SOFTWARE AND DOCUMENTATION IS PROVIDED BY THE COPYRIGHT HOLDERS AND
|
||||||
|
CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING,
|
||||||
|
BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
|
||||||
|
COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||||
|
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||||||
|
NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
|
||||||
|
USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
||||||
|
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||||
|
THIS SOFTWARE AND DOCUMENTATION, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||||
|
SUCH DAMAGE.
|
||||||
4
examples/javascript/MANIFEST.in
Normal file
4
examples/javascript/MANIFEST.in
Normal file
|
|
@ -0,0 +1,4 @@
|
||||||
|
include LICENSE
|
||||||
|
graft js_example/templates
|
||||||
|
graft tests
|
||||||
|
global-exclude *.pyc
|
||||||
49
examples/javascript/README.rst
Normal file
49
examples/javascript/README.rst
Normal file
|
|
@ -0,0 +1,49 @@
|
||||||
|
JavaScript Ajax Example
|
||||||
|
=======================
|
||||||
|
|
||||||
|
Demonstrates how to post form data and process a JSON response using
|
||||||
|
JavaScript. This allows making requests without navigating away from the
|
||||||
|
page. Demonstrates using |XMLHttpRequest|_, |fetch|_, and
|
||||||
|
|jQuery.ajax|_. See the `Flask docs`_ about jQuery and Ajax.
|
||||||
|
|
||||||
|
.. |XMLHttpRequest| replace:: ``XMLHttpRequest``
|
||||||
|
.. _XMLHttpRequest: https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest
|
||||||
|
|
||||||
|
.. |fetch| replace:: ``fetch``
|
||||||
|
.. _fetch: https://developer.mozilla.org/en-US/docs/Web/API/WindowOrWorkerGlobalScope/fetch
|
||||||
|
|
||||||
|
.. |jQuery.ajax| replace:: ``jQuery.ajax``
|
||||||
|
.. _jQuery.ajax: https://api.jquery.com/jQuery.ajax/
|
||||||
|
|
||||||
|
.. _Flask docs: http://flask.pocoo.org/docs/patterns/jquery/
|
||||||
|
|
||||||
|
|
||||||
|
Install
|
||||||
|
-------
|
||||||
|
|
||||||
|
::
|
||||||
|
|
||||||
|
python3 -m venv venv
|
||||||
|
. venv/bin/activate
|
||||||
|
pip install -e .
|
||||||
|
|
||||||
|
|
||||||
|
Run
|
||||||
|
---
|
||||||
|
|
||||||
|
::
|
||||||
|
|
||||||
|
export FLASK_APP=js_example
|
||||||
|
flask run
|
||||||
|
|
||||||
|
Open http://127.0.0.1:5000 in a browser.
|
||||||
|
|
||||||
|
|
||||||
|
Test
|
||||||
|
----
|
||||||
|
|
||||||
|
::
|
||||||
|
|
||||||
|
pip install -e '.[test]'
|
||||||
|
coverage run -m pytest
|
||||||
|
coverage report
|
||||||
5
examples/javascript/js_example/__init__.py
Normal file
5
examples/javascript/js_example/__init__.py
Normal file
|
|
@ -0,0 +1,5 @@
|
||||||
|
from flask import Flask
|
||||||
|
|
||||||
|
app = Flask(__name__)
|
||||||
|
|
||||||
|
from js_example import views
|
||||||
33
examples/javascript/js_example/templates/base.html
Normal file
33
examples/javascript/js_example/templates/base.html
Normal file
|
|
@ -0,0 +1,33 @@
|
||||||
|
<!doctype html>
|
||||||
|
<title>JavaScript Example</title>
|
||||||
|
<link rel="stylesheet" href="https://unpkg.com/sakura.css@1.0.0/css/normalize.css">
|
||||||
|
<link rel="stylesheet" href="https://unpkg.com/sakura.css@1.0.0/css/sakura-earthly.css">
|
||||||
|
<style>
|
||||||
|
ul { margin: 0; padding: 0; display: flex; list-style-type: none; }
|
||||||
|
li > * { padding: 1em; }
|
||||||
|
li.active > a { color: #5e5e5e; border-bottom: 2px solid #4a4a4a; }
|
||||||
|
form { display: flex; }
|
||||||
|
label > input { width: 3em; }
|
||||||
|
form > * { padding-right: 1em; }
|
||||||
|
#result { font-weight: bold; }
|
||||||
|
</style>
|
||||||
|
<ul>
|
||||||
|
<li><span>Type:</span>
|
||||||
|
<li class="{% if js == 'plain' %}active{% endif %}">
|
||||||
|
<a href="{{ url_for('index', js='plain') }}">Plain</a>
|
||||||
|
<li class="{% if js == 'fetch' %}active{% endif %}">
|
||||||
|
<a href="{{ url_for('index', js='fetch') }}">Fetch</a>
|
||||||
|
<li class="{% if js == 'jquery' %}active{% endif %}">
|
||||||
|
<a href="{{ url_for('index', js='jquery') }}">jQuery</a>
|
||||||
|
</ul>
|
||||||
|
<hr>
|
||||||
|
<p>{% block intro %}{% endblock %}</p>
|
||||||
|
<hr>
|
||||||
|
<form>
|
||||||
|
<label>a <input name="a"></label>
|
||||||
|
<span>+</span>
|
||||||
|
<label>b <input name="b"></label>
|
||||||
|
<input type="submit" value="Calculate">
|
||||||
|
</form>
|
||||||
|
<span>= <span id="result"></span></span>
|
||||||
|
{% block script %}{% endblock %}
|
||||||
35
examples/javascript/js_example/templates/fetch.html
Normal file
35
examples/javascript/js_example/templates/fetch.html
Normal file
|
|
@ -0,0 +1,35 @@
|
||||||
|
{% extends 'base.html' %}
|
||||||
|
|
||||||
|
{% block intro %}
|
||||||
|
<a href="https://developer.mozilla.org/en-US/docs/Web/API/WindowOrWorkerGlobalScope/fetch"><code>fetch</code></a>
|
||||||
|
is the <em>new</em> plain JavaScript way to make requests. It's
|
||||||
|
supported in all modern browsers except IE, which requires a
|
||||||
|
<a href="https://github.com/github/fetch">polyfill</a>.
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block script %}
|
||||||
|
<script src="https://unpkg.com/promise-polyfill@7.1.2/dist/polyfill.min.js"></script>
|
||||||
|
<script src="https://unpkg.com/whatwg-fetch@2.0.4/fetch.js"></script>
|
||||||
|
<script>
|
||||||
|
function addSubmit(ev) {
|
||||||
|
ev.preventDefault();
|
||||||
|
fetch('{{ url_for('add') }}', {
|
||||||
|
method: 'POST',
|
||||||
|
body: new FormData(this)
|
||||||
|
})
|
||||||
|
.then(parseJSON)
|
||||||
|
.then(addShow);
|
||||||
|
}
|
||||||
|
|
||||||
|
function parseJSON(response) {
|
||||||
|
return response.json();
|
||||||
|
}
|
||||||
|
|
||||||
|
function addShow(data) {
|
||||||
|
var span = document.getElementById('result');
|
||||||
|
span.innerText = data.result;
|
||||||
|
}
|
||||||
|
|
||||||
|
document.forms[0].addEventListener('submit', addSubmit);
|
||||||
|
</script>
|
||||||
|
{% endblock %}
|
||||||
27
examples/javascript/js_example/templates/jquery.html
Normal file
27
examples/javascript/js_example/templates/jquery.html
Normal file
|
|
@ -0,0 +1,27 @@
|
||||||
|
{% extends 'base.html' %}
|
||||||
|
|
||||||
|
{% block intro %}
|
||||||
|
<a href="https://jquery.com/">jQuery</a> is a popular library that
|
||||||
|
adds cross browser APIs for common tasks. However, it requires loading
|
||||||
|
an extra library.
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block script %}
|
||||||
|
<script src="https://code.jquery.com/jquery-3.3.1.min.js"></script>
|
||||||
|
<script>
|
||||||
|
function addSubmit(ev) {
|
||||||
|
ev.preventDefault();
|
||||||
|
$.ajax({
|
||||||
|
method: 'POST',
|
||||||
|
url: '{{ url_for('add') }}',
|
||||||
|
data: $(this).serialize()
|
||||||
|
}).done(addShow);
|
||||||
|
}
|
||||||
|
|
||||||
|
function addShow(data) {
|
||||||
|
$('#result').text(data.result);
|
||||||
|
}
|
||||||
|
|
||||||
|
$('form:first').on('submit', addSubmit);
|
||||||
|
</script>
|
||||||
|
{% endblock %}
|
||||||
27
examples/javascript/js_example/templates/plain.html
Normal file
27
examples/javascript/js_example/templates/plain.html
Normal file
|
|
@ -0,0 +1,27 @@
|
||||||
|
{% extends 'base.html' %}
|
||||||
|
|
||||||
|
{% block intro %}
|
||||||
|
<a href="https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest"><code>XMLHttpRequest</code></a>
|
||||||
|
is the plain JavaScript way to make requests. It's natively supported
|
||||||
|
by all browsers.
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block script %}
|
||||||
|
<script>
|
||||||
|
function addSubmit(ev) {
|
||||||
|
ev.preventDefault();
|
||||||
|
var request = new XMLHttpRequest();
|
||||||
|
request.addEventListener('load', addShow);
|
||||||
|
request.open('POST', '{{ url_for('add') }}');
|
||||||
|
request.send(new FormData(this));
|
||||||
|
}
|
||||||
|
|
||||||
|
function addShow() {
|
||||||
|
var data = JSON.parse(this.responseText);
|
||||||
|
var span = document.getElementById('result');
|
||||||
|
span.innerText = data.result;
|
||||||
|
}
|
||||||
|
|
||||||
|
document.forms[0].addEventListener('submit', addSubmit);
|
||||||
|
</script>
|
||||||
|
{% endblock %}
|
||||||
16
examples/javascript/js_example/views.py
Normal file
16
examples/javascript/js_example/views.py
Normal file
|
|
@ -0,0 +1,16 @@
|
||||||
|
from flask import jsonify, render_template, request
|
||||||
|
|
||||||
|
from js_example import app
|
||||||
|
|
||||||
|
|
||||||
|
@app.route('/', defaults={'js': 'plain'})
|
||||||
|
@app.route('/<any(plain, jquery, fetch):js>')
|
||||||
|
def index(js):
|
||||||
|
return render_template('{0}.html'.format(js), js=js)
|
||||||
|
|
||||||
|
|
||||||
|
@app.route('/add', methods=['POST'])
|
||||||
|
def add():
|
||||||
|
a = request.form.get('a', 0, type=float)
|
||||||
|
b = request.form.get('b', 0, type=float)
|
||||||
|
return jsonify(result=a + b)
|
||||||
13
examples/javascript/setup.cfg
Normal file
13
examples/javascript/setup.cfg
Normal file
|
|
@ -0,0 +1,13 @@
|
||||||
|
[metadata]
|
||||||
|
license_file = LICENSE
|
||||||
|
|
||||||
|
[bdist_wheel]
|
||||||
|
universal = True
|
||||||
|
|
||||||
|
[tool:pytest]
|
||||||
|
testpaths = tests
|
||||||
|
|
||||||
|
[coverage:run]
|
||||||
|
branch = True
|
||||||
|
source =
|
||||||
|
js_example
|
||||||
30
examples/javascript/setup.py
Normal file
30
examples/javascript/setup.py
Normal file
|
|
@ -0,0 +1,30 @@
|
||||||
|
import io
|
||||||
|
|
||||||
|
from setuptools import find_packages, setup
|
||||||
|
|
||||||
|
with io.open('README.rst', 'rt', encoding='utf8') as f:
|
||||||
|
readme = f.read()
|
||||||
|
|
||||||
|
setup(
|
||||||
|
name='js_example',
|
||||||
|
version='1.0.0',
|
||||||
|
url='http://flask.pocoo.org/docs/patterns/jquery/',
|
||||||
|
license='BSD',
|
||||||
|
maintainer='Pallets team',
|
||||||
|
maintainer_email='contact@palletsprojects.com',
|
||||||
|
description='Demonstrates making Ajax requests to Flask.',
|
||||||
|
long_description=readme,
|
||||||
|
packages=find_packages(),
|
||||||
|
include_package_data=True,
|
||||||
|
zip_safe=False,
|
||||||
|
install_requires=[
|
||||||
|
'flask',
|
||||||
|
],
|
||||||
|
extras_require={
|
||||||
|
'test': [
|
||||||
|
'pytest',
|
||||||
|
'coverage',
|
||||||
|
'blinker',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
)
|
||||||
15
examples/javascript/tests/conftest.py
Normal file
15
examples/javascript/tests/conftest.py
Normal file
|
|
@ -0,0 +1,15 @@
|
||||||
|
import pytest
|
||||||
|
|
||||||
|
from js_example import app
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture(name='app')
|
||||||
|
def fixture_app():
|
||||||
|
app.testing = True
|
||||||
|
yield app
|
||||||
|
app.testing = False
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def client(app):
|
||||||
|
return app.test_client()
|
||||||
28
examples/javascript/tests/test_js_example.py
Normal file
28
examples/javascript/tests/test_js_example.py
Normal file
|
|
@ -0,0 +1,28 @@
|
||||||
|
import pytest
|
||||||
|
|
||||||
|
from flask import template_rendered
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize(('path', 'template_name'), (
|
||||||
|
('/', 'plain.html'),
|
||||||
|
('/plain', 'plain.html'),
|
||||||
|
('/fetch', 'fetch.html'),
|
||||||
|
('/jquery', 'jquery.html'),
|
||||||
|
))
|
||||||
|
def test_index(app, client, path, template_name):
|
||||||
|
def check(sender, template, context):
|
||||||
|
assert template.name == template_name
|
||||||
|
|
||||||
|
with template_rendered.connected_to(check, app):
|
||||||
|
client.get(path)
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize(('a', 'b', 'result'), (
|
||||||
|
(2, 3, 5),
|
||||||
|
(2.5, 3, 5.5),
|
||||||
|
(2, None, 2),
|
||||||
|
(2, 'b', 2),
|
||||||
|
))
|
||||||
|
def test_add(client, a, b, result):
|
||||||
|
response = client.post('/add', data={'a': a, 'b': b})
|
||||||
|
assert response.get_json()['result'] == result
|
||||||
|
|
@ -65,12 +65,11 @@ Test
|
||||||
|
|
||||||
::
|
::
|
||||||
|
|
||||||
pip install pytest
|
pip install '.[test]'
|
||||||
pytest
|
pytest
|
||||||
|
|
||||||
Run with coverage report::
|
Run with coverage report::
|
||||||
|
|
||||||
pip install pytest coverage
|
|
||||||
coverage run -m pytest
|
coverage run -m pytest
|
||||||
coverage report
|
coverage report
|
||||||
coverage html # open htmlcov/index.html in a browser
|
coverage html # open htmlcov/index.html in a browser
|
||||||
|
|
|
||||||
|
|
@ -56,7 +56,7 @@ def register():
|
||||||
elif db.execute(
|
elif db.execute(
|
||||||
'SELECT id FROM user WHERE username = ?', (username,)
|
'SELECT id FROM user WHERE username = ?', (username,)
|
||||||
).fetchone() is not None:
|
).fetchone() is not None:
|
||||||
error = 'User {} is already registered.'.format(username)
|
error = 'User {0} is already registered.'.format(username)
|
||||||
|
|
||||||
if error is None:
|
if error is None:
|
||||||
# the name is available, store it in the database and go to
|
# the name is available, store it in the database and go to
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@
|
||||||
license_file = LICENSE
|
license_file = LICENSE
|
||||||
|
|
||||||
[bdist_wheel]
|
[bdist_wheel]
|
||||||
universal = False
|
universal = True
|
||||||
|
|
||||||
[tool:pytest]
|
[tool:pytest]
|
||||||
testpaths = tests
|
testpaths = tests
|
||||||
|
|
|
||||||
|
|
@ -20,4 +20,10 @@ setup(
|
||||||
install_requires=[
|
install_requires=[
|
||||||
'flask',
|
'flask',
|
||||||
],
|
],
|
||||||
|
extras_require={
|
||||||
|
'test': [
|
||||||
|
'pytest',
|
||||||
|
'coverage',
|
||||||
|
],
|
||||||
|
},
|
||||||
)
|
)
|
||||||
|
|
|
||||||
3
tox.ini
3
tox.ini
|
|
@ -31,7 +31,8 @@ deps =
|
||||||
|
|
||||||
commands =
|
commands =
|
||||||
# the examples need to be installed to test successfully
|
# the examples need to be installed to test successfully
|
||||||
pip install -e examples/tutorial -q
|
pip install -q -e examples/tutorial[test]
|
||||||
|
pip install -q -e examples/javascript[test]
|
||||||
|
|
||||||
# pytest-cov doesn't seem to play nice with -p
|
# pytest-cov doesn't seem to play nice with -p
|
||||||
coverage run -p -m pytest tests examples
|
coverage run -p -m pytest tests examples
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue