Tag import: parent_id isn't re-validated when it matches the pre-import parent
メンテナーはふだん 1 日以内に返信
@ufedaseyeuconsultant がすでに取り組んでいます。
2026年10月9日 から。
評価
この issue はまだ評価されていません。
説明
Summary
A tag import (CSV or JSON, with the optional previous_id column from #673) must reject a row whose parent_id is its current parent's pre-import id when the same file renames that parent. It must keep accepting a child that names the parent's id after the import (the end-state id), also when two parents swap their ids.
Fixing this showed that several neighboring cases went wrong in the same code: parent references depended on the order of the rows, kept children could be lost to the delete cascade, parent cycles and imports deeper than the depth limit were not rejected at the Plan step, and some valid imports failed partway through. Each of those is described below with its own acceptance criteria, because the fix for this issue delivers them too.
In the error messages below, "Row N" is the N-th data row of the file, counting from 1. In a CSV file that is spreadsheet line N + 1, because the header is line 1. In a JSON file it is the N-th entry of tags. In the import log, an error raised by a row's action starts with that action, for example Action error in 'update_parent' (#6): Row 2: .... The #6 is the action's number in the plan, which can differ from the row number because the plan adds its own steps and builds a parent's row before its children's rows. The row is the Row N part.
Problem A: a stale parent_id of an existing child is accepted
UpdateParentTag.applies_for decides whether a row changes its tag's parent by comparing the row's parent_id with the external_id of the tag's parent before the import. When the two match, the row is "no change", so _validate_parent never runs for it.
Take an existing child tag_2 under tag_1. The file renames tag_1 to tag_50 and keeps the tag_2 row with parent_id: tag_1. The tag_2 row matches the pre-import parent, so it is "no change", and the import accepts it silently. After the import no tag has the id tag_1, so the row's parent_id names nothing. #673's scenario "a child row referencing its parent's pre-import external_id is rejected" requires rejecting exactly this file.
The existing regression tests only used a brand-new child. A new child is created through CreateTag, which runs _validate_parent, so those tests passed while the existing-child case was broken.
Problem B: the first fix rejected valid swaps
A first attempt at fixing Problem A (reverted) rejected every parent_id that named an id which one rename row gives up and another rename row takes, as happens in a swap. That also rejected a child that names the correct end-state id. For example, when tag_1 and tag_3 swap ids, tag_4 (a child of the old tag_3) correctly says parent_id: tag_1, and the first fix rejected it. As a result, two swapped parents with children could not be imported at all.
Root cause and proposed fix
Both problems come from keying the validation on the wrong condition. A row that names its parent's pre-import id is only a problem when the same file gives that id away: the file renames the parent to another id, or, in a full replace import, the file deletes the parent and renames another tag onto its id.
The fix treats such a row as a parent change, so the per-row parent validation runs for it even though its parent_id matches the parent's pre-import id. When the file renames the parent away, the validation rejects the row. The only ambiguous reading is a row whose parent_id equals the pre-import id of its own current parent while another row of the same file takes that id, as in a swap. That row is rejected too. The error names the row and the parent's new id, for example: Row 2: Parent tag tag_1 is renamed to tag_50 in this file; use tag_50 as the parent_id. When another row takes the id, the error adds a second sentence for the other possible meaning: To move under the tag that takes the id tag_1 in this file instead, import the id changes and the move as two separate files. A child that names the end-state id of its parent is accepted. When the file deletes the parent and renames another tag onto its id, a child that names that id moves under the renamed tag.
A comment in the import plan says that _validate_parent reports a reference to an id the import renames away. Before this fix the comment was wrong for a "no change" row, which never called _validate_parent. With the fix the comment holds, because such a row is now a parent change and reaches _validate_parent.
Acceptance criteria
Every criterion in this issue must hold whatever the order of the rows in the file.
- #673's scenario "a child row referencing its parent's pre-import external_id is rejected" is rejected:
- Given a taxonomy where
tag_2is an existing child oftag_1 - When the admin imports a file that renames
tag_1totag_50and keeps thetag_2row withparent_id: tag_1 - Then the Plan step rejects the import with an error that names the
tag_2row and says to usetag_50as theparent_id. With thetag_2row as the second row, the error isRow 2: Parent tag tag_1 is renamed to tag_50 in this file; use tag_50 as the parent_id. - And nothing in the taxonomy changes.
- Given a taxonomy where
- A swap of two parents with children succeeds:
- Given
tag_2undertag_1andtag_4undertag_3 - When the admin imports a full replace file (
replace=True) that renamestag_3totag_1andtag_1totag_3, withtag_2sayingparent_id: tag_3andtag_4sayingparent_id: tag_1 - Then the import succeeds
- And each child stays on the same physical parent:
tag_2is under the tag now calledtag_3, andtag_4is under the tag now calledtag_1.
- Given
- An ambiguous swap reference is still rejected:
- Given
tag_2undertag_1 - When the file swaps the ids of
tag_1andtag_3, and thetag_2row still saysparent_id: tag_1 - Then the Plan step rejects the import, because after the import
tag_1names the oldtag_3, so the row could mean "stay with my parent" or "move under the tag now calledtag_1" - And the error names the
tag_2row, says to usetag_3as theparent_id, and says how to make the other move. With thetag_2row as the third row, the error isRow 3: Parent tag tag_1 is renamed to tag_3 in this file; use tag_3 as the parent_id. To move under the tag that takes the id tag_1 in this file instead, import the id changes and the move as two separate files.
- Given
test_generate_actions_parent_id_new_id_after_earlier_rename_acceptedand thetest_generate_actions_rename_external_id_replace_skips_delete*tests continue to pass unchanged.
References
- #673, the ticket that introduced
previous_id, and its acceptance scenarios. - ADR 0010:
docs/openedx_tagging/decisions/0010-mutable-tag-external-id.rst. - Review comments on #804 that led to this issue:
- 主要言語
- Python
- スター
- 10
- フォーク
- 33
- 平均マージ
- 2日 19時間
- マージ済み PR(30日)
- 24
環境構築
- Dockerfile・Docker Compose ファイルなし
- プルリクエストのテンプレートなし
- コントリビューションガイドを読む
はじめの一歩
- issue を最後まで読み、次にプロジェクトのコントリビューションガイドを読みます。
- 着手することを issue にコメントします — 二人が同じ作業をするのを防げます。
- リポジトリをフォークし、ブランチを切って変更します。
- issue 番号を参照したプルリクエストを送ります。
openedx/openedx-core のほかの issue
-
難易度 2/5 1〜3時間 初心者へのやさしさ 75/100
openedx/openedx-core#831 ·
メンテナーはふだん 1 日以内に返信
-
難易度 2/5 1〜3時間 初心者へのやさしさ 85/100
openedx/openedx-core#827 ·
メンテナーはふだん 1 日以内に返信
-
[Follow-up BE] Hide archived tagging records in edx-platform's content_tagging app対応中かも @tbain が 2 日前に担当しました。 オープン
openedx/openedx-core#903 · 担当者 1 名 ·
メンテナーはふだん 1 日以内に返信
-
難易度 5/5 1週間以上 初心者へのやさしさ 15/100
openedx/openedx-core#902 ·
メンテナーはふだん 1 日以内に返信
-
難易度 5/5 1週間以上 初心者へのやさしさ 15/100
openedx/openedx-core#901 ·
メンテナーはふだん 1 日以内に返信
openedx/openedx-core の issue をすべて見る
似ている issue
-
難易度 2/5 1〜3時間 初心者へのやさしさ 68/100
Arekkazu/sgpmp-backend#744 ·
メンテナーはふだん 1 日以内に返信
-
agent/reviewer
難易度 2/5 1〜3時間 初心者へのやさしさ 78/100
projectbluefin/utah-packages#634 ·
メンテナーはふだん 1 日以内に返信
-
enhancement good first issue help wanted
難易度 2/5 1〜2日 初心者へのやさしさ 78/100
メンテナーはふだん 1 日以内に返信
-
難易度 2/5 1〜3時間 初心者へのやさしさ 68/100
home-assistant/core#188613 ·
メンテナーはふだん 1 日以内に返信
-
Websiteオープンgood first issue
難易度 2/5 1〜3時間 初心者へのやさしさ 91/100
TanbirRamim/open-source-radar#314 ·
メンテナーはふだん 1 日以内に返信