Home » 2021 » Juli

Archiv für den Monat: Juli 2021

Flask DB Migrations

Links

Flask-Migrate ist die Library um DB-Modell-Änderungen, die mit SQLAlchemyausgedrückt sind zu kontrollieren. Es benutzt dazu die Library Alembic.

Schritte

Aufsetzen Flask Migrate Tool

Steps:

pip3 install Flask-Migrate

Projekt bezüglich Migrations-Handling aufsetzen

flask db init

Das setzt verschiedene Migrationscripts-Folders und Tools im SW-Projekt bereit.
Output:

C:\tmp\Python_Workspace1\task-app>flask db init
C:\Users\Lenovo W540\AppData\Local\Packages\PythonSoftwareFoundation.Python.3.7_qbz5n2kfra8p0\LocalCache\local-packages\Python37\site-packages\flask_sqlalchemy\__init__.py:873: FSADeprecationWarning: SQLALCHEMY_TRACK_MODIFICATIONS adds significant overhead and will be disabled by default in the future.  Set it to True or False to suppress this warning.
  'SQLALCHEMY_TRACK_MODIFICATIONS adds significant overhead and '
Creating directory C:\tmp\Python_Workspace1\task-app\migrations ...  done
Creating directory C:\tmp\Python_Workspace1\task-app\migrations\versions ...  done
Generating C:\tmp\Python_Workspace1\task-app\migrations\alembic.ini ...  done
Generating C:\tmp\Python_Workspace1\task-app\migrations\env.py ...  done
Generating C:\tmp\Python_Workspace1\task-app\migrations\README ...  done
Generating C:\tmp\Python_Workspace1\task-app\migrations\script.py.mako ...  done
Please edit configuration/connection/logging settings in 'C:\\tmp\\Python_Workspace1\\task-app\\migrations\\alembic.ini' before proceeding.

Funktioniert das Kommando nicht, weil die DB noch nicht gestartet ist, dann etwa folgendes absetzen:

pg_ctl -D "C:\Program Files\PostgreSQL\13\data" start

DB-Migrationen durchführen

flask db migrate

Erkennt die Anforderungen an das DB-Modell aus den Python-Scripts (aus den SQLAlchemy Model-Objekt-Definitionen) heraus und erstellt eine Migration (DDL Statements um das erforderliche DB-Model zu erstellen).

Bsp. einer SQLAlchemy Model-Objekt-Definition im *.py:

class Todo(db.Model):
    __tablename__ = 'todos'
    id = db.Column(db.Integer, primary_key=True)
    description = db.Column(db.String(), nullable=False)

Falls das erzeugte Script das ganze DB-Modell von Grund auf neu erzeugen soll, dann darf die DB diese Entitäten nicht schon enthalten. Das kann man zum Bsp. erreichen durch:

dropdb --username=postgres todoDB
createdb --username=postgres todoDB

!Stelle sicher, dass das Pyton-Script, nicht schon selbst die DB-Objekte erstellt (Z.B. Kommando db.create_all() darf nicht auch schon im Python-Code enthalten sein).

Durchführung:

C:\tmp\Python_Workspace1\task-app>flask db migrate

INFO  [alembic.runtime.migration] Context impl PostgresqlImpl.
INFO  [alembic.runtime.migration] Will assume transactional DDL.
INFO  [alembic.autogenerate.compare] Detected added table 'todos'
Generating C:\tmp\Python_Workspace1\task-app\migrations\versions\70ee48c313e4_.py ...  done

C:\tmp\Python_Workspace1\task-app>

Ein Migrations-File wurde erstellt: task-app/migrations/versions/70ee48c313e4_.py:

"""empty message

Revision ID: 70ee48c313e4
Revises: 
Create Date: 2021-07-22 15:15:43.733091

"""
from alembic import op
import sqlalchemy as sa


# revision identifiers, used by Alembic.
revision = '70ee48c313e4'
down_revision = None
branch_labels = None
depends_on = None


def upgrade():
    # ### commands auto generated by Alembic - please adjust! ###
    op.create_table('todos',
    sa.Column('id', sa.Integer(), nullable=False),
    sa.Column('description', sa.String(), nullable=False),
    sa.PrimaryKeyConstraint('id')
    )
    # ### end Alembic commands ###


def downgrade():
    # ### commands auto generated by Alembic - please adjust! ###
    op.drop_table('todos')
    # ### end Alembic commands ###

Migration auf die DB spielen:

