modified a number of data models, view functions and Jinja templates to reflect changes in database schema

This commit is contained in:
eclipse 2025-08-19 22:02:21 +02:00
parent 56ec3051b6
commit 6b4f961053
7 changed files with 103 additions and 174 deletions

View File

@ -1,11 +1,12 @@
import sys
from typing import List, Optional
from sqlalchemy import ForeignKey, types, CheckConstraint
from sqlalchemy.orm import DeclarativeBase, Mapped, mapped_column, relationship
from typing import List, Optional, NewType
from decimal import Decimal
from sqlalchemy import ForeignKey, CheckConstraint, types, String, Numeric
from sqlalchemy.orm import DeclarativeBase, registry, Mapped, mapped_column, relationship
from sqlalchemy.ext.associationproxy import AssociationProxy, association_proxy
from flask import url_for
# these data models can be used with the generic functions from simple_view
SIMPLE_MODELS = [
{"name": "genre", "title": "Genres"},
{"name": "herausgeber", "title": "Herausgeber", "column": "Name"},
@ -16,8 +17,24 @@ SIMPLE_MODELS = [
{"name": "werksform", "title": "Werksformen"}
]
# define some new data types (strings of specific lengths, numbers of different precision and scale)
str13 = NewType("str13", str)
str10 = NewType("str10", str)
str8 = NewType("str8", str)
price = NewType("price", Decimal)
"""Base class for the_works' data models"""
class Base(DeclarativeBase):
# register new data types
registry = registry(type_annotation_map={
str13: String(13),
str10: String(10),
str8: String(8),
price: Numeric(7, 2)
})
"""Return the data record as a dict"""
def asdict(self) -> dict:
d = {}
for col in self.__table__.c:
@ -26,10 +43,13 @@ class Base(DeclarativeBase):
else:
if isinstance(value := self.__getattribute__(col.key), str) and len(value) > 50:
d[col.key] = value[:48] + '...'
elif value is None:
d[col.key] = ""
else:
d[col.key] = value
return d
"""Display the data record in readable format"""
def __repr__(self) -> str:
return f"{type(self).__name__}({str(self.asdict())})"
@ -61,7 +81,6 @@ 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') #TODO DELETE
werk: Mapped[List['Werk']] = relationship(back_populates='pseudonym')
@ -91,7 +110,6 @@ class Verlag(Base):
ausgabe: Mapped[List['Ausgabe']] = relationship(back_populates='verlag')
reihe: Mapped[List['Reihe']] = relationship(back_populates='verlag')
werk: Mapped[List['Werk']] = relationship(back_populates='verlag') #TODO DELETE
class Werksform(Base):
@ -101,7 +119,6 @@ class Werksform(Base):
Werksform: Mapped[str] = mapped_column(CheckConstraint('Werksform <> ""', name='WerksformNotEmptyConstraint'), nullable=False, unique=True)
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 their own view functions
@ -134,7 +151,6 @@ class Titelbild(Base):
# 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()
@ -152,12 +168,12 @@ class Text(Base):
Untertitel: Mapped[Optional[str]]
# many-to-one
Reihe: Mapped[Optional[int]] = mapped_column(ForeignKey('Reihe.ID'))
reihe: Mapped[Optional["Reihe"]] = relationship(back_populates="text")
Sprache: Mapped[int] = mapped_column(ForeignKey('Sprache.ID'), nullable=False)
sprache: Mapped['Sprache'] = relationship(back_populates='text')
Textform: Mapped[int] = mapped_column(ForeignKey('Textform.ID'), nullable=False)
textform: Mapped['Textform'] = relationship(back_populates='text')
Sprache: Mapped[int] = mapped_column(ForeignKey('Sprache.ID'), nullable=False)
sprache: Mapped['Sprache'] = relationship(back_populates='text')
Reihe: Mapped[Optional[int]] = mapped_column(ForeignKey('Reihe.ID'))
reihe: Mapped[Optional["Reihe"]] = relationship(back_populates="text")
# one-to-many
veroeffentlichung: Mapped[List['Veroeffentlichung']] = relationship(back_populates='text')
@ -172,16 +188,12 @@ class Veroeffentlichung(Base):
# regular columns
ID: Mapped[int] = mapped_column(primary_key=True)
AltTitel: Mapped[Optional[str]]
AltUntertitel: Mapped[Optional[str]]
# many-to-one
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')
AltTitel: Mapped[Optional[str]]
AltUntertitel: Mapped[Optional[str]]
class Werk(Base):
@ -191,26 +203,11 @@ class Werk(Base):
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
Reihennummer: Mapped[Optional[str]]
Pseudonym: Mapped[int] = mapped_column(ForeignKey('Pseudonym.ID'))
pseudonym: Mapped['Pseudonym'] = relationship(back_populates='werk')
# one-to-many
ausgabe: Mapped[List['Ausgabe']] = relationship(back_populates='werk')
@ -228,23 +225,21 @@ class Ausgabe(Base):
# 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')
Titelbild: Mapped[Optional[int]] = mapped_column(ForeignKey('Titelbild.ID'))
titelbild: Mapped[Optional['Titelbild']] = relationship(back_populates='ausgabe')
ISBN_13: Mapped[Optional[str13]]
ISBN_10: Mapped[Optional[str10]]
ISSN: Mapped[Optional[str8]]
Verlag: Mapped[Optional[int]] = mapped_column(ForeignKey('Verlag.ID'))
verlag: Mapped[Optional['Verlag']] = relationship(back_populates='ausgabe')
Erscheinungsdatum: Mapped[Optional[str]]
Preis: Mapped[Optional[price]]
Klappentext: Mapped[Optional[str]]
Anmerkungen: Mapped[Optional[str]]
# Additional tables for many-to-many-relationships within the DB

View File

@ -53,7 +53,7 @@ Ausgabe bearbeiten
</label>
<label>
Preis
<input id="form_Preis" name="form_Preis" aria-label="Preis" placeholder="kein Preis" value="{{ ausgabe['Preis'] or '' }}" />
<input id="form_Preis" name="form_Preis" aria-label="Preis" placeholder="kein Preis" type="number" min="0.01" max="99999.99" step="0.01" value="{{ ausgabe['Preis'] or '' }}" />
</label>
<label>
Titelbild
@ -95,22 +95,22 @@ Ausgabe bearbeiten
<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'] }}" />
<input id="form_Erscheinungstag" name="form_Erscheinungstag" aria-label="Erscheinungstag" placeholder="Tag" value="{{ ausgabe['Erscheinungsdatum'][8:] if ausgabe['Erscheinungsdatum'] }}" type="number" min="1" max="31" />
<input id="form_Erscheinungsmonat" name="form_Erscheinungsmonat" aria-label="Erscheinungsmonat" placeholder="Monat" value="{{ ausgabe['Erscheinungsdatum'][5:7] if ausgabe['Erscheinungsdatum'] }}" type="number" min="1" max="12" />
<input id="form_Erscheinungsjahr" name="form_Erscheinungsjahr" aria-label="Erscheinungsjahr" placeholder="Jahr" value="{{ ausgabe['Erscheinungsdatum'][:4] if ausgabe['Erscheinungsdatum'] }}" type="number" min="1980" max="2100" />
</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 '' }}" />
<input id="form_ISBN_13" name="form_ISBN_13" aria-label="ISBN-13" placeholder="keine ISBN-13" value="{{ ausgabe['ISBN_13'] or '' }}" type="text" size="13" minlength="13" maxlength="13" />
</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 '' }}" />
<input id="form_ISBN_10" name="form_ISBN_10" aria-label="ISBN-10" placeholder="keine ISBN-10" value="{{ ausgabe['ISBN_10'] or '' }}" type="text" size="10" minlength="10" maxlength="10" />
</label>
<label>
ISSN
<input id="form_ISSN" name="form_ISSN" aria-label="ISSN" placeholder="keine ISSN" value="{{ ausgabe['ISSN'] or '' }}" />
<input id="form_ISSN" name="form_ISSN" aria-label="ISSN" placeholder="keine ISSN" value="{{ ausgabe['ISSN'] or '' }}" type="text" size="8" maxlength="8" />
</label>
</div>
</section>

