Skip to content

Admin: 'Merge keywords' bulk action (dedup taxonomy) #1352

@jonfroehlich

Description

@jonfroehlich

Follow-up to the admin audit (#1346, Phase 4 deferred this as its own focused issue because it's destructive).

Problem

Keywords are free-text and case/whitespace-sensitive with no uniqueness constraint, so near-duplicates coexist (e.g. Speech / speech / Speech ) and fragment the public keyword pages. Phase 4 (PR #1351) added a sortable Total Uses column and a Used / Unused filter to find orphans/dupes; this issue adds the tool to merge them.

Proposed feature

A "Merge selected keywords" admin action on the Keyword changelist:

  1. Select two or more keywords → choose the action.
  2. Intermediate confirmation page (standard Django action pattern) listing the selected keywords with their usage counts, and a radio to pick the target keyword to merge into.
  3. On confirm: for every model that references Keyword — Publication, Talk, Poster, Grant, Project, ProjectUmbrella (the 6 keywords M2M holders; Video has none) — reattach the target keyword to each object currently tagged with a non-target keyword, then delete the non-target keywords.

Implementation notes

  • Reattach via the M2M managers (obj.keywords.add(target) is idempotent, then the source keyword's deletion drops its rows) to avoid unique-constraint collisions when an object already has the target.
  • Needs a small template under website/admin/templates/admin/website/keyword/ for the intermediate page.
  • Tests required given it's destructive: target keeps/gains all references, sources are deleted, no duplicate M2M rows, objects already tagged with the target are unaffected, and a no-op/edge case (single keyword selected) is handled.

Acceptance

  • Action + intermediate target-selection page
  • Reassigns all 6 reverse relations correctly, then deletes the merged-away keywords
  • Tests covering the reassignment, deletion, and dedup edge cases
  • Admin-only (not Pa11y-scanned); no model/migration changes

Parent: #1346.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions