I've been testing whether routes exist using
def test_index(self):
r = self.app.get("/")
self.assertEqual(200, r.status_code, "Status code was not 'OK'.")
My template has a hyperlink to another page. Is there a way to test if this exists?
Well, if you are testing templates, every template you render is the result of a request to some route. If you render url's in a template using url_for(), then it will raise a BuildError if the url is pointing to a non existing route, and the server will return the status code 500. Therefore, you don't need to parse your templates manually for testing purposes if you just check the route instead.
Example:
from flask import Flask, render_template_string
app = Flask(__name__)
#app.route('/index')
def index():
return render_template_string("""
{{ url_for('index') }}
{{ url_for('blabla') }}
""")
def test_index(self):
r = self.app.get("/index")
self.assertEqual(200, r.status_code, "Status code was not 'OK'.")
This will result in a
routing.BuildError: Could not build url for endpoint 'blabla'. Did you mean 'static' instead? error, which makes your tests fail.
I hope this explanation is clear enough!
Related
I want to obtain csrf-token provided by flask-wtf and assign it to a variable for future use with my flask app architecture. There's a way to either render it with hidden field {{ form.csrf_token }} or via csrf_token() for jinja. But docs do not mention how to achieve, say, the following:
from flask import Flask, jsonify
from flask_wtf.csrf import CSRFProtect
app = Flask(__name__)
app.secret_key = 'secret'
csrf = CSRFProtect(app)
#app.route('/api/get/endpoint_which_returns_json/', methods=['GET'])
def api():
csrf_token = # ??? what belongs here ???
return jsonify({'token': csrf_token})
if __name__ == '__main__':
app.run(debug=True)
How to achieve that? Thank you in advance.
When you use {{ form.csrf_token }} or {{ csrf_token() }} in your templates through Flask-WTF, they are just calling the flask_wtf.csrf.generate_csrf() function. You can import that and call it yourself to generate a token inside your view:
from flask_wtf.csrf import generate_csrf
#app.route('/', methods=['GET'])
def api():
return jsonify({'token': generate_csrf()})
I'm assuming the idea here is that the api client would be responsible for returning the token with POST requests to your other api resources? If you are just generating the token server-side so as to be able to validate forms that would otherwise require it, you may as well just disable CSRF protection.
I have a python-flask app. And my source.py :
from flask import Flask, flash, redirect, render_template, request, session, abort
import os
from Modules.registry_bend.DockerImageReceiver import http_requester_v2_catalog, read_configurations
app = Flask(__name__)
#app.route('/v1')
def display_index():
return render_template('index.html')
if __name__ == "__main__":
# http_requester_v2_catalog("192.168.1.7", 5000)
app.secret_key = os.urandom(12)
app.run(debug=True, host='0.0.0.0', port=3150)
I run this source.py, and then open the browser and hit localhost:5000/v1.Then index.html appears. So, the challenge is, that a few seconds later I get some data, and I want to add them to index.html. How could it be possible? I have already called index.html once.
You can send the dynamic data to your HTML by sending context variables through the render template method.
flask.render_template(template_name_or_list, **context)
Renders a template from the template folder with the given context.
Parameters:
template_name_or_list – the name of the template to be rendered, or an iterable with template names the first one existing will be rendered
context – the variables that should be available in the context of the template.
Example -
return render_template('index.html', variable1=random.random(), variable2=random.random())
And in your HTML code you need to include these flask variables.
Example -
<p> {{variable1}} </p>
<p> {{variable2}} </p>
And whenever you refresh you html page in your browser. The new data will be displayed.
So I am serving a index.html file from Flask.
The index.html file comes from a built project using: https://github.com/facebookincubator/create-react-app
I have a couple routes setup in the React app, for example:
"/users"
and "/contracts"
When I refresh one of these routes, I get a 404 from flask, but while "clicking" on links found on the site, they work perfectly fine.
When you are clicking the links in the interface, React is re-rendering the page without any server-side intervention, and then updating the route in the URL bar. When loading however, you are making that route request to the server direct, and Flask does not have that route registered.
Simplest way is to register these routes in the decorator for the function serving your homepage view
#app.route('/')
#app.route('/users')
#app.route('/contracts')
def home_page():
If there are many many routes, you may want to use the catch-all URL pattern.
Specifying every route is too prone to error. Here is a more general solution.
Add an error handler to catch 404:
#app.errorhandler(404)
def handle_404(e):
if request.method == 'GET':
return redirect(f'/?request_path={quote_plus(request.path)}')
return e
The error handler will redirect to the home page, where your React works, passing the actual requested request_path as a parameter.
In your view handler do this:
#app.route('/')
def public_page_index(request_path=None):
return render_template('index.html',
request_path=request.args.get('request_path', ''))
The index.html template will create a hidden input:
<input type="hidden" name="request_path" value="{{request_path}}">
Then the actual React path will be available for your React code to respond to. This is what I've done in my Home page component, using jquery and useHistory().
useEffect(() => {
// find if server refresh needs history
const $request_path = $('input[name=request_path]');
const val = $request_path.val();
if (val) {
$request_path.val("");
history.push(val);
return;
}
}, []);
in Flask Framework, define a route
#main.route('/')
def index():
return render_template('index.html')
only can you use get method to request the index.html.
if I try to post some data to the index.html.
I got a method not allow webpage return. and that's correct.
but my problem is, Is there anyway I can customize the page by myself?
for example, return json data, instead of a method not allowed webpage??
You can create error handler
from flask import jsonify
#app.errorhandler(405)
def method_not_allowed(e):
return jsonify({'error': 405}), 405
http://flask.pocoo.org/docs/0.10/patterns/errorpages/
I have a flask route search that serves json search results and an index one that serves a page to search from, simplified as
#app.route('/search')
def search():
res = querydb(request.args)
return jsonify(res)
#app.route('/index')
def index():
return render_template('index.html')
In index.html, I have search forms linked to angular variables, and a button that queries /search using angular's $http and url params from the search forms.
I would like the ability to additionally fetch initial results based on the url (for example, let the url /index?color=red load the /index page and fetch results from /search?color=red on load).
To do this, I'm redefinig the jinja template tags as <%blah%> (to not interfere with angular's), and rendering the page with render_template('index.html', color='red'), with a snippet in the html like
<div ng-controller="MainCtrl" ng-init="fetch('<%color%>')"> </div>
There mustbe a better way to send the params from flask to angular (trying $routeParams or $location.search() doesn't seem to work with flask, returning empty objects). Or should I be composing the views differently somehow?
You should handle your page routes from angular and your API endpoints from flask. So your flask file might look like:
#app.route('/api/search')
def search():
res = querydb(request.args)
return jsonify(res)
#app.route('/')
def index():
return render_template('index.html')
Angular App
var app = angular.module('myApp', ['ngRoute']);
app.config(function($routeProvider) {
$routeProvider
.when('/index', {
templateUrl: 'mainView.html'
controller: 'MainCtrl'
});
});
controller
var MainCtrl = function($location, $http, $scope) {
//Now you can access the search params
var searchParams = $location.search();
$http.get('/api/search?color=' + searchParams.color).success(function(data) {
$scope.results = data;
});
}
Now if you go to http://somedomain/#/index?color=red it should fetch the initial results. Note that Angular handels the part of the url after the /#. Also you'll need to include the angular-route.js file to get routing working in angular.
When you're using angular routing your index.html file will just have all the boiler plate stuff that you want to include in each view and the layout for your page will go into mainView.html. index.html will need to have <div ng-view></div> somewhere in the body to tell angular where to inject mainView.html