C:\tmp\Python_Workspace1\task-app>flask db upgrade
INFO  [alembic.runtime.migration] Context impl PostgresqlImpl.
INFO  [alembic.runtime.migration] Will assume transactional DDL.
INFO  [alembic.runtime.migration] Running upgrade  -> 70ee48c313e4, empty message

Migration rückgängig machen wäre:

flask db downgrade

Prüfung, ob DB-Migrationen auf DB angekommen sind:

C:\tmp\Python_Workspace1\task-app>psql --username=postgres todoDB
Passwort für Benutzer postgres:
todoDB=# \dt
              Liste der Relationen
 Schema |      Name       |   Typ   | Eigent³mer
--------+-----------------+---------+------------
 public | alembic_version | Tabelle | postgres
 public | todos           | Tabelle | postgres
(2 Zeilen)


todoDB=#

Beachte die alembic_version Tabelle! Beinhaltet die Migrationsinfo. Prinzipiell nicht durch den Programmierer zu verändern.

Wenn nun Änderungen am DB-Modell innerhalb des Python-Scripts gemacht werden (z.B. wird hier das boolean Feld ‚completed‘ neu hinzu gefügt),

class Todo(db.Model):
    __tablename__ = 'todos'
    id = db.Column(db.Integer, primary_key=True)
    description = db.Column(db.String(), nullable=False)
    completed = db.Column(db.Boolean(), nullable=False, default=False)

…, dann wird mit flask db migrate ein neues Update-Script erstellt und mit flask db update dieser Update auf die DB gespielt.

Beheben der Problematik des Hinzufügens von NOT-NULL Feldern:
Das Migrations-Script folgendermassen anpassen:

from alembic import op

def upgrade():
    # ### commands auto generated by Alembic - please adjust! ###
    op.add_column('todos', sa.Column('completed', sa.Boolean(), nullable=True))
    op.execute('update todos set completed=False where completed is null')
    op.alter_column('todos', 'completed', nullable=False)
    # ### end Alembic commands ###

Web App with Flask (Python) on PostgresSQL

The following is a fully functional Web App implemented in Flask (Python) profiding CRUD operations on a PostgresSQL DB.

Code

templates/index.html:

{% for d in data %} : The HTML-Templating, die benutzt wird um auf Datenbasis (Parameter-Basis) HTML (DOM) zu generieren ist: Jinja

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Tasks</title>
    <style>
        .hidden {
            display : none;
        }
    </style>
</head>
<body>

    <div>This page (together with its back-end) demonstrates a minimal Web App that uses different technologies to query (toDo) items from the backend (and DB):</div>
    <h2>ADD with HTML Form Submit</h2>
    <div>Demonstrates add DB operation by usage of HTML old fashioned form. The new results are displayed by usage of the redirect to the index page (the page at '/') which you typically would open first and that initially queries all toDos.</div><br>
    <div>Insert todo description to add:</div>
    <form action="/todos/create" method="POST">
        <input type="text" name="description" />
        <input type="submit" value="create" />
    </form>
    <ul>
        {% for d in data %}
        <li>{{ d.description }}</li>
        {% endfor %}
    </ul>


    <h2>ADD with Ajax Single Page Version</h2>
    <div>Demonstrates add DB operation by usage of Ajax request. Simulation of single page app: The added entry is added by DOM manipulation.</div><br>
    <div>Insert todo description to add:</div>
    <div id="error" class="hidden">Something went wrong!</div>
    <form id="form2">
        <input type="text" id="description" />
        <input type="submit" value="create" />
    </form>
    <ul id = "todoListByAjax">
        {% for d in data %}
        <li>{{ d.description }}</li>
        {% endfor %}
    </ul>
    <script>
        document.getElementById('form2').onsubmit = function (e) {
            console.log('submit handler for Ajax case executing');
            e.preventDefault();
            fetch('todos/ajaxCreate', {
                method : 'POST',
                body : JSON.stringify({
                    'description' : document.getElementById('description').value
                }),
                headers : {
                    'Content-Type' : 'application/json'
                }
            }).then(function(response){
                return response.json()
            }).then(function (jsonResponse) {
                console.log('HPS received');
                console.log(jsonResponse);
                const liItem = document.createElement('LI');
                liItem.innerHTML = jsonResponse['description'];
                document.getElementById('todoListByAjax').appendChild(liItem);
                document.getElementById('error').classList = 'hidden';
            }).catch(function (reason) {
                document.getElementById('error').classList = '';
            });
        }

        var xhttp = new XMLHttpRequest();

        description = document.getElementById("description").value;

        xhttp.open("GET", "/todos/create?description=" + description);

        xhttp.send();
    </script>

    <h2>Delete all todo items</h2>
    <form action="/todos/deleteAll" method="GET">
        <input type="submit" value="delete all" />
    </form>

    <h2>XMLHttpRequest Version</h2>
    <div>Demonstrates select/filter DB operation by usage of XMLHttpRequest. Simulation of single page app: The found entry is added to the result node by DOM manipulation.</div><br>
    <div>Insert todo description to search for:</div>
    <form id="form3">
        <input type="text" id="description3" />
        <input type="submit" value="search" />
    </form>
    <h5>Result:</h5>
    <div id="responsefield"/>
    <script>
        function load(url, callback) {
            var xhr = new XMLHttpRequest();

            xhr.onreadystatechange = function() {
                if (xhr.readyState === 4) {
                    callback(xhr.response);
                }
            }
            xhr.open('GET', url, true);
            xhr.send('');
        }
        document.getElementById('form3').onsubmit = function (e) {
            console.log('submit handler for XMLHttpRequest case executing');
            const filterVal = document.getElementById('description3').value;
            e.preventDefault();
            load('todos/getWithXmlHttpRequestExample?description=' + filterVal, function(resp){
                const respField = document.getElementById('responsefield');
                respField.innerText = resp;
            })
        }
    </script>

