refactor(main): set_tag_fandom is pure fandom_id update

No rename, no retroactive-backfill-in-second-txn. display_name @property
reflects the change instantly. The auto-apply side effect (attach fandom
tag to images with the character) is preserved but simplified.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
2026-04-22 15:40:17 -04:00
parent 1e19048b09
commit 8e6a3d84ba
+30 -59
View File
@@ -1038,90 +1038,61 @@ def get_tag(tag_id):
def set_tag_fandom(tag_id): def set_tag_fandom(tag_id):
""" """
Set or clear a character tag's fandom association. Set or clear a character tag's fandom association.
Updates the tag name to include/remove the (Fandom) suffix.
Form params: Form params:
fandom_id: ID of an existing fandom tag (optional, clears if absent) fandom_id: ID of an existing fandom tag (preferred).
fandom_name: Name to find/create a fandom tag (used if fandom_id not given) fandom_name: Name to find/create a fandom tag (used if fandom_id
not given). If both omitted, the fandom is cleared.
""" """
tag = Tag.query.get_or_404(tag_id) tag = Tag.query.get_or_404(tag_id)
if tag.kind != "character": if tag.kind != "character":
return jsonify(ok=False, error="Only character tags can have fandoms"), 400 return jsonify(ok=False, error="Only character tags can have fandoms"), 400
fandom_id = request.form.get("fandom_id", type=int) fandom_id = request.form.get("fandom_id", type=int)
fandom_name = (request.form.get("fandom_name") or "").strip() fandom_name = (request.form.get("fandom_name") or "").strip()
# Get the character's base name (strip existing fandom suffix) and normalize it.
char_part = tag.name.split(":", 1)[1] if ":" in tag.name else tag.name
char_base, _ = _parse_character_fandom(char_part)
char_base = normalize_display_name(char_base)
if fandom_id: if fandom_id:
fandom_tag = Tag.query.get(fandom_id) fandom_tag = Tag.query.filter_by(id=fandom_id, kind="fandom").first()
if not fandom_tag or fandom_tag.kind != "fandom": if fandom_tag is None:
return jsonify(ok=False, error="Invalid fandom tag"), 400 return jsonify(ok=False, error="Invalid fandom tag"), 400
fandom_display = fandom_tag.name.split(":", 1)[1] if ":" in fandom_tag.name else fandom_tag.name tag.fandom_id = fandom_tag.id
elif fandom_name: elif fandom_name:
# _ensure_fandom_tag normalizes internally.
fandom_tag = _ensure_fandom_tag(fandom_name) fandom_tag = _ensure_fandom_tag(fandom_name)
fandom_display = fandom_tag.name.split(":", 1)[1] tag.fandom_id = fandom_tag.id
else: else:
# Clear fandom
tag.fandom_id = None tag.fandom_id = None
tag.name = f"character:{char_base}" fandom_tag = None
db.session.commit()
return jsonify(ok=True, tag={
"id": tag.id,
"name": tag.name,
"kind": tag.kind,
"fandom_id": None,
"fandom_name": None
})
# Check for name conflict before renaming
new_name = f"character:{char_base} ({fandom_display})"
conflict = Tag.query.filter(Tag.name == new_name, Tag.id != tag_id).first()
if conflict:
return jsonify(ok=False, error=f"A tag named '{new_name}' already exists"), 409
tag.fandom_id = fandom_tag.id
tag.name = new_name
db.session.commit() db.session.commit()
# Retroactive backfill: every image already tagged with this character should # Auto-apply the fandom tag to images already tagged with this character.
# also have the fandom tag attached. Runs in a second transaction so a failed if fandom_tag is not None:
# backfill doesn't roll back the rename. try:
try: db.session.execute(
db.session.execute( text("""
text(""" INSERT INTO image_tags (image_id, tag_id)
INSERT INTO image_tags (image_id, tag_id) SELECT it.image_id, :fandom_id
SELECT it.image_id, :fandom_id FROM image_tags it
FROM image_tags it WHERE it.tag_id = :char_id
WHERE it.tag_id = :char_id ON CONFLICT DO NOTHING
AND NOT EXISTS ( """),
SELECT 1 FROM image_tags it2 {"char_id": tag.id, "fandom_id": fandom_tag.id},
WHERE it2.image_id = it.image_id )
AND it2.tag_id = :fandom_id db.session.commit()
) except Exception as e:
"""), db.session.rollback()
{'char_id': tag.id, 'fandom_id': fandom_tag.id}, current_app.logger.warning(
) "set_tag_fandom auto-apply failed for tag %s -> fandom %s: %s",
db.session.commit() tag.id, fandom_tag.id, e,
except Exception as e: )
db.session.rollback()
# Rename already committed; log and continue. Sweep button recovers.
current_app.logger.warning(
"set_tag_fandom backfill failed for tag %s -> fandom %s: %s",
tag.id, fandom_tag.id, e,
)
return jsonify(ok=True, tag={ return jsonify(ok=True, tag={
"id": tag.id, "id": tag.id,
"name": tag.name, "name": tag.name,
"display_name": tag.display_name,
"kind": tag.kind, "kind": tag.kind,
"fandom_id": tag.fandom_id, "fandom_id": tag.fandom_id,
"fandom_name": fandom_display "fandom_name": fandom_tag.name if fandom_tag else None,
}) })