Flask_Grundlagen

ehemalige Veranstaltungen ia3.netz + ia3.data sowie ia4.Netz im Studiengang Interaktive Medien

(c) 2020 Hochschule Augsburg - Fakultät für Informatik - Prof.Dr.Nik Klever

Flask - Grundlagen und Einführung

Flask Übersicht

Flask ist ein Microframework für Python. Es ist auf der WSGI Utility Bibliothek Werkzeug sowie auf dem Template-Modul Jinja 2 aufgebaut.

"Micro" meint damit nicht, dass die gesamte Webanwendung in eine einzige Python-Datei eingepasst werden muss, obwohl dies durchaus möglich ist. Aber es meint auch nicht, dass Flask weniger an Funktionalität besitzt als normale Webframeworks. "Micro" in Microframework bedeutet, dass Flask im Kernbereich einfach aufgebaut ist aber die Möglichkeit hat diesen Kern zu erweitern.

Installation

Flask wird mit dem

PIP - Python Package Installer

über den Aufruf in einem Terminalfenster

pip install Flask

installiert.

Flask "Hello World"

In [1]:
%%writefile flask_beispiel_1.py
from flask import Flask
app = Flask(__name__)

@app.route("/")
def hello():
    return "Hello World!"

if __name__ == "__main__":
    app.run()
Writing flask_beispiel_1.py
In [3]:
!python flask_beispiel_1.py
 * Running on http://127.0.0.1:5000/ (Press CTRL+C to quit)
127.0.0.1 - - [03/Dec/2015 22:05:16] "GET / HTTP/1.1" 200 -
^C

Erklärung

  • app ist die Variable, in der alle Objekte der Anwendung gespeichert sind.
  • @app.route("/") ist ein Decorator, der einen HTTP-Request auf "/" mit der nachfolgenden Funktion beantwortet
  • def hello(): ist eine Funktion, die einen HTTP-Response zurückliefert
  • app.run() startet die Flask-Anwendung als Webserver und arbeitet die Anwendung für jeden eingehenden HTTP-Request ab

Decorator

Einen Decorator kann man sich als einen Wrapper oder ein Macro vorstellen, der als Argument die nachfolgende Funktion verwendet und damit diese Funktion verändert aufrufen kann. Eine ausführliche Beschreibung dazu ist in dem Blog von Simeon Franklin oder auch in dem Blog von Bruce Eckel enthalten.

HTTP Telnet Client zum Flask Server

In [4]:
from telnetlib import Telnet

connection = Telnet('127.0.0.1',5000)
connection.write(b'GET / HTTP/1.1\r\n')
connection.write(b'Host: 127.0.0.1\r\n')
connection.write(b'\r\n')
response = connection.read_all()
print response
HTTP/1.0 200 OK
Content-Type: text/html; charset=utf-8
Content-Length: 12
Server: Werkzeug/0.10.4 Python/2.7.10
Date: Thu, 03 Dec 2015 21:05:57 GMT

Hello World!

Chance im Team

Erstellen sie auf der Basis des obigen "Hello World"-Beispiels einen einfachen Webserver, der eine HTML-Webseite zurückliefert.

In [5]:
%%writefile flask_beispiel_2.py
from flask import Flask
app = Flask(__name__)

@app.route("/")
def index():
    return """<html>
   <head>
   <title>index</title>
   </head>
   <body>
   <h2>Dies ist die erste Seite</h2>
</body>
</html>
"""

if __name__ == "__main__":
    app.run()
Writing flask_beispiel_2.py
In [3]:
!python flask_beispiel_2.py
 * Running on http://127.0.0.1:5000/ (Press CTRL+C to quit)
127.0.0.1 - - [03/Dec/2015 22:05:16] "GET / HTTP/1.1" 200 -
^C

Chance im Team

Erstellen sie einen einfachen Webserver, der zwei HTML-Webseiten zurückliefert, eine als Root- oder Index.html und eine zweite als Antwort auf eine Anfrage auf eine hello.html Webseite.

In [9]:
%%writefile flask_beispiel_3.py

# -*- coding: utf-8 -*-

from flask import Flask
app = Flask(__name__)
app.debug = False

@app.route("/")
def index():
    return """<html>
   <head>
   <title>Erste Seite</title>
   </head>
   <body>
   <h2>Dies ist die erste Seite</h2>
   <a href="zwei">Zur zweiten Seite</a>
   </body>
</html>
"""