View File

@ -19,7 +19,6 @@
<th>Werk</th>
<th>Alt. Titel</th>
<th>Alt. Untertitel</th>
<th>Pseudonym</th>
<th colspan="2">Aktionen</th>
</tr>
</thead>
@ -30,8 +29,7 @@
<td title="Werk" data-display-werksform="{{ veroeffentlichung['wf_id'] }}">{{ veroeffentlichung["Werk"] }}</td>
<td title="Alt. Titel">{{ veroeffentlichung["AltTitel"] }}</td>
<td title="Alt. Untertitel">{{ veroeffentlichung["AltUntertitel"] }}</td>
<td title="Pseudonym">{{ veroeffentlichung["Pseudonym"] }}</td>
<td class="action action-update" data-id="{{ veroeffentlichung['id'] }}" data-text="{{ veroeffentlichung['t_id'] }}" data-werk="{{ veroeffentlichung['w_id'] }}" data-alttitel="{{ veroeffentlichung['AltTitel'] }}" data-altuntertitel="{{ veroeffentlichung['AltUntertitel_id'] }}" data-pseudonym="{{ veroeffentlichung['p_id'] }}"><a href="#" title="Veröffentlichung bearbeiten"><svg viewbox="0 0 24 24"><use href="#update" /></svg></a></td>
<td class="action action-update" data-id="{{ veroeffentlichung['id'] }}" data-text="{{ veroeffentlichung['t_id'] }}" data-werk="{{ veroeffentlichung['w_id'] }}" data-alttitel="{{ veroeffentlichung['AltTitel'] }}" data-altuntertitel="{{ veroeffentlichung['AltUntertitel_id'] }}"><a href="#" title="Veröffentlichung bearbeiten"><svg viewbox="0 0 24 24"><use href="#update" /></svg></a></td>
<td id="delete-{{ veroeffentlichung['id'] }}" class="action"><a onclick="return confirm('Eintrag wirklich löschen?');" href="{{ url_for('veroeffentlichung.delete', id=veroeffentlichung['id']) }}" title="Veröffentlichung löschen"><svg viewbox="0 0 24 24"><use href="#delete" /></svg></a></td>
</tr>
{% endfor %}
@ -70,13 +68,6 @@
Alternativer Untertitel
<input id="form_AltUntertitel" name="form_AltUntertitel" aria-Label="Alternativer Untertitel" placeholder="Alternativen Untertitel eingeben" />
</label>
<label>
<span class="required">Pseudonym</span>
<select id="form_Pseudonym" name="form_Pseudonym" aria-label="Pseudonym" required>
<option selected value="">Pseudonym auswählen …</option>
{% for p in pseudonyme %}<option value="{{ p.ID }}">{{ p.Pseudonym }}</option>{% endfor %}
</select>
</label>
</article>
</fieldset>
@ -97,7 +88,7 @@
window.onload = function () {
initDataTable("veroeffentlichung-table");
initCreateButton("veroeffentlichung-table", "Veröffentlichung hinzufügen …");
initModal("veroeffentlichung-modal", ["form_Text", "form_Werk", "form_AltTitel", "form_AltUntertitel", "form_Pseudonym"], ["Neue Veröffentlichung", "Veröffentlichung bearbeiten"], ["{{ url_for('veroeffentlichung.create') }}", "{{ url_for('veroeffentlichung.update', id=-1) }}"]);
initModal("veroeffentlichung-modal", ["form_Text", "form_Werk", "form_AltTitel", "form_AltUntertitel"], ["Neue Veröffentlichung", "Veröffentlichung bearbeiten"], ["{{ url_for('veroeffentlichung.create') }}", "{{ url_for('veroeffentlichung.update', id=-1) }}"]);
}
</script>
{% endblock script %}