</body>
</html>

app.py:

from flask import Flask, render_template, request, redirect, url_for, jsonify, abort
from flask_sqlalchemy import SQLAlchemy
import sys

app = Flask(__name__) #Tells Flask that this file is the actual app runner
app.config['SQLALCHEMY_DATABASE_URI'] = 'postgresql://postgres:admin@localhost:5432/todoDB'

db = SQLAlchemy(app)

class Todo(db.Model):
    __tablename__ = 'todos'
    id = db.Column(db.Integer, primary_key=True)
    description = db.Column(db.String(), nullable=False)

    def __repr__(self):
        return f'<Todo {self.id} {self.description}>'

#create all defined entities:
db.create_all()

def db_insert(description):
    is_error = False
    try:
        todo_in = Todo(description = description)
        db.session.add(todo_in)
        db.session.commit()
    except:
        is_error = True
        db.session.rollback()
        print(sys.exc_info())
    finally:
        db.session.close()

    return is_error

@app.route('/')
def index():
    return render_template('index.html', data=Todo.query.all())

@app.route("/todos/create", methods=['POST'])
def create_todo():
    description = request.form.get('description', 'Null Default Task')
    is_error = db_insert(description)
    if is_error:
        abort (400)
    else:
        return redirect(url_for('index'))

@app.route("/todos/ajaxCreate", methods=['POST'])
def create_todo_via_ajax():
    print('Method create_todo_via_ajax() start ')
    description = request.get_json()['description']
    is_error = db_insert(description)
    todo_in = Todo(description = description)

    if is_error:
        abort (400)
    else:
        print('Method create_todo_via_ajax(): committed')
        return jsonify({
            'description' : todo_in.description
        })

@app.route("/todos/getWithXmlHttpRequestExample", methods=['GET'])
def get_todo_via_xml_http_request_example():
    data=Todo.query.filter(Todo.description==request.args.get('description')).all()
    try:
        if len(data) > 0:
            print("returning: " + str(data[0].id) + " " + data[0].description)
            return "Found entry with ID: '" + str(data[0].id) + "': '" + data[0].description + "'"
        else:
            return "No data found"
    except:
        return "An error occured"

@app.route("/todos/deleteAll", methods=['GET'])
def remove_all():
   Todo.query.delete()
   db.session.commit()
   return render_template('index.html', data=Todo.query.all())

Project structure:

project_main
   app.py
   templates
      index.html

Starten der Applikation


Auf Windows

set FLASK_APP=.\project_main\app.py
set FLASK_DEBUG=True
flask run

Postgres DB muss aufgesetzt sein (siehe links unten)

Siehe auch

Flask WebApp Request handling

Request-Typen und ihr Handling

Request-TypHandling (Python)
URL parameter:
/foo?field1=value
value1 = request.args.get(field1)
Form inputun = reqest.form.get(‚username‘)
pw = request.form.get(‚password‘)
application/jsondata_string = request.data
data_dictionary = json.loads(data_string)

DB Manipulation with SQLAlchemy

Referenzen:
Docs for the SQLAlchemy Query API
SQLAlchemy Doku

Im folgenden eine Kleine Demo,
– wie bestehende SQLAlchemy-Definitionen eines bestehendes Python-Scripts in die interaktive Python-Konsole importier werden können und
– wie SQLAlchemy-Entitäten SQLAlchemy-artig von der abgefragt und hinzu gefügt werden.

