Skip to Content
Technical Articles

Serving Fiori Applications with Python Flask, part 1

Many of us may know Python as a programming language with data science libraries such as Tensorflow for building Deep Neural Networks, Pandas for data preparation and agile analytics, or Jupyter for putting it all together in a digital notebook. Some of us may have already coded several microservices for SAP Data Hub using Python operators within a pipeline. But did you know that Python can serve Fiori Applications?

Python Flask is a lightweight web application framework which supports RESTful API and MVC (Model, View, Controller) paradigms required by many projects where scalable integration is required. With a few adjustments to existing Fiori templates, you can build applications locally and deploy globally. Although the Flask web server is not production quality, when it is placed within an Apache framework then it can provide a robust platform as seen in use today with massive digital first companies like Netflix, Lyft, and Reddit. In addition it is also used in SAP Extended Services Advanced (XSA) and SAP Cloud Platform (SCP).

Why would you be interested in this series?

  • You are struggling to locally develop Fiori apps
  • You don’t have connections to ABAP or HANA
  • You want to try something new and unopinionated.
  • You really like SAP Data Hub and want to see what else Python can do

What you should know to complete this series?

  • Basic Python, Javascript, HTML/XML
  • Familiarity with Fiori resources, specifically
  • Access to Github to access sample code.

In this first of a multi-part series, we will focus on the basic project structure and changes to Fiori application files required to serve a basic template application. By the last part in the series, we will show how SAP HANA, OrientDB, and innovative use cases are integrated into a fully productive web application.

The sample application contains a simple launchpage with tile:

…and Master-Detail app with dummy date:

DISCLAIMER: This is not a guide on how to code Fiori applications nor is it an end-to-end tutorial on setting up web applications with Python Flask. There are plenty of resources that cover this far better than I can and I am happy to point any interested parties to them. To run the application and code, installing Python and Flask is required. This is rather an integration topic with pinpoint solutions to issues that I have not found elsewhere.

What is Flask?

Flask is a BSD licensed Python minimalistic web application framework based on Werkzeug and Jinja 2. A Flask project can consist of a single python file that initializes the app server and contains routes, models, and any supporting logic as shown in the Flask open source documentation. However, this is not scalable for production and therefore we break these functions into 3 different files.

from flaskr import app

if __name__ == "__main__":

At the application parent directory is which imports the app class from the Flask resources directory (flaskr) and runs default on port 5000. We set the debug configuration to true so that the server resets when changes are made to the code base. Other variables to serve on HTTP or HTTPS are available but won’t be covered at this stage.

from flask import Flask

app = Flask(__name__)

import flaskr.routes

Within flaskr we have which initializes the application and additionally makes the flaskr directory into a Python module which can be imported into other files. Also at this level is which provides the interface between the Fiori application and any back end resources we may have in the future such as SAP HANA or OrientDB.

from flask import render_template, jsonify
import time
from datetime import datetime
from . import app

def index():

    return render_template("index.html")

