Delete my account: export offered first, then everything, including the files on disk #33

Closed
opened 2026-09-05 14:04:15 +00:00 by tiagoagueda · 0 comments
Owner

Why

Raised as an open question in #22 and worth its own issue, because deletion is the one operation where a half-measure is worse than nothing. A person leaving a shared instance — or a job search that ended well — should be able to remove everything themselves, without asking an administrator.

Two facts shape it:

  • Rows cascade already. Every owned model hangs off OwnedModel.owner with on_delete=CASCADE (core/models.py), and allauth's email addresses cascade too. Deleting the user row deletes the data.
  • Files do not. Django never removes a file when the row pointing at it goes: UploadedDocument.file, RenderedDocument.file, and soon avatars (#7) and logos (#21) would stay on disk under MEDIA_ROOT, readable by nobody and deleted by nobody. A deletion that leaves a person's CV on the server has not deleted anything.

Shape

  • Settings → Your data → Delete my account (#22). The page says exactly what goes: applications, listings, companies and contacts, documents and the files behind them, reminders, tokens, connections, the account itself. It offers Export everything first, prominently.
  • Confirmation through allauth's reauthentication (the password again, or the second factor of #27), plus typing the account's email address. No email confirmation link: the person is signed in and has just proven it.
  • The deletion itself, in a service, in one transaction: collect the paths of every file the person's rows reference, delete the user (the cascade does the rows), then remove the files and any empty directories left under their media prefix — files last, because a failed transaction must leave them in place.
  • Invitations they issued are revoked; capture and API tokens cascade; connections (#11) go with their secrets. Sessions end; the browser lands on the sign-in page with one sentence.
  • Administrators: the same service behind Delete account on Server settings → People (#24), with the same file cleanup, and the rule from #24 that the last administrator cannot be deleted — by anyone, including themselves.
  • manage.py delete_account <email> for operators, with a confirmation prompt.
  • The test that matters: after deletion, every OwnedModel subclass has zero rows for that owner and no file under their prefix remains. The suite already sweeps every view for for_user(); this is the same sweep for deletion.

Classification

Enhancement. Not breaking: a new page and service; nothing changes for anyone who does not press the button.

Open questions

  1. A grace period (deactivate now, delete in 14 days) or immediate? Proposal: immediate. This is a self-hosted tool, the export is the safety net, and a "pending deletion" state is a second code path to get wrong.
  2. Should the administrator be notified when someone deletes their account? Only as a line in the People page's history (#24), not an email.
## Why Raised as an open question in #22 and worth its own issue, because deletion is the one operation where a half-measure is worse than nothing. A person leaving a shared instance — or a job search that ended well — should be able to remove everything themselves, without asking an administrator. Two facts shape it: - **Rows cascade already.** Every owned model hangs off `OwnedModel.owner` with `on_delete=CASCADE` (`core/models.py`), and allauth's email addresses cascade too. Deleting the user row deletes the data. - **Files do not.** Django never removes a file when the row pointing at it goes: `UploadedDocument.file`, `RenderedDocument.file`, and soon avatars (#7) and logos (#21) would stay on disk under `MEDIA_ROOT`, readable by nobody and deleted by nobody. A deletion that leaves a person's CV on the server has not deleted anything. ## Shape - **Settings → Your data → Delete my account** (#22). The page says exactly what goes: applications, listings, companies and contacts, documents and the files behind them, reminders, tokens, connections, the account itself. It offers **Export everything** first, prominently. - **Confirmation** through allauth's reauthentication (the password again, or the second factor of #27), plus typing the account's email address. No email confirmation link: the person is signed in and has just proven it. - **The deletion itself**, in a service, in one transaction: collect the paths of every file the person's rows reference, delete the user (the cascade does the rows), then remove the files and any empty directories left under their media prefix — files last, because a failed transaction must leave them in place. - **Invitations they issued** are revoked; **capture and API tokens** cascade; **connections** (#11) go with their secrets. Sessions end; the browser lands on the sign-in page with one sentence. - **Administrators**: the same service behind *Delete account* on Server settings → People (#24), with the same file cleanup, and the rule from #24 that the last administrator cannot be deleted — by anyone, including themselves. - **`manage.py delete_account <email>`** for operators, with a confirmation prompt. - **The test that matters**: after deletion, every `OwnedModel` subclass has zero rows for that owner *and* no file under their prefix remains. The suite already sweeps every view for `for_user()`; this is the same sweep for deletion. ## Classification Enhancement. Not breaking: a new page and service; nothing changes for anyone who does not press the button. ## Open questions 1. A grace period (deactivate now, delete in 14 days) or immediate? Proposal: immediate. This is a self-hosted tool, the export is the safety net, and a "pending deletion" state is a second code path to get wrong. 2. Should the administrator be notified when someone deletes their account? Only as a line in the People page's history (#24), not an email.
tiagoagueda added this to the 0.2.0 milestone 2026-09-05 14:04:15 +00:00
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Reference
Postulo/postulo#33
No description provided.