View File

@ -18,20 +18,11 @@
<tr>
<th>Titel</th>
<th>Untertitel</th>
<th>Werksform</th>
<th>Reihe</th>
<th>Reihennummer</th>
<th>Verlag</th>
<th>Preis</th>
<th>Erscheinungsdatum</th>
<th>ISBN_13</th>
<th>ISBN_10</th>
<th>ISSN</th>
<th>Genre(s)</th>
<th>Herausgeber:in(nen)</th>
<th>Titelbild</th>
<th>Klappentext</th>
<th>Anmerkungen</th>
<th>Pseudonym</th>
<th colspan="2">Aktionen</th>
</tr>
</thead>
@ -40,33 +31,11 @@
<tr id="werk-{{ werk['id'] }}">
<td title="Titel">{{ werk["Titel"] }}</td>
<td title="Untertitel">{{ werk["Untertitel"] }}</td>
<td title="Werksform">{{ werk["Werksform"] }}</td>
<td title="Reihe">{{ werk["Reihe"] }}</td>
<td title="Reihennummer">{{ werk["Reihennummer"] }}</td>
<td title="Verlag">{{ werk["Verlag"] }}</td>
<td title="Preis">{{ werk["Preis"] }}</td>
<td title="Erscheinungsdatum">{{ werk["Erscheinungsdatum"] }}</td>
<td title="ISBN_13">{{ werk["ISBN_13"] }}</td>
<td title="ISBN_10">{{ werk["ISBN_10"] }}</td>
<td title="ISSN">{{ werk["ISSN"] }}</td>
<td title="Genre(s)">{{ werk["Genre_list"] | join(", ") }}</td>
<td title="Herausgeber:in(nen)">{{ werk["Herausgeber_list"] | join(", ") }}</td>
<td title="Titelbild">
{% if werk["Titelbild"] %}
<div class="imageselect-entry" data-bild="{{ werk['Titelbild']['Bild'] }}">
<div class="imageselect-div">
<img src="{{ werk['Titelbild']['Thumbnail'] }}" width="128" height="128" alt="Titelbild (Thumbnail)" />
<span class="imageselect-label display-none">
{{ werk['Titelbild']['Dateiname'] }} ({{ werk['Titelbild']['Breite'] }} x {{ werk['Titelbild']['Hoehe'] }}, {{ werk['Titelbild']['Dateigroesse'] }} Bytes)
</span>
</div>
</div>
{% else %}
&#10008;
{% endif %}
</td>
<td title="Klappentext"{% if werk["Klappentext"] %} data-tooltip="{{ werk['Klappentext'] | replace('\n', ' &#13;&#10; ') | safe }}" data-placement="bottom">&#10004;{% else %}>&#10008;{% endif %}</td>
<td title="Anmerkungen">{{ werk["Anmerkungen"] }}</td>
<td title="Pseudonym">{{ werk["Pseudonym"] }}</td>
<td class="action action-update" data-id="{{ werk['id'] }}"><a href="{{ url_for('werk.read', id=werk['id']) }}" title="Werk ansehen/bearbeiten"><svg viewbox="0 0 24 24"><use href="#update" /></svg></a></td>
<td id="delete-{{ werk['id'] }}" class="action"><a onclick="return confirm('Eintrag wirklich löschen?');" href="{{ url_for('werk.delete', id=werk['id']) }}" title="Werk löschen"><svg viewbox="0 0 24 24"><use href="#delete" /></svg></a></td>
</tr>
@ -75,29 +44,15 @@
</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("werk-table");
initCreateButton("werk-table", "Werk hinzufügen", "{{ url_for('werk.read', id=0) }}");
initImagepreview("imagepreview-modal");
}
</script>
{% endblock script %}

