Skip to content

Commit e76803f

Browse files
committed
Issue 460: Set cascade delete pragma on connect.
- add test to ensure stray book stats are deleted - data cleanup migration (one-time only) - remove LanguageRepo.delete, deletes now cascade correctly
1 parent bce5b46 commit e76803f

File tree

6 files changed

+40
-29
lines changed

6 files changed

+40
-29
lines changed

lute/app_factory.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -314,6 +314,7 @@ def _create_app(app_config, extra_config):
314314
@listens_for(Pool, "connect")
315315
def _pragmas_on_connect(dbapi_con, con_record): # pylint: disable=unused-argument
316316
dbapi_con.execute("pragma recursive_triggers = on;")
317+
dbapi_con.execute("pragma foreign_keys = on;")
317318

318319
with app.app_context():
319320
db.create_all()
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
-- Clean up bad data, where relationships are invalid.
2+
--
3+
-- Per issue 460, the pragma foreign_keys was not ON, so it's possible
4+
-- (though unlikely) that some data in the db is bad/unreachable.
5+
--
6+
-- This is being done as a one-time fix, rather than as a repeatable
7+
-- migration, as it would be very annoying if the data model changed
8+
-- and I forgot to update the script!!
9+
10+
DELETE FROM languagedicts WHERE LdLgID NOT IN (SELECT LgID FROM languages);
11+
DELETE FROM wordsread WHERE WrLgID NOT IN (SELECT LgID FROM languages);
12+
13+
DELETE FROM books WHERE BkLgID NOT IN (SELECT LgID FROM languages);
14+
DELETE FROM bookstats WHERE BkID NOT IN (SELECT BkID FROM books);
15+
DELETE FROM booktags WHERE BtBkID NOT IN (SELECT BkID FROM books) OR BtT2ID NOT IN (SELECT T2ID FROM tags2);
16+
17+
DELETE FROM texts WHERE TxBkID NOT IN (SELECT BkID FROM books);
18+
DELETE FROM textbookmarks WHERE TbTxID NOT IN (SELECT TxID FROM texts);
19+
DELETE FROM sentences WHERE SeTxID NOT IN (SELECT TxID FROM texts);
20+
DELETE FROM wordsread WHERE WrTxID IS NOT NULL AND WrTxID NOT IN (SELECT TxID FROM texts);
21+
22+
DELETE FROM wordtags WHERE WtWoID NOT IN (SELECT WoID FROM words) OR WtTgID NOT IN (SELECT TgID FROM tags);
23+
DELETE FROM wordimages WHERE WiWoID NOT IN (SELECT WoID FROM words);
24+
DELETE FROM wordflashmessages WHERE WfWoID NOT IN (SELECT WoID FROM words);
25+
DELETE FROM wordparents WHERE WpWoID NOT IN (SELECT WoID FROM words) OR WpParentWoID NOT IN (SELECT WoID FROM words);

lute/language/routes.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
from sqlalchemy.exc import IntegrityError
77
from flask import Blueprint, current_app, render_template, redirect, url_for, flash
88
from lute.models.language import Language
9-
from lute.models.repositories import LanguageRepository, UserSettingRepository
9+
from lute.models.repositories import UserSettingRepository
1010
from lute.language.service import Service
1111
from lute.language.forms import LanguageForm
1212
from lute.db import db
@@ -156,8 +156,8 @@ def delete(langid):
156156
language = db.session.get(Language, langid)
157157
if not language:
158158
flash(f"Language {langid} not found")
159-
r = LanguageRepository(db.session)
160-
r.delete(language)
159+
db.session.delete(language)
160+
db.session.commit()
161161
return redirect(url_for("language.index"))
162162

163163

lute/models/repositories.py

Lines changed: 0 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -141,29 +141,6 @@ def find_by_name(self, name):
141141
.first()
142142
)
143143

144-
def delete(self, language):
145-
"""
146-
Hacky method to delete language and all terms, books, and dicts
147-
associated with it.
148-
149-
There is _certainly_ a better way to do this using
150-
Sqlalchemy relationships and cascade deletes, but I
151-
was running into problems with it (things not cascading,
152-
or warnings ("SAWarning: Object of type <Term> not in
153-
session, add operation along 'Language.terms' will not
154-
proceed") during test runs. It would be nice to have
155-
a "correct" mapping, but this is good enough for now.
156-
157-
TODO zzfuture fix: fix Language-Book and -Term mappings.
158-
"""
159-
sqls = [
160-
"pragma foreign_keys = ON",
161-
f"delete from languages where LgID = {language.id}",
162-
]
163-
for s in sqls:
164-
self.session.execute(sqltext(s))
165-
self.session.commit()
166-
167144
def all_dictionaries(self):
168145
"All dictionaries for all languages."
169146
lang_dicts = {}

tests/orm/test_Book.py

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
import pytest
77
from lute.models.book import Book, BookTag, TextBookmark, BookStats
88
from lute.read.service import Service
9+
from lute.book.stats import Service as BookStatsService
910
from lute.db import db
1011
from tests.dbasserts import assert_sql_result, assert_record_count_equals
1112

@@ -56,10 +57,17 @@ def test_delete_book(empty_db, simple_book):
5657
service.mark_page_read(b.id, 1, False)
5758
service.mark_page_read(b.id, 1, True)
5859

60+
bss = BookStatsService(db.session)
61+
bss.refresh_stats()
62+
63+
check_tables = ["books", "bookstats", "texts", "sentences", "booktags"]
64+
for t in check_tables:
65+
assert_record_count_equals(t, 1, f"{t} created")
66+
5967
db.session.delete(b)
6068
db.session.commit()
6169

62-
for t in ["books", "texts", "sentences", "booktags"]:
70+
for t in check_tables:
6371
assert_record_count_equals(t, 0, f"{t} deleted")
6472

6573
sql = "select * from tags2"

tests/orm/test_Language.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -122,8 +122,8 @@ def test_delete_language_removes_book_and_terms(app_context, spanish):
122122
assert_sql_result(sqldict, ["something?[LUTE]"], "dict")
123123
assert_record_count_equals("select * from wordsread", 1, "saved")
124124

125-
repo = LanguageRepository(db.session)
126-
repo.delete(spanish)
125+
db.session.delete(spanish)
126+
db.session.commit()
127127

128128
assert_sql_result(sqlbook, [], "book deleted")
129129
assert_sql_result(sqlterms, [], "terms deleted")

0 commit comments

Comments
 (0)