@app.route('/DashboardAnalytics', methods=["GET"])
def DashboardAnalytics():
    Return a simple odata container with date time information
    odata = {
        'd': {
            'results': []
    i = 0
    names = 'abcdefghijklmnopqrxtu'
    while i < 20:
            "id": i,
            "name": names[i],
            "datetime": datetime.fromtimestamp(time.time()).strftime('%Y-%m-%d %H:%M:%S'),

    return jsonify(odata)

That’s it for the python required to run this demo and in less than 50 lines which includes a small function to generate dummy data in JSON format thanks to Flask’s internal “jsonify” function. To start the application, navigate to the parent directory containing and execute it with Python as seen below. If you have all dependencies installed you should see the server running on the local host.

> python

In summary we have the following structure with 3 files distributed in 2 folders.

  • FioriFlask\ (Execution to start serving the application on port 5000)
  • FioriFlask\flaskr\ (resources for Flask)
    • (initialize the app and directory as a python module)
    • (interface between python back end services and javascript based front end)

A Fiori project on the other hand requires several folders and hierarchies to manage user interactions. We won’t be exhausting the functionality at each level but instead focus on what needs to be changed from the traditional Fiori project structure for serving with Flask. In this case it is a single step that requires understanding 2 additional folders in our Flask project structure, templates and static.

  • FioriFlask\flaskr\templates\index.html
  • FioriFlask\flaskr\static\(all other Fiori Files)

The application will serve HTML files from the file and Flask’s  render_template function which points at the templates folder by default. Therefore, we need to move the index.html file into the templates folder. This is the typical case for single-page apps not only common in Fiori’s framework but also seen in other popular examples like React and Angular. In any index file case we modify it with pointers to the rest of the application content including controllers, views, fragments, css, and other customizing files. For Fiori this means changing only one line using Jinja.

Flask uses a templating engine called Jinja which allows Python logic to be implemented directly in static web files like HTML and is what we will be using to create the pointers within the index file. A Fiori index file is the container for the application content and is initialized with javascript that sets the content source, theme, resource roots and other options such as dynamic loading. The key line to change here is the value for data-sap-ui-resourceroots.

<!DOCTYPE html>
	<meta charset="utf-8">
	<meta name="viewport" content="width=device-width, initial-scale=1.0">
	<title>Basic Template</title>

	<script id="sap-ui-bootstrap"
			"sap.ui.demo.basicTemplate": "{{ url_for('static', filename='.')}}"}'

<body class="sapUiBody">
	<div data-sap-ui-component data-name="sap.ui.demo.basicTemplate" data-id="container" data-settings='{"id" : "basicTemplate"}'></div>

This line can contain a json format to identify several roots. However, for this example we want to ensure that there is a value identifying the namespace of the application and that it is set to the application’s static folder using Jinja syntax, specifically url_for and setting it to the static directory. You will notice the Jinja specific syntax through the double curly braces {{ }}. In our case this will be:

"sap.ui.demo.basicTemplate": "{{ url_for('static', filename='.')}}"}'

This will ensure that when the application server runs, it will set up its static directory so that all Fiori resources are accessible and integrated. That’s it for the initial setup and the only line of code necessary to ensure the application can run. The remaining files can be dropped directly as is into the static folder.

