prepared transition to new table "Ausgabe": modified DB schema; added ORM model, Flask views, HTML templates, nav link

This commit is contained in:
eclipse 2025-08-17 22:27:05 +02:00
parent fe054b3460
commit 56520fc72d
7 changed files with 534 additions and 125 deletions

View File

@ -2,7 +2,7 @@ from flask import Flask
import dotenv # this import is not strictly necessary but it forces pipreqs-to include dotenv when generating `requirements.txt`
from the_works.database import init_db
from the_works.models import SIMPLE_MODELS
from the_works.views import home, text, werk, reihe, veroeffentlichung, titelbild
from the_works.views import home, reihe, titelbild, text, veroeffentlichung, werk, ausgabe
from the_works.views.simple_view import VIEWS, ViewAll, ViewCreate, ViewUpdate, ViewDelete
@ -49,10 +49,11 @@ def create_app(config=None):
# register blueprints for remaining views
app.register_blueprint(home.bp)
app.register_blueprint(reihe.bp)
app.register_blueprint(text.bp)
app.register_blueprint(werk.bp)
app.register_blueprint(veroeffentlichung.bp)
app.register_blueprint(titelbild.bp)
app.register_blueprint(text.bp)
app.register_blueprint(veroeffentlichung.bp)
app.register_blueprint(werk.bp)
app.register_blueprint(ausgabe.bp)
# register helper function
app.jinja_env.globals.update(sizeof_fmt=sizeof_fmt)

View File

