Your details: changing one postal address into another that is being removed crashes the save #458
Labels
No labels
accessibility
authentication
breaking change
bug
documentation
enhancement
interface
internationalisation
observability
performance
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#458
Loading…
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?
Found on 2026-09-29 in a read-only review of postulo at
95348908d, by readingBasePostalAddressFormSetagainst the model's constraints and running the case throughaccounts:profilein a test.Related to #329.
Checked again on 2026-10-01 against
mainat0a5cad587: still present.Postal addresses are unique per owner (
address_unique_per_owner), but the address formset compares a submission only with itself. It checks nothing against the database, and it saves the rows in page order. Two ordinary edits therefore get past validation and then fail at the constraint with a 500.Where:
core/postal.py:253-264(BasePostalAddressFormSet.clean, which skips removed rows and compares only the rows kept in this submission);core/postal.py:266-285(save, which calls Django'ssave_existing_objects, and that updates and deletes the existing rows in index order);core/models.py:564-568(address_unique_per_owneron(owner, comparable));accounts/views.py:186-187.What happens:
IntegrityError: UNIQUE constraint failed: core_postaladdress.owner_id, core_postaladdress.comparable. Reproduced againstaccounts:profile. The same edit with the rows in the other order saves.core/importer.py:523). If somebody edits their own address into one that a contact of theirs already has, the formset again accepts it and the save fails the same way. Reproduced withpostal.formset_for.The telephone and web link formsets do not crash on the first case, because they compare against the saved rows in
clean().Why it matters: a normal move-house edit fails, and everything else typed on Your details is lost with it. Nothing on the page suggests that splitting the change over two saves would work.
Fix:
BasePostalAddressFormSet.save, delete the rows marked for removal before updating the changed ones (deleteself.deleted_formsfirst, then call the parentsave).clean(), compare each kept row with the owner's saved addresses (PostalAddress.objects.filter(owner=...)), leaving out the row itself and the rows being removed, and report "This address is already listed." instead of letting the constraint answer.Done when
tests/test_postal_addresses.pyedits the first of two addresses into the second, removes the second in the same POST toaccounts:profile, and finds one address left and a redirect. Today it raisesIntegrityError.