View File

@ -1,14 +1,14 @@
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 Veroeffentlichung, Text, Werk, Werksform, Pseudonym
from the_works.models import Veroeffentlichung, Text, Werk
bp = Blueprint("veroeffentlichung", __name__)
@bp.route("/veroeffentlichung/")
@bp.route("/veroeffentlichung/all/")
def all():
rows = db.session.execute(select(Veroeffentlichung, Text, Werk, Werksform, Pseudonym).join(Veroeffentlichung.text, isouter=True).join(Veroeffentlichung.werk, isouter=True).join(Veroeffentlichung.pseudonym, isouter=True).join(Werk.werksform))
rows = db.session.execute(select(Veroeffentlichung, Text, Werk).join(Veroeffentlichung.text, isouter=True).join(Veroeffentlichung.werk, isouter=True))
veroeffentlichungen = []
for row in rows:
veroeffentlichungen.append({
@ -17,13 +17,10 @@ def all():
"t_id": row.Veroeffentlichung.Text,
"Werk": row.Werk.Titel,
"w_id": row.Veroeffentlichung.Werk,
"wf_id": row.Werksform.ID,
"AltTitel": row.Veroeffentlichung.AltTitel or "",
"AltUntertitel": row.Veroeffentlichung.AltUntertitel or "",
"Pseudonym": row.Pseudonym.Pseudonym,
"p_id": row.Veroeffentlichung.Pseudonym,
"AltUntertitel": row.Veroeffentlichung.AltUntertitel or ""
})
return render_template("views/veroeffentlichung.html", veroeffentlichungen=veroeffentlichungen, texte=db.session.scalars(select(Text)), werke=db.session.scalars(select(Werk)), pseudonyme=db.session.scalars(select(Pseudonym)))
return render_template("views/veroeffentlichung.html", veroeffentlichungen=veroeffentlichungen, texte=db.session.scalars(select(Text)))
@bp.route("/veroeffentlichung/create/", methods=["POST"])
def create():
@ -31,8 +28,7 @@ def create():
Text = request.form["form_Text"],
Werk = request.form["form_Werk"],
AltTitel = request.form["form_AltTitel"],
AltUntertitel = request.form["form_AltUntertitel"],
Pseudonym = request.form["form_Pseudonym"],
AltUntertitel = request.form["form_AltUntertitel"]
))
db.session.commit()
flash("Eintrag erfolgreich hinzugefügt")
@ -45,7 +41,6 @@ def update(id):
veroeffentlichung.Werk = request.form["form_Werk"]
veroeffentlichung.AltTitel = request.form["form_AltTitel"]
veroeffentlichung.AltUntertitel = request.form["form_AltUntertitel"]
veroeffentlichung.Pseudonym = request.form["form_Pseudonym"]
db.session.commit()
flash("Eintrag erfolgreich geändert")
return redirect(url_for("veroeffentlichung.all"), code=303)