@app.route("/zwei")
def zwei():
    return """<html>
   <head>
   <title>Zweite Seite</title>
   </head>
   <body>
   <h2>Dies ist die zweite Seite</h2>
   <a href="/">Zurück zur ersten Seite</a>
   </body>
</html>
"""

if __name__ == "__main__":
    app.run()
Overwriting flask_beispiel_3.py
In [3]:
!python flask_beispiel_3.py
 * Running on http://127.0.0.1:5000/ (Press CTRL+C to quit)
127.0.0.1 - - [03/Dec/2015 22:05:16] "GET / HTTP/1.1" 200 -
^C

Dokumentation

Sehen sie sich auch die Dokumentation von Flask an.

In [4]:
from IPython.display import IFrame
IFrame('http://flask.pocoo.org/docs/0.10/quickstart/#debug-mode', width='100%', height=350)
Out[4]:

Tutorial

statt der Datenbank-Anbindung Verwendung einer CSV- oder XML-Datei

In [5]:
from IPython.display import IFrame
IFrame('http://flask.pocoo.org/docs/0.10/tutorial', width='100%', height=350)
Out[5]:

Flask - Objekte

Quelle: Flask Quickstart

Flask Request Objekt

Das Request Objekt ist detailliert in der Flask API beschrieben.

Die folgenden Methoden sind die wichtigsten:

  • method: die Request Methode
  • form: ein Dictionary mit dem Inhalt eines HTML-Formulars (POST-Methode)
  • args: ein Dictionary mit dem Inhalt des Query-Strings (GET-Methode)
  • environ: die komplette WSGI Umgebung
  • path: s.u.
  • script_root: s.u.
  • url: s.u.
  • base_url: s.u.
  • url_root: s.u.

Wenn eine Anwendung auf die folgende URL hört:

  • http://www.example.com/myapplication

und ein Benutzer den Request absetzt:

  • http://www.example.com/myapplication/page.html?x=y

dann sind die obigen Pfad-Variablen folgendermaßen besetzt:

Variable Wert
path /page.html
script_root /myapplication
base_url http://www.example.com/myapplication/page.html
url http://www.example.com/myapplication/page.html?x=y
url_root http://www.example.com/myapplication/

Der folgende Code stellt einen Auszug aus einer entsprechenden Anwendung dar:

In [10]:
%%writefile flask_beispiel_4.py
from flask import request, Flask, render_template_string

login_template = """  <h2>Login</h2>
  {% if error %}<p class=error><strong>Error:</strong> {{ error }}{% endif %}
  <form action="{{ url_for('login') }}" method=post>
    <dl>
      <dt>Username:
      <dd><input type=text name=username>
      <dt>Password:
      <dd><input type=password name=password>
      <dd><input type=submit value=Login>
    </dl>
  </form>
"""

helloworld_template = """  <h2>Hello {{user}}</h2>
  {% if error %}<p class=error><strong>Error:</strong> {{ error }}{% endif %}
"""

def valid_login(user,passwd):
    if user.startswith('a'): return True
    else: return False

def log_the_user_in(user):
    return render_template_string(helloworld_template, user=user)


app = Flask(__name__)
app.debug = True

@app.route('/login', methods=['POST', 'GET'])
def login():
    error = None
    if request.method == 'POST':
        if valid_login(request.form['username'],
                       request.form['password']):
            return log_the_user_in(request.form['username'])
        else:
            error = 'Invalid username/password'
    # the code below is executed if the request method
    # was GET or the credentials were invalid
    return render_template_string(login_template, error=error)

if __name__ == "__main__":
    app.run()
Writing flask_beispiel_4.py
In [12]:
!python flask_beispiel_4.py
 * Running on http://127.0.0.1:5000/ (Press CTRL+C to quit)
 * Restarting with stat
127.0.0.1 - - [03/Dec/2015 22:38:06] "POST /login HTTP/1.1" 200 -
^C

Flask Session Objekt

Neben dem Request Objekt gibt es ein zweites Objekt namens session, welches für die Speicherung von Informationen zu einem Benutzer von einem Request zum nächsten verwendet werden kann. Dies wird mittels kryptographisch verschlüsselten Cookies durchgeführt, d.h. der Benutzer kann zwar die Cookies ansehen, aber ohne die entsprechenden geheimen Schlüssel nicht verändern.

In [14]:
%%writefile flask_beispiel_5.py
from flask import Flask, session, redirect, url_for, escape, request

app = Flask(__name__)
app.debug = True

