From 9309dd74faf926c9d31d6fe32152e80ac9e4cd68 Mon Sep 17 00:00:00 2001 From: JasonC Date: Sat, 12 Sep 2026 11:50:36 -0400 Subject: [PATCH] fix: avoid reusing singleton field template instances across libraries --- .../core/library/alchemy/constants.py | 30 ++++++++++++------- src/tagstudio/core/library/alchemy/library.py | 4 +-- .../core/library/alchemy/migrations.py | 4 +-- 3 files changed, 24 insertions(+), 14 deletions(-) diff --git a/src/tagstudio/core/library/alchemy/constants.py b/src/tagstudio/core/library/alchemy/constants.py index aba48a278..46a86bbe6 100644 --- a/src/tagstudio/core/library/alchemy/constants.py +++ b/src/tagstudio/core/library/alchemy/constants.py @@ -39,13 +39,23 @@ """) -DEFAULT_FIELD_TEMPLATES = ( - TextFieldTemplate(name="Title"), - TextFieldTemplate(name="Author"), - TextFieldTemplate(name="Artist"), - TextFieldTemplate(name="URL"), - TextFieldTemplate(name="Description", is_multiline=True), - TextFieldTemplate(name="Notes", is_multiline=True), - TextFieldTemplate(name="Comments", is_multiline=True), - DatetimeFieldTemplate(name="Date"), -) +def default_field_templates() -> tuple[TextFieldTemplate | DatetimeFieldTemplate, ...]: + """Build a fresh set of default field template instances. + + These must be constructed anew on every call rather than shared as module-level + singletons. SQLAlchemy instances remember their persistent identity once they've been + added and flushed to a session; reusing the same instances across multiple `Library` + (and therefore multiple database engines/sessions) causes every `Library` after the + first to silently skip inserting these rows, since SQLAlchemy assumes they already + exist. + """ + return ( + TextFieldTemplate(name="Title"), + TextFieldTemplate(name="Author"), + TextFieldTemplate(name="Artist"), + TextFieldTemplate(name="URL"), + TextFieldTemplate(name="Description", is_multiline=True), + TextFieldTemplate(name="Notes", is_multiline=True), + TextFieldTemplate(name="Comments", is_multiline=True), + DatetimeFieldTemplate(name="Date"), + ) diff --git a/src/tagstudio/core/library/alchemy/library.py b/src/tagstudio/core/library/alchemy/library.py index 473774b46..fbbf86d82 100644 --- a/src/tagstudio/core/library/alchemy/library.py +++ b/src/tagstudio/core/library/alchemy/library.py @@ -65,10 +65,10 @@ DB_VERSION, DB_VERSION_CURRENT_KEY, DB_VERSION_INITIAL_KEY, - DEFAULT_FIELD_TEMPLATES, JSON_FILENAME, SQL_FILENAME, TAG_CHILDREN_QUERY, + default_field_templates, ) from tagstudio.core.library.alchemy.db import Base as ModelBase from tagstudio.core.library.alchemy.enums import MAX_SQL_VARIABLES, BrowsingState, SortingModeEnum @@ -461,7 +461,7 @@ def create_sqlite_library( session.flush() # Add default field templates - for template in DEFAULT_FIELD_TEMPLATES: + for template in default_field_templates(): session.add(template) session.flush() diff --git a/src/tagstudio/core/library/alchemy/migrations.py b/src/tagstudio/core/library/alchemy/migrations.py index 5acc31e8a..9855c130d 100644 --- a/src/tagstudio/core/library/alchemy/migrations.py +++ b/src/tagstudio/core/library/alchemy/migrations.py @@ -18,7 +18,7 @@ DB_VERSION, DB_VERSION_CURRENT_KEY, DB_VERSION_INITIAL_KEY, - DEFAULT_FIELD_TEMPLATES, + default_field_templates, ) from tagstudio.core.library.alchemy.fields import LEGACY_FIELD_MAP, DatetimeField, TextField from tagstudio.core.library.alchemy.joins import TagParent @@ -468,7 +468,7 @@ def run(cls, session: Session, library_dir: Path, fmt_log: LoggingMethod): # Add default field templates logger.info(fmt_log("Adding default field templates...")) - for template in DEFAULT_FIELD_TEMPLATES: + for template in default_field_templates(): session.add(template) session.flush()