View File

@ -1,7 +1,7 @@
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 Werk, Reihe, Verlag, Werksform, Genre, Herausgeber, Titelbild
from the_works.models import Werk, Reihe, Pseudonym, Genre, Herausgeber
bp = Blueprint("werk", __name__)
@ -10,7 +10,7 @@ bp = Blueprint("werk", __name__)
@bp.route("/werk/all/")
def all():
# select all rows from table "Werk", ORM style
rows = db.session.execute(select(Werk, Reihe, Verlag, Werksform).join(Werk.reihe, isouter=True).join(Werk.verlag, isouter=True).join(Werk.werksform, isouter=True))
rows = db.session.execute(select(Werk, Reihe, Pseudonym).join(Werk.reihe, isouter=True).join(Werk.pseudonym, isouter=True))
# condense result into list of dicts
werke = []
for row in rows:
@ -18,33 +18,20 @@ def all():
"id": row.Werk.ID,
"Titel": row.Werk.Titel,
"Untertitel": row.Werk.Untertitel or "",
"Werksform": row.Werksform.Werksform if row.Werksform else "",
"Verlag": row.Verlag.Verlag if row.Verlag else "",
"Reihe": row.Reihe.Titel if row.Reihe else "",
"Reihennummer": row.Werk.Reihennummer or "",
"Erscheinungsdatum": row.Werk.Erscheinungsdatum or "",
"ISBN_13": row.Werk.ISBN_13 or "",
"ISBN_10": row.Werk.ISBN_10 or "",
"ISSN": row.Werk.ISSN or "",
"Preis": row.Werk.Preis or "",
# "Titelbild": url_for("titelbild.thumbnail", id=row.Werk.Titelbild) if row.Werk.Titelbild else "",
"Titelbild": db.session.get(Titelbild, row.Werk.Titelbild).asdict_with_urls() if row.Werk.Titelbild else "",
"Klappentext": row.Werk.Klappentext or "",
"Anmerkungen": row.Werk.Anmerkungen or "",
"Herausgeber_list": [wh.herausgeber.Name for wh in row.Werk.herausgeber],
"Genre_list": [wg.genre.Genre for wg in row.Werk.genres],
"Pseudonym": row.Pseudonym.Pseudonym if row.Pseudonym else ""
})
return render_template("views/werk.html", werke=werke)
@bp.route("/werk/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/werk_detail.html", werk={"ID": 0, "Erscheinungsdatum": ""}, reihen=db.session.scalars(select(Reihe)), verlage=db.session.scalars(select(Verlag)), werksformen=db.session.scalars(select(Werksform)), genres=db.session.scalars(select(Genre)), hrsg=db.session.scalars(select(Herausgeber)), titelbilder=titelbilder)
return render_template("views/werk_detail.html", werk={"ID": 0, "Erscheinungsdatum": ""}, reihen=db.session.scalars(select(Reihe)), genres=db.session.scalars(select(Genre)), hrsg=db.session.scalars(select(Herausgeber)), pseudonyme=db.session.scalars(select(Pseudonym)))
# all other ids -> read existing entry from DB and return as dict
w = db.session.get(Werk, id)
@ -54,7 +41,7 @@ def read(id):
werk["Genres"] = w.genre_ids
werk["Herausgeber"] = w.herausgeber_ids
return render_template("views/werk_detail.html", werk=werk, reihen=db.session.scalars(select(Reihe)), verlage=db.session.scalars(select(Verlag)), werksformen=db.session.scalars(select(Werksform)), genres=db.session.scalars(select(Genre)), hrsg=db.session.scalars(select(Herausgeber)), titelbilder=titelbilder)
return render_template("views/werk_detail.html", werk=werk, reihen=db.session.scalars(select(Reihe)), genres=db.session.scalars(select(Genre)), hrsg=db.session.scalars(select(Herausgeber)), pseudonyme=db.session.scalars(select(Pseudonym)))
@bp.route("/werk/create/", methods=["POST"])
@ -62,18 +49,9 @@ def create():
werk = Werk(
Titel = request.form["form_Titel"],
Untertitel = request.form["form_Untertitel"] or None,
Werksform = request.form["form_Werksform"],
Verlag = request.form["form_Verlag"] or None,
Reihe = request.form["form_Reihe"] or None,
Reihennummer = request.form["form_Reihennummer"] 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
Pseudonym = request.form["form_Pseudonym"],
)
for g in request.form.getlist("form_Genre"):
werk.genre_ids.append(g)
@ -93,18 +71,9 @@ def update(id):
# update values
werk.Titel = request.form["form_Titel"]
werk.Untertitel = request.form["form_Untertitel"] or None
werk.Werksform = request.form["form_Werksform"]
werk.Verlag = request.form["form_Verlag"] or None
werk.Reihe = request.form["form_Reihe"] or None
werk.Reihennummer = request.form["form_Reihennummer"] or None
werk.Erscheinungsdatum = _get_datum(request.form["form_Erscheinungsjahr"], request.form["form_Erscheinungsmonat"], request.form["form_Erscheinungstag"])
werk.ISBN_13 = request.form["form_ISBN_13"] or None
werk.ISBN_10 = request.form["form_ISBN_10"] or None
werk.ISSN = request.form["form_ISSN"] or None
werk.Preis = request.form["form_Preis"] or None
werk.Titelbild = request.form["form_Titelbild"] or None
werk.Klappentext = request.form["form_Klappentext"] or None
werk.Anmerkungen = request.form["form_Anmerkungen"] or None
werk.Pseudonym = request.form["form_Pseudonym"]
# update associated values: Genre
form_set = set(map(int, request.form.getlist("form_Genre")))
@ -133,15 +102,3 @@ def delete(id):
db.session.commit()
flash("Eintrag erfolgreich gelöscht")
return redirect(url_for("werk.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

36
tmp.md
View File

@ -57,7 +57,43 @@ Sprache, (Genres) Reihe, R.No, (Genres), (Hrsg) IS?Nx3, Preis, Titel
Stand der neuen DB
- Basisdaten sind vorhanden, Ausnahme: Titelbild
- Text ist auf dem alten Stand
- es fehlt
- Veroeffentlichung
- Titelbild
- Werk
- Ausgabe
- die Python views sind alle upgedated (theoretisch)
- zu ändernde Jinja-Templates
- x veroeffentlichung
- x werk
- werk_detail
- ausgabe_detail
- Darstellung von Preis updaten und validaten
- nice to have: Darstellung und Validation von IS*N verbessern (auf Basis der jew. Spezifikation)
- https://en.wikipedia.org/wiki/ISBN, https://en.wikipedia.org/wiki/ISSN
- isbnlib (Python package)
- isbnlib-dnb
- nice to have: Metadaten zu meinen Werken direkt aus einer externen DB holen
- zB DNB
- keine Cover o. Klappentexte, Daten sind unsauber (zB Preis)
- Demo-Abruf-Seite: https://dnb-sru-demo.streamlit.app/
- ist kompliziert; vllt reicht es auch, einmal per Hand alle Werke von Tobias Radloff / Paul Jansen abzurufen und in die DB zu übernehmen?
- https://services.dnb.de/sru/dnb?version=1.1&operation=searchRetrieve&query=atr%3DTobias%20and%20atr%3DRadloff
- buchhandel.de
- keine frei verfügbare API, aber [Standardsuche](https://buchhandel.de/suche)
- openlibrary.org: ein Treffer [Amoralisch](https://openlibrary.org/works/OL26179908W/Philip_Strasser_in_Amoralisch?edition=key%3A/books/OL35329529M)
- Google Books: null Treffer
- VLB hat ne [REST-API](https://vlb.de/hilfe/datenbezug/rest-api), aber die kostet
- booklooker: taugt nicht (ist ein Marktplatz und kein Katalog)
- ISBNdb.com -> kennt 9 Bücher von mir
- kostet, gibt aber free 7-day trial
- [Amazon Product Advertising API](https://webservices.amazon.com/paapi5/documentation/)
- kostet nix