@app.route('/')
def index():
    if 'username' in session:
        return 'Logged in as %s' % escape(session['username'])
    return 'You are not logged in'

@app.route('/login', methods=['GET', 'POST'])
def login():
    if request.method == 'POST':
        session['username'] = request.form['username']
        return redirect(url_for('index'))
    return '''
        <form action="" method="post">
            <p><input type=text name=username>
            <p><input type=submit value=Login>
        </form>
    '''

@app.route('/logout')
def logout():
    # remove the username from the session if it's there
    session.pop('username', None)
    return redirect(url_for('index'))

# set the secret key.  keep this really secret:
app.secret_key = 'A0Zr98j/3yX R~XHH!jmN]LWX/,?RT'

if __name__ == "__main__":
    app.run()
Overwriting flask_beispiel_5.py
In [16]:
!python flask_beispiel_5.py
 * Running on http://127.0.0.1:5000/ (Press CTRL+C to quit)
 * Restarting with stat
127.0.0.1 - - [03/Dec/2015 22:42:37] "GET / HTTP/1.1" 200 -
127.0.0.1 - - [03/Dec/2015 22:42:41] "GET /login HTTP/1.1" 200 -
127.0.0.1 - - [03/Dec/2015 22:42:55] "POST /login HTTP/1.1" 302 -
127.0.0.1 - - [03/Dec/2015 22:42:55] "GET / HTTP/1.1" 200 -
127.0.0.1 - - [03/Dec/2015 22:43:02] "GET /logout HTTP/1.1" 302 -
127.0.0.1 - - [03/Dec/2015 22:43:02] "GET / HTTP/1.1" 200 -
^C

Flask Response Objekt

Der Rückgabewert einer Anwendungsfunktion wird automatisch in ein Response Objekt umgewandelt. Falls der Rückgabewert eine Zeichenkette ist wird automatisch ein Response Objekt mit dem Response Status Code 200 OK erstellt und ein content-type Headerfeld text/html mimetype hinzugefügt. Die Logik von Flask bei der Erstellung des Response Objekts lautet folgendermaßen:

  • Falls ein Response Objekt eines korrekten Typs zurückgegeben wird, wird es direkt weitergeleitet
  • Falls der Rückgabewert eine Zeichenkette ist, wird ein Response Objekt mit diesem Text als Body erstellt und mit den Default Parametern versehen
  • Falls ein Tupel der Form (response, status, headers) zurückgeliefert wird, überschreiben die entsprechend vorhandenen Parameter in dem Tupel die Default Parameter. Headers kann eine Liste oder ein Dictionary mit zusätzlichen Header Feldern sein
  • Falls keine der obigen Fälle zutrifft, nimmt Flask an, dass der Rückgabewert eine gültige WSGI Anwendung ist und wandelt diese in ein entsprechendes Response Objekt um

Um das resultierende Response Objekt in der Anwendungsfunktion noch vor der Auslieferung zu verändern kann die make_response() Funktion verwendet werden, wie folgendes Beispiel zeigt:

In [9]:
@app.errorhandler(404)
def not_found(error):
    resp = make_response(render_template('error.html'), 404)
    resp.headers['X-Something'] = 'A value'
    return resp

Flask 'g' - Globale Objekte in Anwendungen

Um Daten in einem Request in mehreren Funktionen gemeinsam nutzen zu können ist eine globale Variable in einer thread-orientierten Umgebung nicht geeignet, da diese in einem anderen Thread verändert werden könnte. In Flask gibt es daher ein spezielles Objekt g, welches nur für einen Request seine Gültigkeit besitzt und somit für unterschiedliche Requests auch unterschiedliche Werte zurückliefert. Dieses Objekt ist seit Flask 0.10 an eine Anwendung geknüpft und damit im Application-Context gespeichert.

g ist damit ein Container, in dem alle möglichen Daten und Objekte abgespeichert werden können, z.B. eine Datenbankverbindung oder der gerade eingeloggte Benutzer.

In [10]:
from flask import Flask

app = Flask('Test')
ctx = app.app_context()
ctx.g.user = 'nik'

user = getattr(ctx.g, 'user', None)
user = ctx.g.get('user', None)
print(user)
print('user' in ctx.g)
nik
True

Aktives Plenum

Erstellen sie eine Flask Anwendung, die das PTFBestellformular von oben ausliefert und ihnen die Eingaben als Ergebnis wieder ausliefert

PTFBestellformular.html

In [18]:
%%writefile PTFBestellformular.html
<html>
<head>
  <title>PTF Bestellformular</title>
