Justian: Technical Case Study
- Role: Designer, architect, and sole developer
- Status: Live personal photography platform, under continuing development
- URL: justian.narian.org.uk
- Stack: Django · PostgreSQL · Docker Compose · Nginx · pyvips · Matomo · Darktable workflow integration · AI-assisted editorial review
Overview
Justian is a personal photography publishing platform built around self-owned photographic sets.
It supports the workflow from finished image export through to publication: draft set creation, multiple-image upload, derivative generation, metadata handling, image ordering, preview, publication, unpublication, selected work, controlled keywords, social-share preparation, and privacy-conscious statistics.
The purpose is practical: photographs remain the principal content, while the platform provides enough structure to publish them repeatedly with stable URLs, useful supporting text, browsable archives, feeds, sitemaps, and share-ready pages.
At a glance
- Purpose: Turn edited photographs into stable, shareable published sets.
- Users: A solo photographer and public visitors.
- Key workflows: Upload, derivative generation, editing, preview, publication, selected work, keyword exploration and social-share preparation.
- What it demonstrates: Django workflow design, image handling, publication state, privacy-conscious metadata handling and AI-assisted editorial review.
Why It Was Built
Justian was built to support a practical personal photography workflow: take photographs, edit them, select the stronger images, and publish them somewhere stable.
The friction is not usually the image work. It is the supporting material: titles, captions, alt text, keywords, set introductions, archive structure and share text. Those words make the work easier to find, understand and share, but adding them consistently can become a chore.
The site provides a controlled way to turn finished photographs into published sets with stable URLs, useful metadata, browsable archives, selected work, keyword exploration and share-ready material that links back to the original page.
It also supports Ian’s public presence as a photographer. That matters for the photography itself, and it strengthens Camerawalk: a walk leader asking others to take part, upload images and share results should have a credible photographic presence of his own.
The design principle is simple: the photographs come first, and the system reduces the friction of publishing them properly.
Technical Stack
Application Framework: Django
Django was chosen because the project needs clear, model-driven application code rather than a theme-led CMS or a PHP-based publishing stack.
It had also become a known and trusted platform from earlier work: robust enough for production, easy enough to code quickly, and structured enough to keep the application simple. The core objects are photo sets, photographs, generated derivatives, keywords, selected-work references, social-publication records, contact submissions, processing jobs, and publication states.
The project benefits from Django’s built-in admin, forms, permissions, URL routing, migrations and server-rendered templates. The public site does not need to be a single-page application, and it remains browsable without JavaScript.
Database: PostgreSQL
PostgreSQL stores the structured publication state: sets, photographs, keywords, selected-work records, social-share state, contact submissions, image-processing jobs, and aggregate statistics.
The site depends on database-backed consistency. Draft and unpublished material must not leak through the home page, archive, keyword pages, feeds, sitemaps, selected work, or public metadata. That is a data-modelling problem as much as a presentation problem.
Deployment: Docker Compose
The platform runs as a Docker Compose deployment with separate application, database, and worker responsibilities.
The web process serves Django. The worker handles image-processing and deferred jobs. PostgreSQL provides persistence. Runtime configuration is held outside the repository, and production media paths distinguish between protected originals and public derivatives.
Image Processing
Finished exports, usually from Darktable, are uploaded as source images. Originals are stored privately and are never served directly.
The system generates public derivatives for responsive display, previews, social cards, and share packs. Public derivatives are generated separately from the uploaded originals, with unnecessary metadata removed before publication. This avoids exposing original filenames or private workflow metadata through public image URLs.
Image processing runs outside the request path through a worker process. Jobs can be retried, failures can be inspected, and publication can depend on required derivatives existing before public exposure.
Publishing Workflow
A photographic outing or project instalment is published as a PhotoSet.
The workflow is:
- Create a draft set.
- Upload finished images.
- Generate safe public derivatives.
- Edit titles, captions, alt text and metadata.
- Apply controlled keywords.
- Select lead image and selected-work candidates.
- Preview the unpublished set.
- Validate publication requirements.
- Publish atomically.
- Generate feeds, metadata, sitemaps and social-share material.
A published set appears consistently across public surfaces. Unpublishing removes it from the home page, archive, Explore, selected work, feeds and sitemaps without deleting the underlying editorial work.
Privacy and Security
Privacy is a design constraint, not a paragraph added after implementation.
Key decisions include:
- Protected originals are not publicly served.
- Public derivatives are generated separately.
- Unnecessary metadata is removed from public images before publication.
- Original filenames are not exposed.
- Public discovery features use the same publication rules as public pages.
- Drafts and previews are excluded from feeds and sitemaps.
- Contact handling is minimal and rate-limited.
- Self-hosted Matomo is optional and configured to avoid advertising-style tracking.
- Third-party pixels, embedded social widgets and advertising trackers are avoided.
- Public share controls remain ordinary links rather than embedded third-party widgets.
The site’s built-in view statistics are aggregate and intended to answer simple editorial questions. Operational logs are handled separately from public editorial statistics.
AI-Assisted Editorial Tooling
AI assistance is present as private editorial support, not public automation.
The system supports owner-controlled assistance for titles, introductions, captions, keywords, sequencing, exclusion suggestions and private editorial review. The AI worker uses public-safe derivatives and runs outside publication. Suggestions are reviewable and must be accepted by the owner before they affect public content.
AI-generated text is not published automatically. AI failure does not block editing or publication. Imported tags are filtered so only trusted editorial context is used; camera workflow tags and raw EXIF clutter are not treated as meaningful prompt material.
The project may later be opened as a reusable AI-assisted photography portfolio and publishing system. The core principle remains unchanged: AI can assist the editor, but it must not become an unsupervised publisher.
Operational Lessons
The project demonstrates a set of practical production concerns that do not show up in a screenshot:
- Docker service separation.
- PostgreSQL-backed state and jobs.
- Production configuration outside Git.
- Private and public media storage separation.
- Static and derivative routing.
- Worker failure and retry behaviour.
- Publication validation.
- Public metadata handling.
- Feed and sitemap consistency.
- Contact-form rate limiting and retention.
- Backup and recovery planning.
- Deployment behind an existing reverse proxy.
The real work is less “make a page showing photos” and more “make the full publication lifecycle reliable enough to use repeatedly without manual reconstruction each time.”
What Was Deliberately Not Built
Justian is deliberately not a social network, public upload platform, booking system, comments-and-likes machine, general-purpose CMS, or mobile app. Those omissions protect the shape of the product.
Summary
Justian is a full-stack Django photography publishing platform built to support durable, self-owned photographic publication.
It demonstrates:
- Django application design around real editorial workflows;
- PostgreSQL-backed publication state;
- Docker Compose deployment;
- asynchronous image-processing workers;
- protected original storage and generated public derivatives;
- controlled handling of originals, derivatives and public metadata;
- controlled keyword exploration;
- selected-work curation;
- feed, sitemap and social metadata generation;
- privacy-conscious analytics and contact handling;
- AI-assisted editorial review under human control;
- operational discipline around deployment, storage and recovery.
The central argument is that a serious personal photography site does not need to become a social network. It needs clear ownership, stable publication, careful image handling, privacy by design, and enough operational structure for the work to remain available over time.