Tracking
Linear: TYS-191 — Add avatar field to CustomUser
Goal
Add user avatar support on accounts.CustomUser (Django AbstractUser subclass) so profiles can display an image across API and UI.
Context
CustomUser currently has email, role, and bio; no image/avatar field yet. Profile is a separate one-to-one model — decide whether avatar lives on CustomUser only (this issue) or is duplicated; default assumption: avatar on CustomUser as the canonical source.
Scope (suggested)
- Model: Add nullable avatar field (
ImageField with upload path, or URLField if using external URLs only).
- Migration:
makemigrations / apply; consider default for existing users (null/placeholder).
- Storage: Align with existing media settings (
MEDIA_ROOT / production object storage if applicable).
- Admin: Expose on
CustomUserAdmin (list/display/fieldsets as appropriate).
- API: Expose read (and optionally write) on relevant Ninja routes — e.g. user/profile serializers — with contract tests for shape and auth (401/403).
- Frontend: Show avatar in header/profile where user identity is shown; optional upload UI if write path is in scope.
- Tests: Unit tests for model/API; Robot or API tests if endpoints change.
Out of scope (unless expanded)
- Image processing pipeline (resize/crop) — can be a follow-up.
- Gravatar fallback — optional nice-to-have.
Acceptance criteria
Tracking
Linear: TYS-191 — Add avatar field to CustomUser
Goal
Add user avatar support on
accounts.CustomUser(DjangoAbstractUsersubclass) so profiles can display an image across API and UI.Context
CustomUsercurrently hasemail,role, andbio; no image/avatar field yet.Profileis a separate one-to-one model — decide whether avatar lives onCustomUseronly (this issue) or is duplicated; default assumption: avatar onCustomUseras the canonical source.Scope (suggested)
ImageFieldwith upload path, orURLFieldif using external URLs only).makemigrations/ apply; consider default for existing users (null/placeholder).MEDIA_ROOT/ production object storage if applicable).CustomUserAdmin(list/display/fieldsets as appropriate).Out of scope (unless expanded)
Acceptance criteria