</head>
<body>
  <h1>PTF Bestellformular</h1>
  <form ACTION="/formular" method="GET">
    <h3>Kunde</h3>
      <p>Name <input name="Kunde" size="46"/></p>
      <p>Strasse <input name="Strasse" size="40"/></p>
      <p>Ort <input name="Ort" size="20"> Plz <input name="Plz" size="4"></p>
    <h3>Kreditkarte</h3> 
      <p>Mastercard <input name="Kreditkarte" type="radio" value="Mastercard"/>
         Visacard <input name="Kreditkarte" type="radio" value="Visacard"/></p>
      <p>Nummer <input name="Kreditkartennummer" size="10"/>
         Ablaufdatum <input name="Ablaufdatum" size="4"/></p>
    <h3>PTF Bestellgr&ouml;&szlig;e</h3>
      <p>Gro&szlig; <input name="Groesse" type="radio" value="gross"/>
         Mittel <input name="Groesse" type="radio" value="mittel"/>
         Klein <input name="Groesse" type="radio" value="klein"/></p>
    <h3>Zustellung</h3>
      <p>Expresszustellung <input name="Express" type="checkbox"/></p>
    <p><input type="submit" value="Bestellung abschicken"/></p>
  </form>
  <p>Vielen Dank f&uuml;r ihre Bestellung!</p>
</body>
</html>
Writing PTFBestellformular.html

PTFBestellformular.py

In [17]:
%%writefile flask_beispiel_6.py
from flask import Flask, request

app = Flask(__name__)
app.debug = False

def leseDatei(dateiname):
    try:
        datei = open(dateiname,"r")
        gesamterText = datei.read()
        datei.close()
        return gesamterText
    except IOError:
        return "Die Datei {} wurde nicht gefunden".format(dateiname)

@app.route('/')
def index():
    return leseDatei('PTFBestellformular.html')

@app.route('/formular', methods=['GET', 'POST'])
def formularausfuehren():
    if request.method == 'GET':
        html = """<!doctype html>
<html>
  <head>
  </head>
  <body>
    <ul>"""
        for i in request.args: 
            html = html + '      <li>'+i+': '+request.args[i]+'</li>'
        html = html + """
    </ul>
  </body>
</html>"""
        return html
    
app.run()
Writing flask_beispiel_6.py
In [3]:
!python flask_beispiel_6.py
 * Running on http://127.0.0.1:5000/ (Press CTRL+C to quit)
127.0.0.1 - - [03/Dec/2015 22:05:16] "GET / HTTP/1.1" 200 -
^C

Flask Templates - Jinja

Quelle: Jinja

MVC Architektur

In der schon im allgemeinen Teil von Webframeworks angesprochen ist in der Model - View - Controller (MVC) Architektur

die View in der Regel durch HTML Templates realisiert.

HTML Templates

HTML Template Dateien sind HTML Dateien, in denen über ein Template Markup Code eingebaut werden kann.

Das untenstehende Beispiel ist aus dem Flask Quickstart Beispiel entnommen:

In [19]:
%%writefile flask_beispiel_7.py
from flask import render_template

@app.route('/hello/')
@app.route('/hello/<name>')
def hello(name=None):
    return render_template('hello.html', name=name)
Writing flask_beispiel_7.py
In [3]:
!python flask_beispiel_7.py
 * Running on http://127.0.0.1:5000/ (Press CTRL+C to quit)
127.0.0.1 - - [03/Dec/2015 22:05:16] "GET / HTTP/1.1" 200 -
^C

Flask schaut nach Template Dateien in dem templates Ordner. Wenn die entsprechende Anwendung ein Modul ist (eine einzige Python-Datei), dann ist der Ordner auf der Ebene dieses Moduls, wenn die Anwendung ein Paket ist, dann ist der Ordner auf der Ebene des Pakets enthalten:

  • Modul:

      /application.py
      /templates
          /hello.html
  • Paket:

      /application
          /__init__.py
          /templates
              /hello.html
In [21]:
%%writefile templates/hello.html
<!doctype html>
<title>Hello from Flask</title>
{% if name %}
  <h1>Hello {{ name }}!</h1>
{% else %}
  <h1>Hello World!</h1>
{% endif %}
Writing templates/hello.html

Obige Aufgabe jetzt mit Templates

In [22]:
%%writefile flask_beispiel_8.py
from flask import Flask, request, render_template

app = Flask(__name__)
app.debug = False

@app.route('/')
def index():
    return render_template('PTFBestellformular.html')

