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:
+30
-59
@@ -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,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user