The next step is to ensure the application can make calls to the back end. For this we will use the press function of the tile within the App.controller.js. The press function takes the name of the tile pressed and uses that as the url to call to the back end. In the file we have defined an endpoint to match the name of that tile, DashboardAnalytics and it returns a simple JSON with some values to show it populated the application.

], function(Controller, formatter, History) {
	"use strict";

	return Controller.extend("sap.ui.demo.basicTemplate.controller.App", {

		formatter: formatter,

		onInit: function () {

		    self = this;

		press: function(tile) {

			var selectedData = {};;
			this.getData(tile).done(function(result) {
				var oModel = new sap.ui.model.json.JSONModel(result.d);
				sap.ui.getCore().setModel(oModel, "DashboardAnalyticsModel");

			}).fail(function(result) {

		onNavBack: function() {

			var sPreviousHash = History.getInstance().getPreviousHash();

			if (sPreviousHash !== undefined) {
			} else {
				this.getRouter().navTo("home", {}, true);


		getData: function(url){
			return jQuery.ajax({
				url: url,
				type: "GET"

		getRouter : function () {
			return sap.ui.core.UIComponent.getRouterFor(this);

		routeToApp: function(tile) {
			self.getRouter().navTo(tile, {});


Instead of returning the index file as before with render_template, we just want to update the application without an entire re-load. Fiori has JQuery and Ajax built in to ensure we can do this, and Flask provides the pre-packaged function called, jsonify mentioned earlier that transforms Python dictionaries into a JSON that are then updated into the Fiori models. Therefore, once the button is pressed, it makes a GET call to the python routes and receives a JSON to populate the UI elements.

		<App id="app" class="appBackground">
				<!--<Page title="{i18n>title}">-->
				<!--	<content></content>-->

In Fiori and MVC in general, you will have separate controllers for each view. Above is the simple App view which is a container for all pages including the launchpad view below.

<core:View controllerName="sap.ui.demo.basicTemplate.controller.App"
	<m:Page class="firstPage" title="Launchpad">
    	<m:customHeader class="NavToolbar">
    	    <m:Bar class="audienceSensorHeader">



    				<m:Text id="titleText" class="titleTextHeader" text="Launchpad"/>




		    <m:HBox class="mainPage" renderType="Bare">
				<ObjectPageLayout id="ObjectPageLayout">
						<ObjectPageSection id="section1" title="Section 1">
								<ObjectPageSubSection id="OpsSection1" title="Operations">
										<core:Fragment fragmentName="sap.ui.demo.basicTemplate.view.Fragments.Operations" type="XML"/>


Here we see an example with the FlexibleColumnLayout which provides 3 main frames for navigation, header, and objective detail and its controller below. Together they form the basis for the Master-Detail Fiori pattern for apps.

<mvc:View id="Main" controllerName="sap.ui.demo.basicTemplate.controller.FlexibleColumnLayout"
    <m:Page class="secondPage" title="{i18n>title}">
        <m:customHeader class="NavToolbar">
            <core:Fragment fragmentName="sap.ui.demo.basicTemplate.view.Fragments.Header" type="XML"/>
                <FlexibleColumnLayout id="fcl" initialMidColumnPage="start" layout="TwoColumnsMidExpanded">
                        <m:Page showHeader="false">
                            	<core:Fragment fragmentName="sap.ui.demo.basicTemplate.view.Fragments.Master" type="XML"/>
                        <m:Page id="start" showHeader="false">
                                <core:Fragment fragmentName="sap.ui.demo.basicTemplate.view.Fragments.Details" type="XML"/>
                        <m:Page id="charts" showHeader="false">
                               <m:Text text="Two" />
], function(AppController, Filter, FilterOperator, JSONModel, MessageToast) {
	"use strict";

	var self;
	return AppController.extend("sap.ui.demo.basicTemplate.controller.FlexibleColumnLayout", {

		onInit: function() {

			this.oModelSettings = new JSONModel({
				maxIterations: 200,
				maxTime: 500,
				initialTemperature: 200,
				coolDownStep: 1
			this.getView().setModel(this.oModelSettings, "settings");
			this.getView().setModel(sap.ui.getCore().getModel("DashboardAnalyticsModel"), "DashboardAnalyticsModel");

Fiori apps are highly configurable and scalable due their implementation through small files that are called into the mobile stack as needed. An example of this is the concept of fragments which are individual pieces of a page that can be re-used across the application. Below are the fragments required to render the basic objects in the Master-Detail app pattern.

	<f:DynamicPage id="dynamicPageId" preserveHeaderStateOnScroll="true" headerExpanded="{/headerExpanded}">
		<!-- DynamicPage Title -->
					<Title text="Entity name: {SELECTED_ATTACHMENT>/pname}"/>
					<Label text="Age: {SELECTED_ATTACHMENT>/age}"/>

		<!-- DynamicPage Header -->
			<f:DynamicPageHeader pinnable="true">
					<HBox alignItems="Start" justifyContent="SpaceBetween">
					<!--<layout:HorizontalLayout allowWrapping="true" >-->
						<layout:VerticalLayout class="sapUiMediumMarginEnd">
							<ObjectAttribute title="Name" text="{DashboardAnalyticsModel>/results/0/name}"/>
							<ObjectAttribute title="Date" text="{DashboardAnalyticsModel>/results/0/datetime}"/>

				<core:Fragment fragmentName="sap.ui.demo.basicTemplate.view.Fragments.TabbedControl" type="XML" />
	<f:DynamicPage toggleHeaderOnTitleClick="false" id="masterListAttachments">
		<!-- DynamicPage Title -->
					<Title text="User profiles ({= ${DashboardAnalyticsModel>/results}.length })"/>
		<!-- DynamicPage Content -->
				<SearchField liveChange="onSearchChange" class="searchBar sapUiTinyMarginTop sapUiSmallMarginBottom" width="90%" search="onSearch" />
				<HBox alignItems="Start" justifyContent="End">
					<Button press="onSort" icon="sap-icon://sort"/>

					<CustomListItem type="Active">
						<HBox alignItems="Start" justifyContent="SpaceBetween">
								<VBox class="sapUiTinyMarginTop sapUiTinyMarginBottom">

									<Label text="Name:" />
									<Text class="titleText" text="{DashboardAnalyticsModel>name}" />

									<Label class="sapUiTinyMarginTop" text="Date:" />
									<Text text="{DashboardAnalyticsModel>datetime}" />


            <Button class="sapUiMediumMarginBegin sapUiMediumMarginEnd" icon="sap-icon://nav-back" press="onNavBack" />

And even the individual tile rows.

	<VBox renderType="Bare">
		<HBox alignItems="Start" justifyContent="SpaceBetween" wrap="Wrap">
			<VBox wrap="Wrap" >
				<HBox wrap="Wrap" width="100%">
					<VBox class="sapUiSmallMarginEnd">
							<GenericTile class="sapUiTinyMarginBegin sapUiTinyMarginTop tileLayout" header="Dashboards" subheader="Analytics"
									<NumericContent value="1" icon="sap-icon://multiple-line-chart" />

Hopefully this has provided you with an overview of how to serve a simple Fiori application from your local client and update data models with calls to the Flask back end. In the next part of this series we will aim at productizing the base using Docker and other Flask tools so we can add more components down the road. For example, we will add OrientDB, an Open Source multi-modal database that provides a Document Store, Graph Engine and full text indexing with Lucene.


You must be Logged on to comment or reply to a post.
  • Hi Lynn,

    Thanks for sharing this interesting article.
    Unfortunately I do not seem to get access to your code in github.
    Is there any other way to get the sample code ?
    I would love to see how you get Flask working with UI5.



  • Hi Srdjan,

    Thanks for the Links! I did not refer to a technical restriction but rather one of personal resources. Not everyone knows ABAP or HANA. I was new to SAP with no technical skill and no access to ABAP or HANA so learned Python since it's free.

    But of course if you have ABAP and HANA, then nothing to stop you from still playing with Flask there too.

  • Great article, and a great starting point.  I've extended your example to use an oData back end provisioned by PySlet (and SQLLITE) which is the traditional oData v2 standard and I ran into the usual CORS issue which I have fixed using the proxy method.  Rather than a single tile I have used multiple tiles within panels calling a generic tile function.  I'm currently struggling with annotations and updates.



  • Hi Justin,

    That's awesome! If you want to share a link to it, please do. Otherwise would be great to catch up and see what can be done together!

  • Hi Lynn,

    I just copy pasted your project(download from git) into a new Python project, it ran into multiple errors.



    Please let me know if I have followed correct procedure.

    Please let me know in which perspective I have to copy the python project.

    Also, please help in understanding on how to deploy the python project into SAP system to run as SAPUI5/Fiori Application.


    Thanks& Regards,



    • Hi Lynn,

      I got the answer for the error.


      I have to inform the flask - to use 'Static' folder as template in file

      app = Flask(__name__,template_folder='static')

      With, this I got the output.


      I am still looking for the information on how to deploy the python project into SAP system to run as SAPUI5/Fiori Application. Please help me on this / guide me to relevant study links.


      Thanks and Regards,



  • Hi Krishna,

    Sorry for the late reply to your post and glad that you were able to solve this on your own. Well done :).

    In terms of serving this as part of a Fiori Application, we will have to set up your Flask service behind an HTTP server from which you can set up a destination in SCP Cockpit. The image below shows the desinations tab in SCP cockpit and two services which you'll see in the NeoApp json file...

    As shown here...


    And then in the actual code making a call to the service here...