@app.route('/formular', methods=['GET', 'POST'])
def formularausfuehren():
    if request.method == 'GET':
        return render_template('requestTemplate.html')
    
app.run()
Writing flask_beispiel_8.py
In [23]:
%%writefile templates/requestTemplate.html
<!doctype html>
<html>
  <head>
  </head>
  <body>
    <ul>
        {% for i in request.args %} 
        <li>{{ i }}:{{ request.args[i] }}</li>
        {% endfor %}
    </ul>
  </body>
</html>
Writing templates/requestTemplate.html
In [3]:
!python flask_beispiel_8.py
 * Running on http://127.0.0.1:5000/ (Press CTRL+C to quit)
127.0.0.1 - - [03/Dec/2015 22:05:16] "GET / HTTP/1.1" 200 -
^C

Weiteres einfaches Beispiel

Dieses Beispiel ist - unabhängig von Flask - angepasst an das Beispiel aus der Startseite von Jinja 2:

In [14]:
from jinja2 import Template

users = [
dict(username='Klever',url='nik.klever@hs-augsburg.de'),
dict(username='Kowarschick',url='wolfgang.kowarschick@hs-augsburg.de'),
dict(username='Kipp',url='michael.kipp@hs-augsburg.de'),
dict(username='Rist',url='thomas.rist@hs-augsburg.de'),
]
    
templateForm = """
<title>{% block title %}{% endblock %}</title>
<ul>
{% for user in users %}
  <li><a href="{{ user['url'] }}">{{ user['username'] }}</a></li>
{% endfor %}
</ul>
"""
    
template = Template(templateForm)
rs=template.render(users=users)
print(rs)
<title></title>
<ul>

  <li><a href="nik.klever@hs-augsburg.de">Klever</a></li>

  <li><a href="wolfgang.kowarschick@hs-augsburg.de">Kowarschick</a></li>

  <li><a href="michael.kipp@hs-augsburg.de">Kipp</a></li>

  <li><a href="thomas.rist@hs-augsburg.de">Rist</a></li>

</ul>

Jinja High-Level API

In der Jinja High-Level API stehen in der Klasse Environment die Default-Werte für das Markup, u.a. die wichtigsten:

  • block_start_string: The string marking the beginning of a block. Defaults to '{%'.
  • block_end_string: The string marking the end of a block. Defaults to '%}'.
  • variable_start_string: The string marking the beginning of a print statement. Defaults to '{{'.
  • variable_end_string: The string marking the end of a print statement. Defaults to '}}'.
  • comment_start_string: The string marking the beginning of a comment. Defaults to '{#'.
  • comment_end_string: The string marking the end of a comment. Defaults to '#}'.

Template Design

Ein Template ist einfach eine Textdatei. Dieses kann jegliches text-basierte Format (HTML, XML, CSV, LaTeX, etc.) erzeugen. Es gibt keine spezifisches Dateiextension für Templates, .html oder .xml ist völlig ok.

Die wichtigsten Template Design Komponenten sind im Folgenden aufgeführt. Die komplette API ist unter der Template Designer Documentation zu finden.

Variable

{{ foo.bar }}  
{{ foo['bar'] }}

Filter

{{ name|striptags|title }}  
{{ list|join(', ') }}
{{ key|escape }} oder {{ key|e }}

Liste der eingebauten Filter

Tests

{% if loop.index is divisibleby 3 %}  
{% if loop.index is divisibleby(3) %}

Kommentare