@ -61,7 +61,8 @@ class Pseudonym(Base):
ID: Mapped[int] = mapped_column(primary_key=True)
Pseudonym: Mapped[str] = mapped_column(CheckConstraint('Pseudonym <> ""', name='PseudonymNotEmptyConstraint'), nullable=False, unique=True)
veroeffentlichung: Mapped[List['Veroeffentlichung']] = relationship(back_populates='pseudonym')
veroeffentlichung: Mapped[List['Veroeffentlichung']] = relationship(back_populates='pseudonym') #TODO DELETE
werk: Mapped[List['Werk']] = relationship(back_populates='pseudonym')
class Sprache(Base):
@ -88,8 +89,9 @@ class Verlag(Base):
ID: Mapped[int] = mapped_column(primary_key=True)
Verlag: Mapped[str] = mapped_column(CheckConstraint('Verlag <> ""', name='VerlagNotEmptyConstraint'), nullable=False, unique=True)
ausgabe: Mapped[List['Ausgabe']] = relationship(back_populates='verlag')
reihe: Mapped[List['Reihe']] = relationship(back_populates='verlag')
werk: Mapped[List['Werk']] = relationship(back_populates='verlag')
werk: Mapped[List['Werk']] = relationship(back_populates='verlag') #TODO DELETE
class Werksform(Base):
@ -98,10 +100,11 @@ class Werksform(Base):
ID: Mapped[int] = mapped_column(primary_key=True)
Werksform: Mapped[str] = mapped_column(CheckConstraint('Werksform <> ""', name='WerksformNotEmptyConstraint'), nullable=False, unique=True)
werk: Mapped[List['Werk']] = relationship(back_populates='werksform')
ausgabe: Mapped[List['Ausgabe']] = relationship(back_populates='werksform')
werk: Mapped[List['Werk']] = relationship(back_populates='werksform') #TODO DELETE
# Classes that have more than one column and need dedicated view functions
# Classes that have more than one column and need their own view functions
class Reihe(Base):
__tablename__ = 'Reihe'
@ -129,7 +132,9 @@ class Titelbild(Base):
Thumbnail: Mapped[bytes] = mapped_column(nullable=False)
sha256: Mapped[str] = mapped_column(CheckConstraint('sha256 <> ""', name='sha256NotEmptyConstraint'), nullable=False, unique=True)
werk: Mapped[List['Werk']] = relationship(back_populates='titelbild')
# one-to-many
ausgabe: Mapped[List['Ausgabe']] = relationship(back_populates='titelbild')
werk: Mapped[List['Werk']] = relationship(back_populates='titelbild') #DELETE
def asdict_with_urls(self):
tb = self.asdict()
@ -162,42 +167,6 @@ class Text(Base):
genre_ids: AssociationProxy[List["Genre"]] = association_proxy("genres", "Genre", creator=lambda genre_id: Text_Genre(Genre=genre_id))
class Werk(Base):
__tablename__ = 'Werk'
# regular columns
ID: Mapped[int] = mapped_column(primary_key=True)
Titel: Mapped[str] = mapped_column(CheckConstraint('Titel <> ""', name='WerkstitelNotEmptyConstraint'), nullable=False)
Untertitel: Mapped[Optional[str]]
Reihennummer: Mapped[Optional[str]]
Erscheinungsdatum: Mapped[Optional[str]]
ISBN_13: Mapped[Optional[str]]
ISBN_10: Mapped[Optional[str]]
ISSN: Mapped[Optional[str]]
Preis: Mapped[Optional[str]]
Klappentext: Mapped[Optional[str]]
Anmerkungen: Mapped[Optional[str]]
# many-to-one
Reihe: Mapped[Optional[int]] = mapped_column(ForeignKey('Reihe.ID'))
reihe: Mapped[Optional['Reihe']] = relationship(back_populates='werk')
Titelbild: Mapped[Optional[int]] = mapped_column(ForeignKey('Titelbild.ID'))
titelbild: Mapped[Optional['Titelbild']] = relationship(back_populates='werk')
Verlag: Mapped[Optional[int]] = mapped_column(ForeignKey('Verlag.ID'))
verlag: Mapped[Optional['Verlag']] = relationship(back_populates='werk')
Werksform: Mapped[int] = mapped_column(ForeignKey('Werksform.ID'), nullable=False)
werksform: Mapped['Werksform'] = relationship(back_populates='werk')
# one-to-many
veroeffentlichung: Mapped[List['Veroeffentlichung']] = relationship(back_populates='werk')
# many-to-many
genres: Mapped[List['Werk_Genre']] = relationship(back_populates='werk', cascade='all, delete-orphan')
genre_ids: AssociationProxy[List["Genre"]] = association_proxy("genres", "Genre", creator=lambda genre_id: Werk_Genre(Genre=genre_id))
herausgeber: Mapped[List['Werk_Herausgeber']] = relationship(back_populates='werk', cascade="all, delete-orphan")
herausgeber_ids: AssociationProxy[List['Herausgeber']] = association_proxy("herausgeber", "Herausgeber", creator=lambda hrsg_id: Werk_Herausgeber(Herausgeber=hrsg_id))
class Veroeffentlichung(Base):
__tablename__ = 'Veroeffentlichung'
@ -207,14 +176,77 @@ class Veroeffentlichung(Base):
AltUntertitel: Mapped[Optional[str]]
# many-to-one
Pseudonym: Mapped[int] = mapped_column(ForeignKey('Pseudonym.ID'), nullable=False)
pseudonym: Mapped['Pseudonym'] = relationship(back_populates='veroeffentlichung')
Pseudonym: Mapped[int] = mapped_column(ForeignKey('Pseudonym.ID'), nullable=False) #TODO DELETE
pseudonym: Mapped['Pseudonym'] = relationship(back_populates='veroeffentlichung') #TODO DELETE
Text: Mapped[int] = mapped_column(ForeignKey('Text.ID'), nullable=False)
text: Mapped['Text'] = relationship(back_populates='veroeffentlichung')
Werk: Mapped[int] = mapped_column(ForeignKey('Werk.ID'), nullable=False)
werk: Mapped['Werk'] = relationship(back_populates='veroeffentlichung')
class Werk(Base):
__tablename__ = 'Werk'
# regular columns
ID: Mapped[int] = mapped_column(primary_key=True)
Titel: Mapped[str] = mapped_column(CheckConstraint('Titel <> ""', name='WerkstitelNotEmptyConstraint'), nullable=False)
Untertitel: Mapped[Optional[str]]
Reihennummer: Mapped[Optional[str]]
Erscheinungsdatum: Mapped[Optional[str]] #TODO DELETE
ISBN_13: Mapped[Optional[str]] #TODO DELETE
ISBN_10: Mapped[Optional[str]] #TODO DELETE
ISSN: Mapped[Optional[str]] #TODO DELETE
Preis: Mapped[Optional[str]] #TODO DELETE
Klappentext: Mapped[Optional[str]] #TODO DELETE
Anmerkungen: Mapped[Optional[str]] #TODO DELETE
# many-to-one
Pseudonym: Mapped[int] = mapped_column(ForeignKey('Pseudonym.ID'))
pseudonym: Mapped['Pseudonym'] = relationship(back_populates='werk')
Reihe: Mapped[Optional[int]] = mapped_column(ForeignKey('Reihe.ID'))
reihe: Mapped[Optional['Reihe']] = relationship(back_populates='werk')
Titelbild: Mapped[Optional[int]] = mapped_column(ForeignKey('Titelbild.ID')) #TODO DELETE
titelbild: Mapped[Optional['Titelbild']] = relationship(back_populates='werk') #TODO DELETE
Verlag: Mapped[Optional[int]] = mapped_column(ForeignKey('Verlag.ID')) #TODO DELETE
verlag: Mapped[Optional['Verlag']] = relationship(back_populates='werk') #TODO DELETE
Werksform: Mapped[int] = mapped_column(ForeignKey('Werksform.ID'), nullable=False) #TODO DELETE
werksform: Mapped['Werksform'] = relationship(back_populates='werk') #TODO DELETE
# one-to-many
ausgabe: Mapped[List['Ausgabe']] = relationship(back_populates='werk')
veroeffentlichung: Mapped[List['Veroeffentlichung']] = relationship(back_populates='werk')
# many-to-many
genres: Mapped[List['Werk_Genre']] = relationship(back_populates='werk', cascade='all, delete-orphan')
genre_ids: AssociationProxy[List["Genre"]] = association_proxy("genres", "Genre", creator=lambda genre_id: Werk_Genre(Genre=genre_id))
herausgeber: Mapped[List['Werk_Herausgeber']] = relationship(back_populates='werk', cascade="all, delete-orphan")
herausgeber_ids: AssociationProxy[List['Herausgeber']] = association_proxy("herausgeber", "Herausgeber", creator=lambda hrsg_id: Werk_Herausgeber(Herausgeber=hrsg_id))
class Ausgabe(Base):
__tablename__ = 'Ausgabe'
# regular columns
ID: Mapped[int] = mapped_column(primary_key=True)
Erscheinungsdatum: Mapped[Optional[str]]
ISBN_13: Mapped[Optional[str]]
ISBN_10: Mapped[Optional[str]]
ISSN: Mapped[Optional[str]]
Preis: Mapped[Optional[str]]
Klappentext: Mapped[Optional[str]]
Anmerkungen: Mapped[Optional[str]]
# many-to-one
Titelbild: Mapped[Optional[int]] = mapped_column(ForeignKey('Titelbild.ID'))
titelbild: Mapped[Optional['Titelbild']] = relationship(back_populates='ausgabe')
Verlag: Mapped[Optional[int]] = mapped_column(ForeignKey('Verlag.ID'))
verlag: Mapped[Optional['Verlag']] = relationship(back_populates='ausgabe')
Werk: Mapped[int] = mapped_column(ForeignKey('Werk.ID'), nullable=False)
werk: Mapped["Werk"] = relationship(back_populates='ausgabe')
Werksform: Mapped[int] = mapped_column(ForeignKey('Werksform.ID'), nullable=False)
werksform: Mapped['Werksform'] = relationship(back_populates='ausgabe')
# Additional tables for many-to-many-relationships within the DB
class Text_Genre(Base):
@ -245,4 +277,3 @@ class Werk_Herausgeber(Base):
werk: Mapped['Werk'] = relationship(back_populates="herausgeber")
herausgeber: Mapped['Herausgeber'] = relationship(back_populates="werke")

