Merger

Einige Jahre nach dem erfolgreichen Outsourcing des Literaturnetzes haben sich die Rahmenbedingungen geändert und der Betrieb einer eigenen Django-Umgebung für literarische Texte ist unwirtschaftlich geworden (was ausdrücklich nicht als Ausdruck der Geringschätzung von Kunst und Kultur verstanden werden sollte).

Im Gegensatz zur relativ einfachen Separation ist die Zusammenführung der Daten auf Grund unterschiedlicher Tabellenstrukturen aber nicht ganz einfach, und sowohl Djangos Serialisierungsfunktionen als auch die Nutzung des Django ORM scheitern an der Verwendung zweier Django-Umgebungen in einem Skript. Auf Datenbankebene lassen sich die Tabellen authors und pages durch Ergänzung von models.py und anschließende DB-Migrationen vorbereiten, und der Transfer der Autorinnen funktioniert mit pg_dump (auch wenn der django_content_type author durch die Migrationen nicht erzeugt, sondern manuell nachgetragen werden muss). Die Tabelle pages enthält aber einen selbstreferentiellen Fremdschlüssel (mother_id), so dass die Datensätze wie folgt transferiert werden müssen:

#! /usr/local/bin/python3 import psycopg2 con_source = psycopg2.connect(database="db1", user="user", password="pw") con_target = psycopg2.connect(database="db2", user="user", password="pw") print("Databases opened successfully") def move_page(id, target_mother): cursor_source = con_source.cursor() cursor_source.execute(f'SELECT title, asin, content, author_id, mod_date, position, expansion, docbook, epub, url_name from pages WHERE id = {id}') print(f'selecting page {id} from literaturnetz.pages...') current_page = cursor_source.fetchone() # adding the target mother and the standard page type to the db values... current_page = current_page + (target_mother, 'P', False, False, False, '') cursor_target = con_target.cursor() print(f'inserting page {id} into mysites.pages...') sql_string = "INSERT INTO pages (title, asin, content, author_id, create_date, position, expansion, docbookcontent, epubcontent, url_name, mother_id, page_type, texcontent, pdfcontent, private, short_name) VALUES (%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s) RETURNING id;" cursor_target.execute(sql_string, current_page) new_id = cursor_target.fetchone()[0] print(f'new id for current page is {new_id}...') print(f'selecting children of page {id} from literaturnetz.pages...') cursor_source.execute(f'SELECT id from pages WHERE mother_id = {id}') rows = cursor_source.fetchall() if rows: for row in rows: move_page(row[0], new_id) move_page(11976, 1) con_target.commit() print("Operation completed successfully") con_source.close() con_target.close()

Die ePub- und DocBook-Versionen einiger Seiten müssen manuell übertragen werden. Die Kinder einer einzigen Seite sorgen mit inkonsistenten Werten im Feld position für Fehlermeldungen (MultipleObjectsReturned für die Geschwister-Funktion), aber im Wesentlichen fügen sich die neuen Inhalte gut ein. Die Erzeugung statischer Seiten funktioniert ebenfalls auf Anhieb, allerdings werden bestimmte Seiten während des Abgleichs mit bereits vorhandenen HTML-Dateien –

changed = False if os.path.exists(filename): existing_file = io.open(filename, encoding='utf-8') existing_content = existing_file.read() existing_file.close() if existing_content != content: changed = True

– bei jedem Durchlauf als geändert markiert. Dieses Problem wird durch special characters verursacht, die offensichtlich beim Lesen der UTF-8-Datei und der Generierung des neuen Inhalts unterschiedlich interpretiert werden. Ich hatte vergeblich gehofft, mich nach der flächendeckenden Durchsetzung von UTF-8 nicht mehr mit Textkodierungen befassen zu müssen, aber auch in der Django-Weboberfläche, in BBEdit und in vim bleiben die problematischen Zeichen ungreifbar. Erst die Django-Shell enthüllt ihren wahren Charakter als Wagenrücklauf und Tabulator (\r und \t) und ermöglicht die systematische Entfernung (page.content = page.content.replace('\\r', '')).

Innerhalb der Django-App wird die Logik zur Handhabung unterschiedlicher Autorinnen übernommen (mit umfangreichen Änderungen an models.py, views.py, forms.py, admin.py, Templates und Filtern), und die urheberrechtskonforme Zugriffsbeschränkung für unlängst verstorbene Autorinnen wird statisch auf der Datenbankebene (UPDATE pages SET private = True WHERE pages.id IN (SELECT pages.id FROM pages INNER JOIN authors ON (pages.author_id = authors.id) WHERE authors.death > '1950-12-31');) implementiert.

Schließlich kann ich nginx.conf guten Gewissens ergänzen:

server { listen 80; server_name literaturnetz.org www.literaturnetz.org; location = / { return 301 https://eden.one/literatur; } location / { return 301 https://eden.one$request_uri; } }