AblaufCommandMeaning
1>pythonStart Python Console
2>from path/flask_postgres_sql_hello_app import db,PersonImportiert die in flask_postgres_sql_hello_app.py
(–> siehe) gemachten Definitionen von db und Person
3ffresults = Person.query.all()Holt alle Person Entitäten von der entsprechenden Tabelle
3ffresults = Person.query.first()
3ffPerson.query.filter_by(name=’Hans‘).all()

#Alternativ:
Person.query.filter(Person.name=’Hans‘).all()

#Alternativ:
db.session.query(Person).filter(Person.name=’Hans‘).all()
Holt Person Entitäten die dem Filter entsprechen von der entsprechenden Tabelle
User.query.filter(User.name.like(‚B%‘)).all()

ilike(‚b%‘)
Like-Query

Like (Case-insensitive)
3ffPerson.query.filter(Person.name==‚Hans‘).filter(Person.name==‚Peter‘).all()Multiple Filters (Method chaining)
3ffquery.count()
query.first()
query.get(9)
query.get(‚ID1‘)
query.limit(2)
query.delete()

query.order_by(db.desc(Person.name))
Wird für Grouping benutzt
Nur erstes Finding bringen
Nur 9tes Finding bringen
Primary-Key filter
Nur die ersten 2
Löschen des Query-Resultats

Order by
Person.query.join(‚vehicles‘).filter(Vehicle.marke==‚Opel‘).all()Join-Beispiel
3ffperson = Person(name=’Bob‘)
db.session.add(person)
db.sesssion.commit()
Erstellen eines Person-Objekts und speichern desselben.
3ffdb.session.add_all([Person(name=’Ada‘), Person(‚Jira‘)])
db.sesssion.commit()
Erstellen mehrerer Person-Objekt und speichern desselben
3ff
3ff
3ff

Erste Flask Python Webapp mit DB zugriff über sqlAlchemy

Referenzen

Overall Example

Python HTTP-Server (Flask) Applikation, die vom Browser aufgerufen mit ‚Hello <name>‘ antwortet, wobei der Name von der Personen-Tabelle der DB gelesen wird.
Flask wird dabei benutzt um den HTTP Server zur Verfügung zu stellen und darin die Web-App auf definierter URL anzubieten.
SQLAlchemy (in der Flask-Version) bietet die APIs um DB Entitäten als Klassen (z.B. ‚Person‘) formulieren zu können diese als Objekte auf die relationale DB zu mappen (ORM provider).

flask-postgres-sql-hello-app.py:

from flask import Flask
from flask_sqlalchemy import SQLAlchemy

app = Flask(__name__) #Create a flask app with the name of this runnable python file (stored in the env var __name__)
app.config['SQLALCHEMY_DATABASE_URI'] = 'postgresql://myDB:pw@localhost:5432/testDB'
db = SQLAlchemy(app)

class Person(db.Model):
  __tablename__ = 'persons'
  id = db.Column(db.Integer, primary_key=True)
  name = db.Column(db.String(), nullable=False)

#create all defined entities:
db.create_all()

#Insert an object to the DB:
person1 = Person(name = 'Marc')
db.session.add(person1)
db.session.commit()

person = Person.query.first()

#Handle web request on the web root:
@app.route('/')
def index():
    return 'Hello ' + person.name

# Wird dieser Code auskommentiert, dann kann diese HTTP-Server-App 
# durch 'python dieses-file-py' gestartet werden.
#if __name__ == '__main__':
#   app.run()

Define an Entity

class Person(db.Model):
  __tablename__ = 'persons'
  id = db.Column(db.Integer, primary_key=True)
  name = db.Column(db.String(), unique=False, nullable=False)
  ahv = db.Column(db.String(), unique=True, nullable=False)

How to run a Flask HTTP Server

Das obige HTTP-Server-Script flask-postgres-sql-hello-app.py wird gestartet mit:

> flask run

nachdem die Umgebungsvariablen FLASK_APP=pfad/flask-postgres-sql-hello-app.py gesetzt ist (Windows).
In Linux wird anscheinend so aufgerufen: >FLASK_APP=pfad/flask-postgres-sql-hello-app.py flask run

Debug-Mode: (Autorerun des HTTP-Server-Scripts)
Env. Variable setzen: FLASK_DEBUG=true

Alternative Start-Methode:
Sieh code auch oben:

# Wird dieser Code auskommentiert, dann kann diese HTTP-Server-App 
# durch 'python dieses-file-py' gestartet werden.
#if __name__ == '__main__':
#   app.run()