{# ... #}

Template Vererbung

mit den {% block %}, {% endblock %} und {% extends %} Tags sowie dem {{ super() }} Block

Basis Template

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN">
<html lang="en">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
    {% block head %}
    <link rel="stylesheet" href="style.css" />
    <title>{% block title %}{% endblock %} - My Webpage</title>
    {% endblock %}
</head>
<body>
    <div id="content">{% block content %}{% endblock %}</div>
    <div id="footer">
        {% block footer %}
        © Copyright 2008 by <a href="http://domain.invalid/">you</a>.
        {% endblock %}
    </div>
</body>

Kind Template

{% extends "base.html" %}
{% block title %}Index{% endblock %}
{% block head %}
    {{ super() }}
    <style type="text/css">
        .important { color: #336699; }
    </style>
{% endblock %}
{% block content %}
    <h1>Index</h1>
    <p class="important">
      Welcome on my awesome homepage.
    </p>
{% endblock %}

Kontrollstrukturen

For
<dl>
{% for key, value in my_dict.iteritems() %}
    <dt>{{ key|e }}</dt>
    <dd>{{ value|e }}</dd>
{% endfor %}
</dl>

mit dem loop Objekt sind zusätzliche Methoden verwendbar:

{% for row in rows %}
    <li class="{{ loop.cycle('odd', 'even') }}">{{ loop.index }}. Zeile = {{ row }}</li>
{% endfor %}
If
{% if kenny.sick %}
    Kenny is sick.
{% elif kenny.dead %}
    You killed Kenny!  You bastard!!!
{% else %}
    Kenny looks okay --- so far
{% endif %}
Assignment
{% set navigation = [('index.html', 'Index'), ('about.html', 'About')] %}
{% set key, value = call_something() %}
Include
{% include 'header.html' %}
    Body
{% include 'footer.html' %}

Jinja in Flask

Innerhalb der Templates hat man Zugriff auf die Flask Objekte request, session und g sowie auf die Funktion get_flashed_messages().

Templates sind dann ausgesprochen sinnvoll, wenn Templates vererbt werden, d.h. bestimmte Elemente einer Webseite immer wieder auf allen Webseiten gleich verwendet werden, z.B. Header, Footer, Navigationsmenü.

Markup in Flask

In Templates ist die automatische Escape-Funktionalität eingestellt, d.h. wenn der Dateityp HTML ist, wird automatisch jeglicher verwendete Inhalt escaped. Kann einer Variablen vertraut werden und in dieser wird sicheres HTML verwendet, dann kann die Variable mit der Verwendung der Klasse Markup als sicher markiert werden.

Folgende Beispiele zeigen die Verwendung der Klasse Markup:

In [15]:
from flask import Markup
Markup('<strong>Hello %s!</strong>') % '<blink>hacker</blink>'
Out[15]:
Markup(u'<strong>Hello &lt;blink&gt;hacker&lt;/blink&gt;!</strong>')
In [16]:
Markup.escape('<blink>hacker</blink>')
Out[16]:
Markup(u'&lt;blink&gt;hacker&lt;/blink&gt;')
In [17]:
Markup('<em>Marked up</em> &raquo; HTML').striptags()
Out[17]:
u'Marked up \xbb HTML'

Aktives Plenum

Erstellen sie eine Flask Anwendung, die ein Formular ausliefert, über die sie eine Datei hochladen können und anschliessend diese Datei Zeile für Zeile wieder ausliefern, sodass jede Zeile einmal mit der CSS-Klasse odd und einmal mit even versehen wird. Verwenden sie als Datei eine CSV Datei.

Hinweis:
Beachten sie das Quickstart Beispiel zu File Uploads

CSVUpload.py

In [3]:
%%writefile flask_beispiel_9.py
# -*- coding: utf-8 -*-
from flask import Flask, request, render_template

app = Flask(__name__)
app.debug = True

@app.route('/')
def index():
    return render_template('CSVUpload.html')

@app.route('/upload', methods=['GET', 'POST'])
def upload_file():
    if request.method == 'POST':
        f = request.files['upload']
        dateiSpeicher = f.stream.read().decode('utf-8')
        dateiZeilen = dateiSpeicher.split('\n')[:-1]
        return render_template('uploadfile.html', dateiZeilen = dateiZeilen, dateiName = f.filename)
    
app.run()
Overwriting flask_beispiel_9.py

CSVUpload.html

In [25]:
%%writefile templates/CSVUpload.html
<html>
  <head>
    <title>CSV Upload</title>
  </head>
  <body>
    <h1>CSV Upload</h1>
    <form ACTION="/upload" method="POST" enctype ="multipart/form-data">
      <p><input type="file" name="upload"/></p>
      <p><input type="submit" value="Datei hochladen"/></p>
    </form>
    <p>Vielen Dank!</p>
  </body>
</html>
Writing templates/CSVUpload.html

uploadfile.html

In [26]:
%%writefile templates/uploadfile.html
<html>
<head>
  <title>CSV Datei</title>
  <style type="text/css">
.even { background-color:#ffffff; }
.odd { background-color:#e0e0e0; }
  </style>
</head>
<body>
  <h1>CSV Datei "{{ dateiName }}"</h1>
    <ul>
{% for dateiZeile in dateiZeilen %}
      <li class =  "{{loop.cycle('odd','even')}}"> {{ loop.index}}. Zeile = "{{ dateiZeile }}" </li>
{% endfor %}
    </ul>
</body>
</html>
Writing templates/uploadfile.html