View File

@ -1,7 +1,8 @@
{% set menu = (
("Texte", url_for("text.all")),
("Werke", url_for("werk.all")),
("Veröffentlichungen", url_for("veroeffentlichung.all")),
("Werke", url_for("werk.all")),
("Ausgaben", url_for("ausgabe.all")),
("Basisdaten", (
("Genres", url_for("genre.all")),
("Herausgeber:innen", url_for("herausgeber.all")),

View File

@ -0,0 +1,94 @@
{% extends 'base.html' %}
{% block title %}Ausgaben{% endblock title %}
{% block head %}
<link type="text/css" rel="stylesheet" href="{{ url_for('static', filename='datatables.css') }}">
{% endblock head %}
{% block heading %}Ausgaben{% endblock heading %}
{% block content %}
{% include "_icons.svg" %}
<div class="overflow-auto">
<table id="ausgabe-table" class="striped">
<thead>
<tr>
<th>Werk</th>
<th>Werksform</th>
<th>Verlag</th>
<th>Preis</th>
<th>Erscheinungsdatum</th>
<th>ISBN_13</th>
<th>ISBN_10</th>
<th>ISSN</th>
<th>Titelbild</th>
<th>Klappentext</th>
<th>Anmerkungen</th>
<th colspan="2">Aktionen</th>
</tr>
</thead>
<tbody>
{% for ausgabe in ausgaben %}
<tr id="ausgabe-{{ ausgabe['id'] }}">
<td title="Werk">{{ ausgabe["Werk"] }}</td>
<td title="Werksform">{{ ausgabe["Werksform"] }}</td>
<td title="Verlag">{{ ausgabe["Verlag"] }}</td>
<td title="Preis">{{ ausgabe["Preis"] }}</td>
<td title="Erscheinungsdatum">{{ ausgabe["Erscheinungsdatum"] }}</td>
<td title="ISBN_13">{{ ausgabe["ISBN_13"] }}</td>
<td title="ISBN_10">{{ ausgabe["ISBN_10"] }}</td>
<td title="ISSN">{{ ausgabe["ISSN"] }}</td>
<td title="Titelbild">
{% if ausgabe["Titelbild"] %}
<div class="imageselect-entry" data-bild="{{ ausgabe['Titelbild']['Bild'] }}">
<div class="imageselect-div">
<img src="{{ ausgabe['Titelbild']['Thumbnail'] }}" width="128" height="128" alt="Titelbild (Thumbnail)" />
<span class="imageselect-label display-none">
{{ ausgabe['Titelbild']['Dateiname'] }} ({{ ausgabe['Titelbild']['Breite'] }} x {{ ausgabe['Titelbild']['Hoehe'] }}, {{ ausgabe['Titelbild']['Dateigroesse'] }} Bytes)
</span>
</div>
</div>
{% else %}
&#10008;
{% endif %}
</td>
<td title="Klappentext"{% if ausgabe["Klappentext"] %} data-tooltip="{{ ausgabe['Klappentext'] | replace('\n', ' &#13;&#10; ') | safe }}" data-placement="bottom">&#10004;{% else %}>&#10008;{% endif %}</td>
<td title="Anmerkungen">{{ ausgabe["Anmerkungen"] }}</td>
<td class="action action-update" data-id="{{ ausgabe['id'] }}"><a href="{{ url_for('ausgabe.read', id=ausgabe['id']) }}" title="Ausgabe ansehen/bearbeiten"><svg viewbox="0 0 24 24"><use href="#update" /></svg></a></td>
<td id="delete-{{ ausgabe['id'] }}" class="action"><a onclick="return confirm('Eintrag wirklich löschen?');" href="{{ url_for('ausgabe.delete', id=ausgabe['id']) }}" title="Ausgabe löschen"><svg viewbox="0 0 24 24"><use href="#delete" /></svg></a></td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
<dialog id="imagepreview-modal" closedby="any">
<article>
<header>
<button class="modal-close" aria-label="close" rel="prev"></button>
<div class="imagepreview-details"></div>
</header>
<div class="imagepreview-div">
<img src="" />
</div>
</article>
</dialog>
{% endblock content %}
{% block script %}
<script src="{{ url_for('static', filename='js/datatables.js') }}"></script>
<script src="{{ url_for('static', filename='js/init_dt.js') }}"></script>
<script src="{{ url_for('static', filename='js/imagepreview.js') }}"></script>
<script>
window.onload = () => {
initDataTable("ausgabe-table");
initCreateButton("ausgabe-table", "Ausgabe hinzufügen", "{{ url_for('ausgabe.read', id=0) }}");
initImagepreview("imagepreview-modal");
}
</script>
{% endblock script %}

View File

@ -0,0 +1,163 @@
{% extends 'base.html' %}
{% set create_mode = (ausgabe['ID'] == 0) %}
{% block title %}
{% if create_mode %}
Neue Ausgabe erstellen
{% else %}
Ausgabe bearbeiten
{% endif %}
{% endblock title %}
{% block head %}
{% endblock head %}
{% block heading %}
{% if create_mode %}
Neue Ausgabe erstellen
{% else %}
Ausgabe bearbeiten
{% endif %}
{% endblock heading %}
{% block content %}
{% include "_icons.svg" %}
<form id="ausgabe_detail_form" method="post" enctype="multipart/form-data" action="#">
<section>
<label>
<span class="required">Werk</span>
<select id="form_Werk" name="form_Werk" aria-label="Werk" required>
<option value="">bitte wählen …</option>
{% for w in werke %}<option value="{{ w.ID }}"{% if w.ID == ausgabe['Werk'] %} selected{% endif %}>{{ w.Titel }}</option>{% endfor %}
</select>
</label>
</section>
<hr />
<section class="grid">
<div>
<label>
<span class="required">Werksform</span>
<select id="form_Werksform" name="form_Werksform" aria-label="Werksform" required>
<option value="">bitte wählen …</option>
{% for wf in werksformen %}<option value="{{ wf.ID }}"{% if wf.ID == ausgabe['Werksform'] %} selected{% endif %}>{{ wf.Werksform }}</option>{% endfor %}
</select>
</label>
<label>
Verlag
<select id="form_Verlag" name="form_Verlag" aria-label="Verlag">
<option selected value="">kein Verlag</option>
{% for v in verlage %}<option value="{{ v.ID }}"{% if v.ID == ausgabe['Verlag'] %} selected{% endif %}>{{ v.Verlag }}</option>{% endfor %}
</select>
</label>
<label>
Preis
<input id="form_Preis" name="form_Preis" aria-label="Preis" placeholder="kein Preis" value="{{ ausgabe['Preis'] or '' }}" />
</label>
<label>
Titelbild
<details id="form_Titelbild_dropdown" class="dropdown imageselect">
<summary id="form_Titelbild_summary" class="imageselect-summary">
</summary>
<ul>
<li class="imageselect-entry" data-bild='<svg width="900" height="1280" viewbox="0 0 90 128"><use class="imageselect-svg" href="#placeholder" /></svg>'>
<div class="imageselect-input">
<input id="imageselect-radio-0" type="radio" name="form_Titelbild" value="" {% if not ausgabe['Titelbild'] %}checked{% endif %}/>
</div>
<div class="imageselect-div">
<svg viewbox="0 0 90 128">
<use class="imageselect-svg" href="#placeholder" />
</svg>
</div>
<div class="imageselect-title">
<label class="imageselect-label" for="imageselect-radio-0">kein Titelbild</label>
</div>
</li>
{% for t in titelbilder %}
<li class="imageselect-entry" data-bild="{{ t.Bild }}">
<div class="imageselect-input">
<input id="imageselect-radio-{{ t.ID }}" type="radio" name="form_Titelbild" value="{{ t.ID }}" {% if ausgabe['Titelbild'] == t.ID %}checked{% endif %}/>
</div>
<div class="imageselect-div">
<img class="imageselect-img" src="{{ t.Thumbnail }}" />
</div>
<div class="imageselect-title">
<label class="imageselect-label" for="imageselect-radio-{{ t.ID }}">{{ t.Dateiname }} ({{ t.Breite }} x {{ t.Hoehe }}, {{ t.Dateigroesse }} Bytes)</label>
</div>
</li>
{% endfor %}
</ul>
</details>
</label>
</div>
<div>
<label>
Erscheinungsdatum (TT-MM-JJJJ, MM-JJJJ, JJJJ oder leer)
<div class="grid">
<input type="number" min="1" max="31" id="form_Erscheinungstag" name="form_Erscheinungstag" aria-label="Erscheinungstag" placeholder="Tag" value="{{ ausgabe['Erscheinungsdatum'][8:] if ausgabe['Erscheinungsdatum'] }}" />
<input type="number" min="1" max="12" id="form_Erscheinungsmonat" name="form_Erscheinungsmonat" aria-label="Erscheinungsmonat" placeholder="Monat" value="{{ ausgabe['Erscheinungsdatum'][5:7] if ausgabe['Erscheinungsdatum'] }}" />
<input type="number" min="1980" max="2100" id="form_Erscheinungsjahr" name="form_Erscheinungsjahr" aria-label="Erscheinungsjahr" placeholder="Jahr" value="{{ ausgabe['Erscheinungsdatum'][:4] if ausgabe['Erscheinungsdatum'] }}" />
</div>
</label>
<label>
ISBN-13
<input id="form_ISBN_13" name="form_ISBN_13" aria-label="ISBN-13" placeholder="keine ISBN-13" value="{{ ausgabe['ISBN_13'] or '' }}" />
</label>
<label>
ISBN-10
<input id="form_ISBN_10" name="form_ISBN_10" aria-label="ISBN-10" placeholder="keine ISBN-10" value="{{ ausgabe['ISBN_10'] or '' }}" />
</label>
<label>
ISSN
<input id="form_ISSN" name="form_ISSN" aria-label="ISSN" placeholder="keine ISSN" value="{{ ausgabe['ISSN'] or '' }}" />
</label>
</div>
</section>
<hr />
<section class="grid">
<label>
Klappentext
<textarea id="form_Klappentext" name="form_Klappentext" aria-label="Klappentext" placeholder="kein Klappentext" rows="10">{{ ausgabe['Klappentext'] or '' }}</textarea>
</label>
<label>
Anmerkungen
<textarea id="form_Anmerkungen" name="form_Anmerkungen" aria-label="Anmerkungen" placeholder="keine Anmerkungen" rows="10">
{{ ausgabe['Anmerkungen'] or '' }}</textarea>
</label>
</section>
<footer class="grid">
<button id="form_submit" type="submit" onclick="return validate_date()" formmethod="post" formaction="{% if create_mode %}{{ url_for('ausgabe.create') }}{% else %}{{ url_for('ausgabe.update', id=ausgabe['ID']) }}{% endif %}">
{% if create_mode %}Eintrag speichern{% else %}Änderungen speichern{% endif %}
</button>
<button type="reset" onclick="return confirm('Wirklich zurücksetzen? Alle geänderten Daten gehen dabei verloren.');" title="Alle Felder auf den vorherigen Zustand zurücksetzen">Alles zurücksetzen</button>
<a role="button" class="contrast" href="{{ url_for('ausgabe.all') }}">Abbrechen (nicht speichern)</a>
</footer>
</form>
<dialog id="imagepreview-modal" closedby="any">
<article>
<header>
<button class="modal-close" aria-label="close" rel="prev"></button>
<div class="imagepreview-details"></div>
</header>
<div class="imagepreview-div" />
</article>
</dialog>
{% endblock content %}
{% block script %}
<script src="{{ url_for('static', filename='js/imageselect.js') }}"></script>
<script src="{{ url_for('static', filename='js/imagepreview.js') }}"></script>
<script src="{{ url_for('static', filename='js/validate_date.js') }}"></script>
<script src="{{ url_for('static', filename='js/datalosswarning.js') }}"></script>
<script>
window.onload = () => {
initImageselect("form_Titelbild_dropdown");
initImagepreview("imagepreview-modal");
initDatalosswarning();
}
</script>
{% endblock script %}

116
the_works/views/ausgabe.py Normal file
View File

@ -0,0 +1,116 @@
from flask import Blueprint, render_template, request, redirect, flash, url_for
from sqlalchemy import select
from the_works.database import db
from the_works.models import Ausgabe, Werk, Verlag, Werksform, Titelbild
bp = Blueprint("ausgabe", __name__)
@bp.route("/ausgabe/")
@bp.route("/ausgabe/all/")
def all():
# select all rows from table "Ausgabe", ORM style
rows = db.session.execute(select(Ausgabe, Titelbild, Verlag, Werk, Werksform).join(Ausgabe.titelbild, isouter=True).join(Ausgabe.verlag, isouter=True).join(Ausgabe.werk, isouter=True).join(Ausgabe.werksform, isouter=True))
# condense result into list of dicts
ausgaben = []
for row in rows:
ausgaben.append({
"id": row.Ausgabe.ID,
"Werk": row.Werk.Titel if row.Werk else "",
"Werksform": row.Werksform.Werksform if row.Werksform else "",
"Verlag": row.Verlag.Verlag if row.Verlag else "",
"Erscheinungsdatum": row.Ausgabe.Erscheinungsdatum or "",
"ISBN_13": row.Ausgabe.ISBN_13 or "",
"ISBN_10": row.Ausgabe.ISBN_10 or "",
"ISSN": row.Ausgabe.ISSN or "",
"Preis": row.Ausgabe.Preis or "",
"Titelbild": db.session.get(Titelbild, row.Ausgabe.Titelbild).asdict_with_urls() if row.Titelbild else "",
"Klappentext": row.Ausgabe.Klappentext or "",
"Anmerkungen": row.Ausgabe.Anmerkungen or "",
})
return render_template("views/ausgabe.html", ausgaben=ausgaben)
@bp.route("/ausgabe/read/<int:id>")
def read(id):
# prepare Titelbilder as dict including URLs for thumbnail and full pic
titelbilder = map(lambda t: t.asdict_with_urls(), db.session.scalars(select(Titelbild)))
# id of zero -> return empty data
if id == 0:
return render_template("views/ausgabe_detail.html", ausgabe={"ID": 0, "Erscheinungsdatum": ""}, werke=db.session.scalars(select(Werk)), verlage=db.session.scalars(select(Verlag)), werksformen=db.session.scalars(select(Werksform)), titelbilder=titelbilder)
# all other ids -> read existing entry from DB and return as dict
a = db.session.get(Ausgabe, id)
if not a:
raise ValueError(f"Ausgabe with ID {id} not found")
ausgabe = a.asdict()
return render_template("views/ausgabe_detail.html", ausgabe=ausgabe, werke=db.session.scalars(select(Werk)), verlage=db.session.scalars(select(Verlag)), werksformen=db.session.scalars(select(Werksform)), titelbilder=titelbilder)
@bp.route("/ausgabe/create/", methods=["POST"])
def create():
ausgabe = Ausgabe(
Werk = request.form["form_Werk"],
Werksform = request.form["form_Werksform"],
Verlag = request.form["form_Verlag"] or None,
Erscheinungsdatum = _get_datum(request.form["form_Erscheinungsjahr"], request.form["form_Erscheinungsmonat"], request.form["form_Erscheinungstag"]),
ISBN_13 = request.form["form_ISBN_13"] or None,
ISBN_10 = request.form["form_ISBN_10"] or None,
ISSN = request.form["form_ISSN"] or None,
Preis = request.form["form_Preis"] or None,
Titelbild = request.form["form_Titelbild"] or None,
Klappentext = request.form["form_Klappentext"] or None,
Anmerkungen = request.form["form_Anmerkungen"] or None
)
db.session.add(ausgabe)
db.session.commit()
flash("Eintrag erfolgreich hinzugefügt")
return redirect(url_for("ausgabe.all"))
@bp.route("/ausgabe/update/<int:id>", methods=["POST"])
def update(id):
# get record
ausgabe = db.session.get(Ausgabe, id)
# update values
ausgabe.Werk = request.form["form_Werkl"]
ausgabe.Werksform = request.form["form_Werksform"]
ausgabe.Verlag = request.form["form_Verlag"] or None
ausgabe.Erscheinungsdatum = _get_datum(request.form["form_Erscheinungsjahr"], request.form["form_Erscheinungsmonat"], request.form["form_Erscheinungstag"])
ausgabe.ISBN_13 = request.form["form_ISBN_13"] or None
ausgabe.ISBN_10 = request.form["form_ISBN_10"] or None
ausgabe.ISSN = request.form["form_ISSN"] or None
ausgabe.Preis = request.form["form_Preis"] or None
ausgabe.Titelbild = request.form["form_Titelbild"] or None
ausgabe.Klappentext = request.form["form_Klappentext"] or None
ausgabe.Anmerkungen = request.form["form_Anmerkungen"] or None
# commit changes
db.session.commit()
flash("Eintrag erfolgreich geändert")
return redirect(url_for("ausgabe.all"))
@bp.route("/ausgabe/delete/<int:id>")
def delete(id):
ausgabe = db.session.get(Werk, id)
db.session.delete(ausgabe)
db.session.commit()
flash("Eintrag erfolgreich gelöscht")
return redirect(url_for("ausgabe.all"))
def _get_datum(jahr, monat, tag):
if tag != "":
return "-".join([jahr, monat.zfill(2), tag.zfill(2)])
elif monat != "":
return "-".join([jahr, monat.zfill(2)])
elif jahr != "":
return jahr
else:
return None

View File

@ -1,65 +1,19 @@
BEGIN TRANSACTION;
DROP TABLE IF EXISTS "Genre";
CREATE TABLE "Genre" (
"ID" INTEGER,
"Genre" TEXT NOT NULL,
PRIMARY KEY("ID" AUTOINCREMENT)
);
DROP TABLE IF EXISTS "Herausgeber";
CREATE TABLE "Herausgeber" (
"ID" INTEGER,
"Name" TEXT NOT NULL,
PRIMARY KEY("ID" AUTOINCREMENT)
);
DROP TABLE IF EXISTS "Pseudonym";
CREATE TABLE "Pseudonym" (
"ID" INTEGER,
"Pseudonym" TEXT NOT NULL,
PRIMARY KEY("ID" AUTOINCREMENT)
);
DROP TABLE IF EXISTS "Reihe";
CREATE TABLE "Reihe" (
CREATE TABLE sqlite_sequence(name,seq);
CREATE TABLE IF NOT EXISTS "Reihe" (
"ID" INTEGER,
"Titel" TEXT NOT NULL,
"Verlag" TEXT,
PRIMARY KEY("ID" AUTOINCREMENT),
FOREIGN KEY("Verlag") REFERENCES "Verlag"("ID")
);
DROP TABLE IF EXISTS "Sprache";
CREATE TABLE "Sprache" (
"ID" INTEGER,
"Sprache" TEXT NOT NULL,
PRIMARY KEY("ID" AUTOINCREMENT)
);
DROP TABLE IF EXISTS "Text";
CREATE TABLE "Text" (
"ID" INTEGER,
"Titel" TEXT NOT NULL,
"Untertitel" TEXT,
"Reihe" INTEGER,
"Textform" INTEGER,
"Sprache" INTEGER,
PRIMARY KEY("ID" AUTOINCREMENT),
FOREIGN KEY("Reihe") REFERENCES "Reihe"("ID"),
FOREIGN KEY("Sprache") REFERENCES "Sprache"("ID"),
FOREIGN KEY("Textform") REFERENCES "Textform"("ID")
);
DROP TABLE IF EXISTS "Text_Genre";
CREATE TABLE "Text_Genre" (
CREATE TABLE IF NOT EXISTS "Text_Genre" (
"Text" INTEGER,
"Genre" INTEGER,
PRIMARY KEY("Text","Genre"),
FOREIGN KEY("Genre") REFERENCES "Genre"("ID"),
FOREIGN KEY("Text") REFERENCES "Text"("ID")
);
DROP TABLE IF EXISTS "Textform";
CREATE TABLE "Textform" (
"ID" INTEGER,
"Textform" TEXT NOT NULL,
PRIMARY KEY("ID" AUTOINCREMENT)
);
DROP TABLE IF EXISTS "Titelbild";
CREATE TABLE "Titelbild" (
CREATE TABLE IF NOT EXISTS "Titelbild" (
"ID" INTEGER,
"Mimetype" TEXT NOT NULL,
"Dateiname" TEXT NOT NULL,
@ -71,31 +25,58 @@ CREATE TABLE "Titelbild" (
"sha256" TEXT NOT NULL UNIQUE,
PRIMARY KEY("ID" AUTOINCREMENT)
);
DROP TABLE IF EXISTS "Verlag";
CREATE TABLE "Verlag" (
CREATE TABLE IF NOT EXISTS "Genre" (
"ID" INTEGER,
"Verlag" TEXT NOT NULL,
"Genre" TEXT NOT NULL UNIQUE,
PRIMARY KEY("ID" AUTOINCREMENT)
);
DROP TABLE IF EXISTS "Veroeffentlichung";
CREATE TABLE "Veroeffentlichung" (
CREATE TABLE IF NOT EXISTS "Herausgeber" (
"ID" INTEGER,
"Text" INTEGER NOT NULL,
"Werk" INTEGER NOT NULL,
"AltTitel" TEXT,
"AltUntertitel" TEXT,
"Pseudonym" INTEGER NOT NULL,
PRIMARY KEY("ID" AUTOINCREMENT),
FOREIGN KEY("Pseudonym") REFERENCES "Pseudonym"("ID"),
FOREIGN KEY("Text") REFERENCES "Text"("ID"),
FOREIGN KEY("Werk") REFERENCES "Werk"("ID")
"Name" TEXT NOT NULL UNIQUE,
PRIMARY KEY("ID" AUTOINCREMENT)
);
DROP TABLE IF EXISTS "Werk";
CREATE TABLE "Werk" (
CREATE TABLE IF NOT EXISTS "Pseudonym" (
"ID" INTEGER,
"Pseudonym" TEXT NOT NULL UNIQUE,
PRIMARY KEY("ID" AUTOINCREMENT)
);
CREATE TABLE IF NOT EXISTS "Sprache" (
"ID" INTEGER,
"Sprache" TEXT NOT NULL UNIQUE,
PRIMARY KEY("ID" AUTOINCREMENT)
);
CREATE TABLE IF NOT EXISTS "Textform" (
"ID" INTEGER,
"Textform" TEXT NOT NULL UNIQUE,
PRIMARY KEY("ID" AUTOINCREMENT)
);
CREATE TABLE IF NOT EXISTS "Verlag" (
"ID" INTEGER,
"Verlag" TEXT NOT NULL UNIQUE,
PRIMARY KEY("ID" AUTOINCREMENT)
);
CREATE TABLE IF NOT EXISTS "Werksform" (
"ID" INTEGER,
"Werksform" TEXT NOT NULL UNIQUE,
PRIMARY KEY("ID" AUTOINCREMENT)
);
CREATE TABLE IF NOT EXISTS "Text" (
"ID" INTEGER,
"Titel" TEXT NOT NULL,
"Untertitel" TEXT,
"Werksform" INTEGER,
"Reihe" INTEGER,
"Textform" INTEGER NOT NULL,
"Sprache" INTEGER NOT NULL,
PRIMARY KEY("ID" AUTOINCREMENT),
FOREIGN KEY("Reihe") REFERENCES "Reihe"("ID"),
FOREIGN KEY("Sprache") REFERENCES "Sprache"("ID"),
FOREIGN KEY("Textform") REFERENCES "Textform"("ID")
);
CREATE TABLE IF NOT EXISTS "Werk" (
"ID" INTEGER,
"Titel" TEXT NOT NULL,
"Untertitel" TEXT,
"Werksform" INTEGER NOT NULL,
"Verlag" INTEGER,
"Reihe" INTEGER,
"Reihennummer" TEXT,
@ -113,26 +94,48 @@ CREATE TABLE "Werk" (
FOREIGN KEY("Verlag") REFERENCES "Verlag"("ID"),
FOREIGN KEY("Werksform") REFERENCES "Werksform"("ID")
);
DROP TABLE IF EXISTS "Werk_Genre";
CREATE TABLE "Werk_Genre" (
CREATE TABLE IF NOT EXISTS "Werk_Genre" (
"Werk" INTEGER,
"Genre" INTEGER,
PRIMARY KEY("Werk","Genre"),
FOREIGN KEY("Genre") REFERENCES "Genre"("ID"),
FOREIGN KEY("Werk") REFERENCES "Werk"("ID")
);
DROP TABLE IF EXISTS "Werk_Herausgeber";
CREATE TABLE "Werk_Herausgeber" (
CREATE TABLE IF NOT EXISTS "Werk_Herausgeber" (
"Herausgeber" INTEGER,
"Werk" INTEGER,
PRIMARY KEY("Herausgeber","Werk"),
FOREIGN KEY("Herausgeber") REFERENCES "Herausgeber"("ID"),
FOREIGN KEY("Werk") REFERENCES "Werk"("ID")
);
DROP TABLE IF EXISTS "Werksform";
CREATE TABLE "Werksform" (
CREATE TABLE IF NOT EXISTS "Veroeffentlichung" (
"ID" INTEGER,
"Werksform" TEXT NOT NULL,
PRIMARY KEY("ID" AUTOINCREMENT)
"Text" INTEGER NOT NULL,
"Werk" INTEGER NOT NULL,
"AltTitel" TEXT,
"AltUntertitel" TEXT,
"Pseudonym" INTEGER NOT NULL,
PRIMARY KEY("ID" AUTOINCREMENT),
FOREIGN KEY("Pseudonym") REFERENCES "Pseudonym"("ID"),
FOREIGN KEY("Text") REFERENCES "Text"("ID"),
FOREIGN KEY("Werk") REFERENCES "Werk"("ID")
);
CREATE TABLE IF NOT EXISTS "Ausgabe" (
"ID" INTEGER,
"Werk" INTEGER NOT NULL,
"Werksform" INTEGER NOT NULL,
"Verlag" INTEGER,
"Erscheinungsdatum" TEXT,
"ISBN_13" TEXT,
"ISBN_10" TEXT,
"ISSN" TEXT,
"Preis" TEXT,
"Titelbild" INTEGER,
"Klappentext" TEXT,
"Anmerkungen" TEXT,
PRIMARY KEY("ID"),
FOREIGN KEY("Titelbild") REFERENCES "Titelbild"("ID"),
FOREIGN KEY("Verlag") REFERENCES "Verlag"("ID"),
FOREIGN KEY("Werk") REFERENCES "Werk"("ID"),
FOREIGN KEY("Werksform") REFERENCES "Werksform"("ID")
);
COMMIT;