Soll die Server-App von aussen Aufrufbar sein:

>flask run --host=0.0.0.0

oder

if __name__ == '__main__':
   app.run(host="0.0.0.0")

Use Python Interactive Console to Import script and query script defined Entities

C:\tmp\Python_Workspace1\src>python

>>> from flask_postgres_sql_hello_app import db,Person
>>> Person.query.all()
[<Person ID: 5, name: Marc>
, <Person ID: 9, name: Marc>
, <Person ID: 10, name: Marc>

Funktioniert nur mit PY-Files deren Namen nach Konvention ist (kein ‚-‚).

Object Lifecycle in SQLAlchemy

The Lifecycle is: –object instantiated–> transient— query add executed–> pending –qery select/filter OR commit exec–> flushed –commit ex.–> committed

Flushed heisst: Im internen (in Memory) ORM-Object-Model-Cache sind die Änderungen bereits geschrieben.

!! ORM Selects (z.B. Person.query.all()) erhält bereits alle Objekte, die oberhalb im Code mit session.query.add(person) hinzugefügt/verändert/gelöscht wurden. –> ORM select-artige Queries löschen Flush aus!
Der SQL Select (direkt auf der DB) zeigt diese aber erst nach einem Commit!

PostgresSQL mit Python (psycopg2)

Setup

Administering PostgresSQL

Using PSQL commandline tool of PgAdmin GUI (included with the Windows installer PostgreSQL Database Download)
Der Pfad muss noch gesetzt werden um es aus dem Dos-Prompt heraus ausführen zu können.
Start/Stop PostgresSQL Server.

!template1 und postgres DBs sind nicht zum Verändern gedacht (ausser man will die DB Vorlage für neue DBs verändern)! Template1 DB ist – wie der Name schon sagt – ein Template für neu zu erstellendes DBs.

Postgres default port: 5432

postgres commands issued at the OS command line:

CommandDescription
sudo -u <username> -ilog in as username. Default installed user is called postgres
createdb <dbname>
dropdb <dbname>
select * from tableA;


PSQL

CommandDescription
psql <dbname> [<username>]Initial login to the db starting the PSQL interactive tool.
\llist all databases on the server, owners, access levels
\c <dbname>connect to a DB
\dtshow DB tables
\d <tablename>describe table
\qquit PSQL tool

Python access to PostgresSQL using psycopg2

import psycopg2

conn = psycopg2.connect('dbname=testDB user=postgres password=*****')
cursor = conn.cursor()

# Open a cursor to perform database operations
cur = conn.cursor()

# drop any existing todos table
cur.execute("DROP TABLE IF EXISTS todos;")

# (re)create the todos table
# (note: triple quotes allow multiline text in python)
cur.execute("""
  CREATE TABLE todos (
    id serial PRIMARY KEY,
    description VARCHAR NOT NULL
  );
""")

todos = ["Abwaschen", "Putzen", "Aufgabenhilfe", "Ferienplanung"]

for tup in enumerate(todos):
    cur.execute('insert into todos (id, description) values (%s, %s)', tup)

# Alternative: Parameter resolution with named parameters by providing an parameter object:
for tup in enumerate(todos, 10):
    cur.execute('insert into todos (id, description) values (%(key)s, %(value)s)', { 'key' : tup[0], 'value' : tup[1] })

cur.execute('select * from todos')
results = cur.fetchall()

print('Read from DB: ')
for res2 in results:
    print(res2)

# commit, so it does the executions on the db and persists in the db
conn.commit()

cur.close()
conn.close()

Python (Merkpunkte für den Java-Gewohnten)

Hierarchy of Online Resources

–> From Udacity training: Hierarchy of Online Resources

Ausführung

Python ist normalerweise im Windows enthalten. Also: Konsole öffnen, dann python eintippen und schon wartet der Python-Interpreter auf deine Kommandos.

Fancy Interpreter: Wenn der Standard Interactive Interpreter nicht genügt, wäre IPython eine konfortablerer Alternative.

Operators

Speziell:

CodeBedeutung
x**2x hoch 2

Variables and Assignment Operators

Variablen müssen nicht mit Typ deklariert werden.

CodeBedeutung
x, y, z = 3, 4, 5Werte werden der Reihe nach den variablen x, y, z zugeordnet.
i = 1Definition eines Int
f = 1.0Definition eines Float
i1 = int(f)Umwandlung Float zu Int
f1 = float(i)Umwandlung Int zu Float
boolNot ‚boolean‘!
type(i)Typenprüfung
print(type(x))Drucken des Typen
> print(.1 + .1 + .1 == .3)
False
Float hat nur diskrete Werte, kann also 0.1 nicht genau abbilden!
not (i1 > i2 or (i3 == i4 and i4 != i5))boolean expression demo 🙂

String

CodeBedeutung
this_string = 'Simon\'s skateboard is in the garage.'
this_string2 = "Simon\'s skateboard is in the garage.
String demo
>>> print("Hello" * 5) HelloHelloHelloHelloHello
>>> print(len("Hanspeter"))
9

„Hanspeter“[2]
–> „n“

print(‚Hanspeter ist so lang: ‚ + str(len(‚Hanspeter‘)))
Hanspeter ist so lang: 9
Int zu String Konvertierung
Nötig bei Print-Ausgabe
>>>my_string = „ab“
>>> my_string.islower()
True
>>> my_string.count('a')
2
>>> my_string.find('a')
3

firstName, lastName = „Max“, „Muster“
„Hello! My name is {} {}„.format(firstName, lastName)

String Interpolation Demo
>new_str = "The cow jumped over the moon." >new_str.split()
['The', 'cow', 'jumped', 'over', 'the', 'moon.']
Split demo.
A list is created.
new_str.split('.')Split with ‚.‘ as delimiter.
>“Nomen est omen“.find(‚om‘)
1
erstes Vorkommen finden
>“Nomen est omen“.rfind(‚om‘)
10
letztes Vorkommen finden
>“Nomen est omen“.count(‚omen‘)
2
Anzahl Vorkommen eruieren

Lists

CodeMeaning
list_of_random_things = [1, 3.4, 'a string', True]Listen könnne heterogenen Inhalt haben!
>>> list_of_random_things[0]
1
>>> list_of_random_things[-1]
True
Letztes Element der Liste selektieren!
>>> list_of_random_things = [1, 3.4, 'a string', True]
>>> list_of_random_things[1:2]
[3.4]
Range einer Liste extrahieren
>>> list_of_random_things[2:]
['a string', True]
End-Range einer Liste extrahieren
>>> list_of_random_things[:2]
[1, 3.4, 'a string']
Anfangs-Range einer Liste extrahieren
>>> 'this' in 'this is a string'
True
>>> 'in' in 'this is a string'
True
>>> 'isa' in 'this is a string'
False
>>> 5 not in [1, 2, 3, 4, 6]
True
>>> 5 in [1, 2, 3, 4, 6]
False
‚in‘ / ’not in‘ demo
len(list) returns how many elements are in a list.
max(list)returns the greatest element of the list. How the greatest element is determined depends on what type objects are in the list. The maximum element in a list of numbers is the largest number. The maximum elements in a list of strings is element that would occur last if the list were sorted alphabetically. This works because the the max function is defined in terms of the greater than comparison operator. The max function is undefined for lists that contain elements from different, incomparable types.
min(list) returns the smallest element in a list. min is the opposite of max, which returns the largest element in a list.
sorted(list)returns a copy of a list in order from smallest to largest, leaving the list unchanged.
„-„.join([„Anna“, „Maria“])
‚Anna-Maria‘
Join List Demo
>>>letters = ['a', 'b', 'c', 'd'] >>>letters.append('z') >>>print(letters)
['a', 'b', 'c', 'd', 'z']
Hinzufügen von objekten in die Liste
list1 = [„Hans“, „Karl“, „Guido“]
for s in enumerate(list1):
… print(s)

(0, ‚Hans‘)
(1, ‚Karl‘)
(2, ‚Guido‘)
Enumerate-Demo
Erstellen eines Iterable über Tupels, wobei das Tupen eine Fortlaufenden Sequenzunummer und den jeweiligen Wert aus der Liste enthält.
cities = [„amsterdam“, „paris“, „berlin“]
capitalizedCities = city.title() for city in cities
List Comprehension Demo
Action Block, Laufvariable, behandelte Liste
even_squares = n**2 for n in range(9) if n**2%2==0List Comprehension Demo mit Condition
nonsensList = n**2 if n%2 == 0 else „-„ for n in range(9)List Comprehension Demo mit Condition and Else value
list(iterator)Liste aus Iterator erstellen

Tuple

CodeMeaning
t1 = („Max“, „Muster“, „Zürich“) Tupel erstellen
t2 = „Max“, „Muster“, „Zürich“Tupel erstellen
vn, nn, ort = t1Tupel auspacken 1
vn = t1[0]Tupel auspacken 2
t1[2] = „Zürich“
Traceback (most recent call last):
File „“, line 1, in
TypeError: ‚tuple‘ object does not support item assignment
Tupel ist immutable?
listOfTupels = zip(listA, listB)Zip Demo
Erstellen eines Iterable über Tupels die jeweils einen Wert aus beiden Ursprungslisten enthalten.
for t in zip([„Karl“, „Lagerfeld“], [„Heidi“, „Klum“]):
… print(t)

(‚Karl‘, ‚Heidi‘)
(‚Lagerfeld‘, ‚Klum‘)
Zip example
Erstellen eines Iterable über Tupels die jeweils einen Wert aus beiden Ursprungslisten enthalten.

Sets

CodeMeaning
set1 = {„Hans“, „Karl“, „Guido“}Set erstellen
>list1 = [„Hans“, „Karl“, „Guido“, „Hans“]
>set2 = set(list1)
>set2
{‚Karl‘, ‚Guido‘, ‚Hans‘}
Set aus Liste erstellen
set2.add(„Hermann“)Hinzufügen

Dictionaries (Maps)

CodeMeaning
elements = {"Karl": 1, "Mark": 2, "John": 6}Set erstellen
print(elements["John"]) Zugriff
elements[„John“] = 77Update
>elements[„Inexisting_Name“]
Traceback (most recent call last):
File „“, line 1, in
KeyError: ‚Inexisting_Name‘
Error bei nicht gefundener Key
Besser:
>v1 = map1.get(‚Inexisting_Name‘)
>print(v1)
None
Oder (mit Default):
v1 = map1.get(‚Inexisting_Name‘, 99)
print(v1)
99
print(map1)
{‚Hans‘: 1, ‚Peter‘: 2}
>"Inexisting_Name" in elements
False
Präsenz-Test
map1[„Hermann“]
Traceback (most recent call last):
File „“, line 1, in
KeyError: ‚Hermann‘

Generators

Generators werden benutzt um einen Iterator zu definieren. Dazu wird eine Funktion programmiert, die yield Statements enthält. Der basierend auf dieser Funktion definierte Iterator durchläuft die Funktion dann bei jedem Next-Aufruf eine yield -Position weiter.

-> Udacity Explaination

Control Flow

CodeMeaning
if a > b:
doSomething()
!Der Block ist limitiert zu beginn durch das ‚:‘ und zum ende durch die Leerzeile!
Zeileneinzug ist obligatorisch!
if a > b:
doSomething()
elif a = b:
doSthOther()
else
for i in array1:
doSomething()
For-Loop-Demo
range(start, end[, stepWidth])
>for(i in range(1, 9)):
> print(i**2)
1
4
9
for k in dictionary: Keys der Map holen
for k,v in dict.items(): Key/Value Items der Map holen
for v in dict.values(9:Values der Map holen
while <condition>:
doSomething()
While-Loop-Demo
try:
# some code
except (ValueError, KeyboardInterrupt): # some code
finally
# some code
Try-Catch:
Except ohne Error-Angabe möglich
Mehrere Except-Blöcke mit unterschiedlichen Prozeduren möglich
try:
# some code
except Exception as e:
print("ZeroDivisionError occurred: {}".format(e))
Error-Message benutzen

Functions

CodeMeaning
>def cyl_vol(height, radius=5):
> return height * PI * radius**2
>
>cyl_vol(10)
785.72425
Function definition mit Defaultwert-Vorgabe
cyl_vol(radius=2, height=10)
Equivalent:
dyl_vol(10, 2)
call with named parameters
def cyl_vol(height, radius=5):
„““Berechnet das Zylinder-Volumen
INPUT:
height: float, höhe des Zylinders
radius: float, radius des Zylinders
OUTPUT:
float, berechnetes Volumen
„““
return height * PI * radius**2
Methoden-Dokumentation
height, radius: height * PI * radius**2Lambda definition
map(function, iterable)Map Definition
cities = [„amsterdam“, „berlin“, „rome“]
capitalized_cities = map(lambda c: c.title(), cities)
Map Demo
filter(filter_fn, iterator)Filter Definition
cities = [„amsterdam“, „berlin“, „rome“]
a_cities = filter(lambda c: c[0]==’a‘, cities)
Filter Demo

Scope:
Globale Variablen können nicht innerhalb einer Funktion verändert werden.

Scripting

CodeMeaning
>python script1.pyPython-Script ausführen
a = input(„Gib eine Zahl:“)
aQuadrat = int(a)**2
Einen wert von der Konsole einlesen
print(eval(„3**3“))
27
Eval-Funktion: Ein String wird als Python-Code interpretiert.

Files I/O

CodeMeaning
f = open('my_path/my_file.txt', 'r') file_data = f.read()
f.close()
Opening and reading a file
with open('my_path/my_file.txt', 'r') as f: file_data = f.read()… alternatively (including the close op)
f = open('my_path/my_file.txt', 'w') f.write("Hello there!") f.close()Write to a file (erases the preliminarlily existin content!)
# Open a file with access mode ‚a‘
file_object = open(’sample.txt‘, ‚a‘)

# Append ‚hello‘ at the end of file
file_object.write(‚hello‘)

# Close the file
file_object.close()
Append to an existing file
camelot_lines = []
with open("camelot.txt") as f:
for line in f:
camelot_lines.append(line.strip())
print(camelot_lines)
Read file, line by line
f = open('my_path/my_file.txt', 'r') file_data = f.readLine()
f.close()
.. alternatively

Imports/Libraries/Modules

CodeLabel
#This is a library file
def doSth():
# some code

def main():
# Code that runs only if this file is called directly by
# >python libraryfile.py
# –> see below how / when this function is called!

#Trick to evaluate if this file is called directly by
# >python libraryfile.py and only then call main()
if __name__ == '__main__':
main()
Library definition
(Trick to code main part there that is only called when it runs as root)
import useful_functions as uf

mean = uf.mean(scores)
Import of library in Main file

‚as uf‘ is optional
import random

print(random.randint(1,10))
Import der standard Random library
from ranwom import randint as rdInt
import doSth from sillylib
Similar as in Java:
import static com.lib.Myclass.doSth;
from mylib import *Do not use this!
import os.path
os.path.isdir(„mypath“)
Importing a Submodule or a Module
from datetime import datetimeImports a class from a module (both having the same name)
>python
>>>import myscript as my
>>>from myother import MyEntityClass, db
MyEntityClass.query.all()
Imports können selbstvertändlich auch innerhalb der python interaktiven Konsole gemacht werden. Damit ein eigenes File importiert werden kann, muss dessen Mame Python-Kompatibel sein (z.B. keine ‚-‚)
Import ohne „.py“-Endung!
Python Standard Libraries
vs.
3rd Pary Libraries
Usefull 3rd-Party Libraries
PipStandard Phython Package Manager
AnnacondaPython Package Manger specifically designed for Data Science
pip install package_namePackage ‚package_name‘ installieren
Danach kann es genau wie Python Standard Libs im Code benutzt werden.
requirements.txt:
meineLibrary==1.2.5
andereLibrary==9.30.3

>pip install -r requirements.txt
Definition aller Dependencies in einem Requirments-File und installieren derselben via PIP.

Node.js und NPM

Eine wunderbare Einführung in Javascript (nur kurz), Node.js und NPM: https://medium.com/jspoint/introduction-to-node-js-a-beginners-guide-to-node-js-and-npm-eca9c408f9fe

JS code auf Node.js laufen lassen:

  1. >node
  2. >>console.log(‘Hello World!’)
    //Hello World!

Oder:

>node meinJSscript.js

HTTP Server mit Node.js laufen lassen

https://medium.com/jspoint/introduction-to-node-js-a-beginners-guide-to-node-js-and-npm-eca9c408f9fe

NPM: Projektsetup und

Im Projekt-Basis-Verzeichnis npm init aufrufen.
Dadurch wird ein package.json file angelegt (analog pom.xml bei Maven)

Eine dependency des Projekts beschaffen: Im Projekt-Basis-Verzeichnis
npm install –-save gewünschte-library aufrufen (z.B. npm install --save lodash). Dabei wird (sofern noch nicht existierend) ein node_modules Verzeichnis angelegt, darin die Dependencygespeichert und die Dependency im package.json und im package-lock.json registriert.
package.json  und package-lock.json sollen im SW repo gespeichert, node_modules jedoch ignoriert werden.
Beim Setup des SW-Projekts wird dann jeweils npm install aufgerufen, was NPM dazu veranlasst alle Dependencies gemäss package-lock.json zu installieren.

Web Programming – Dom-Manipulation – Lernjournal

Im Broser: Developer Console öffnen: Ctrl + Shift + I
$0: Ist das Object, das zur Zeit in im Fokus steht. Abfragen wie z.B.: $0.children möglich.
Etwas loggen:
>console.log($0.id)

Zugriff auf Elemente: Z.B.:
>document.body

Mozilla Developer Network: https://developer.mozilla.org/en-US/docs/Web/API#interfaces

Wichtige Interfaces:
Document
Node
Element