Identifier scheme field renders as free text; a valid ORCID is refused with 'Unknown identifier scheme.' #298
Labels
No labels
accessibility
authentication
breaking change
bug
documentation
enhancement
interface
internationalisation
observability
security
tier
1
tier
2
tier
3
tier/4
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference
Postulo/postulo#298
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
What happens
On the profile page (Your details), the identifiers' block renders the scheme input as a free-text box. Saving a valid ORCID - e.g. scheme
ORCID, value0000-0003-3947-1881(the checksum passes) - is refused with:The identifiers' block on a company page renders the same way.
Reproduced on current main (
b47674329)GETof the profile page renders the scheme field as<input type="text" name="identifiers-0-scheme" maxlength="20">- not a select, despite the help text "Choose what kind of identifier this is."orcidand value0000-0003-3947-1881validates and saves fine. Only the bare lowercase key passes; nothing on the page tells the person what it is. Typing the scheme's own name -ORCID- reachesfind()incore/identifiers.py, matches nothing, and raises theschemeValidationError.Root cause
fea7b090d("Make one registry of identifiers, and say what each one identifies", #109; shipped in v0.3.0) moved the schemes out of the model field'schoicesand into the plugin registry.PersonIdentifier.schemedeliberately lost itschoices(scheme_field()incore/identifiers.py: "Notchoices, which is what it was"), and the form now setsin
__init__(accounts/forms.py:607,jobs/forms.py:364). Django picks the widget fromchoiceswhen the field is constructed; assigningchoicesafterwards leaves the defaultTextInputin place, so the select the field had before the registry work never came back.Why the tests did not catch it
The identifier tests (
test_person_identifiers.py,test_name_if_other.py) exercise the forms from posted data only. No test asserts what the scheme field renders; a widget assertion - a select populated from the registry, scoped to the subject - would have caught this.Affected
PersonIdentifierForm,accounts/forms.py:607- Your detailsCompanyIdentifierForm,jobs/forms.py:364- company pageExpected
The scheme field renders as a select (the blank choice plus the registry's schemes for the subject), or at least accepts a scheme's display label case-insensitively. (Either way, a test that renders the page and checks the widget would keep it there.)
Environment
Postulo 0.3.0 / main
b47674329, Windows, dev settings; reproduced against a fresh in-memory database.Fixed on
bug/298(d56463974), fast-forwarded tomainand pushed.The report's diagnosis was exactly right, with a second layer underneath it: assigning
choicesafter the form is built does not change the widget Django picked from the model field, and this field is a plainCharFieldto boot — aCharField'schoicesattribute is read by nothing, not for validation and not for rendering. So the fix is not a select that inherits the choices, but a select handed them:PersonIdentifierForm(Your details) andCompanyIdentifierForm(company page) now build the choices from the subject-scoped registry view and pass them to the widget:forms.Select(choices=...).lei; a company's row offers its own set, and noorcid.:has(select option[value="other"]:checked)rule never matched a text box.Keeping it there, the gap the report named:
test_the_kind_is_a_select_of_the_schemes_that_identify_peoplerenders the profile page and asserts the control is a<select>carrying the registry's person schemes — and no company scheme.test_the_company_form_offers_a_select_scoped_to_companiesdoes the same on the company page, scoped the other way.One housekeeping note: the branch's first full-suite run failed
test_translations.py::test_the_catalogues_are_what_a_fresh_extraction_writes[postulo]— the seven lines inserted into eachforms.pymoved the source-line pointers of the strings below them. The catalogues were re-extracted in the same commit (pointers and dates only;extract --checkclean) and recompiled.Full suite after the re-extraction: 8615 passed, 63 skipped, 1 xfailed.