fix(modal): autocomplete picks resolve by tag_id, not just by name
Clicking an autocomplete row (or pressing Enter on a highlighted row) now sends tag_id in the add-tag POST. Server attaches that exact tag, skipping parse_kind_prefix. Same-name ambiguity — e.g. a character tag 'Yidhari' and a user tag 'Yidhari' both matching the autocomplete query 'yid' — now resolves by whichever row the user picked, instead of silently routing to the user-kind fallback. Bare free-text input (no click) still goes through parse_kind_prefix with its kind: shortcut and kind='user' default. The fandom picker path is untouched. The click handler also stashes tag_id on the dataset so the keyboard Enter path on a highlighted row picks up the same resolution. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
+57
-42
@@ -932,61 +932,76 @@ def add_tag(image_id):
|
||||
"""
|
||||
JSON endpoint to add a tag to an image.
|
||||
|
||||
Accepts the user-facing 'kind:name' shortcut in the 'name' field
|
||||
(e.g. 'character:Saber'). Bare strings without a recognized prefix are
|
||||
treated as user tags.
|
||||
Resolution order:
|
||||
- If 'tag_id' is provided, attach that exact tag (used when the
|
||||
autocomplete picker resolves a specific row). Auto-applies the
|
||||
character's fandom if set. Skips parse_kind_prefix entirely.
|
||||
- Else parse the user-facing 'kind:name' shortcut in the 'name' field
|
||||
(e.g. 'character:Saber'). Bare strings without a recognized prefix
|
||||
are treated as user tags.
|
||||
|
||||
For character tags, optional 'fandom_id' (preferred) or 'fandom_name'
|
||||
attaches the character to a fandom. Both omitted = null fandom (allowed).
|
||||
For character tags created via the parse path, optional 'fandom_id'
|
||||
(preferred) or 'fandom_name' attaches the character to a fandom. Both
|
||||
omitted = null fandom (allowed).
|
||||
"""
|
||||
from app.utils.tag_prefix import parse_kind_prefix
|
||||
from app.utils.tag_names import underscores_to_spaces
|
||||
|
||||
raw = (request.form.get("name") or "").strip()
|
||||
if not raw:
|
||||
abort(400)
|
||||
|
||||
kind, name = parse_kind_prefix(raw)
|
||||
if kind is None:
|
||||
# No recognized prefix — treat as a user-typed general tag.
|
||||
kind = "user"
|
||||
name = underscores_to_spaces(name)
|
||||
elif kind in ("character", "fandom"):
|
||||
name = normalize_display_name(name)
|
||||
|
||||
img = ImageRecord.query.get_or_404(image_id)
|
||||
|
||||
explicit_tag_id = request.form.get("tag_id", type=int)
|
||||
fandom_tag = None
|
||||
if kind == "character":
|
||||
fandom_id = request.form.get("fandom_id", type=int)
|
||||
fandom_name = (request.form.get("fandom_name") or "").strip() or None
|
||||
|
||||
if fandom_id:
|
||||
fandom_tag = Tag.query.filter_by(id=fandom_id, kind="fandom").first()
|
||||
if fandom_tag is None:
|
||||
return jsonify(ok=False, error="fandom_not_found"), 400
|
||||
elif fandom_name:
|
||||
fandom_tag = _ensure_fandom_tag(fandom_name)
|
||||
|
||||
tag = Tag.query.filter_by(
|
||||
kind="character",
|
||||
name=name,
|
||||
fandom_id=fandom_tag.id if fandom_tag else None,
|
||||
).first()
|
||||
if explicit_tag_id:
|
||||
# Autocomplete-picker path: attach the exact clicked tag.
|
||||
tag = Tag.query.get(explicit_tag_id)
|
||||
if tag is None:
|
||||
tag = Tag(
|
||||
return jsonify(ok=False, error="tag_not_found"), 404
|
||||
if tag.kind == "character" and tag.fandom_id:
|
||||
fandom_tag = Tag.query.get(tag.fandom_id)
|
||||
else:
|
||||
raw = (request.form.get("name") or "").strip()
|
||||
if not raw:
|
||||
abort(400)
|
||||
|
||||
kind, name = parse_kind_prefix(raw)
|
||||
if kind is None:
|
||||
# No recognized prefix — treat as a user-typed general tag.
|
||||
kind = "user"
|
||||
name = underscores_to_spaces(name)
|
||||
elif kind in ("character", "fandom"):
|
||||
name = normalize_display_name(name)
|
||||
|
||||
if kind == "character":
|
||||
fandom_id = request.form.get("fandom_id", type=int)
|
||||
fandom_name = (request.form.get("fandom_name") or "").strip() or None
|
||||
|
||||
if fandom_id:
|
||||
fandom_tag = Tag.query.filter_by(id=fandom_id, kind="fandom").first()
|
||||
if fandom_tag is None:
|
||||
return jsonify(ok=False, error="fandom_not_found"), 400
|
||||
elif fandom_name:
|
||||
fandom_tag = _ensure_fandom_tag(fandom_name)
|
||||
|
||||
tag = Tag.query.filter_by(
|
||||
kind="character",
|
||||
name=name,
|
||||
fandom_id=fandom_tag.id if fandom_tag else None,
|
||||
)
|
||||
db.session.add(tag)
|
||||
db.session.flush()
|
||||
else:
|
||||
tag = Tag.query.filter_by(kind=kind, name=name).first()
|
||||
if tag is None:
|
||||
tag = Tag(kind=kind, name=name)
|
||||
db.session.add(tag)
|
||||
db.session.flush()
|
||||
).first()
|
||||
if tag is None:
|
||||
tag = Tag(
|
||||
kind="character",
|
||||
name=name,
|
||||
fandom_id=fandom_tag.id if fandom_tag else None,
|
||||
)
|
||||
db.session.add(tag)
|
||||
db.session.flush()
|
||||
else:
|
||||
tag = Tag.query.filter_by(kind=kind, name=name).first()
|
||||
if tag is None:
|
||||
tag = Tag(kind=kind, name=name)
|
||||
db.session.add(tag)
|
||||
db.session.flush()
|
||||
|
||||
if tag not in img.tags:
|
||||
img.tags.append(tag)
|
||||
|
||||
Reference in New Issue
Block a user