Skip to content
raigonlabPublic

About

Full-stack gallery management platform for artists — built with Django and PostgreSQL

Resources

Stars

1 star

Watchers

0 watching

Forks

Repository files navigation

RaigonOS

Developer: Railson Gonçalves (raigonlab)

GitHub commit activity GitHub last commit GitHub repo size


Project Introduction and Rationale

RaigonOS is a full-stack gallery management platform built with Django and PostgreSQL, allowing an artist to catalogue, organise and present their art Collections and individual Artworks online, with full create, read, update and delete control over their own content.

It is a conceptual continuation of raigon — a static, single-page gallery built with HTML, CSS and JavaScript only — but rebuilt entirely from scratch as a dynamic, database-backed application. No code from that project is reused; only the domain (an artist's digital gallery) carries over, as permitted by the assessment brief.

Target audience

  • Primary — the site owner (artist): needs a simple, dependable tool to manage their portfolio (add/edit/remove Collections and Artworks) without touching code.
  • Secondary — visitors: collectors, peers and the public browsing the published gallery.

Value provided

  • The artist gets a private, authenticated back office to manage their public-facing gallery content directly.
  • Visitors get organised, structured access to an artist's body of work, grouped into Collections.

Key technical decisions

  • Django + PostgreSQL, with two core related models — Collection and Artwork (one-to-many) — kept deliberately minimal for the MVP.
  • Authentication restricts all create/edit/delete actions to the authenticated owner; the public gallery remains read-only for visitors.
  • Configuration (SECRET_KEY, DEBUG, DATABASE_URL, allowed hosts) is entirely environment-variable driven, so the same codebase runs unchanged locally (SQLite) and in production (Render + PostgreSQL).
  • Uploaded images are stored on Cloudinary in production (switched on via CLOUDINARY_CLOUD_NAME/CLOUDINARY_API_KEY/CLOUDINARY_API_SECRET), since Render's own filesystem is wiped on every deploy and cannot be used for persistent user uploads.

Long-term vision (explicitly out of scope for this MVP)

Beyond this submission, RaigonOS is conceived as something bigger than a single-artist portfolio tool: a secure, lifelong archive where any artist can catalogue their entire body of work as it grows — a robust visual record of their artistic journey, not just a gallery for the present. None of the following is built or planned for this cycle; it's recorded here to document the reasoning behind decisions like the extensible owner-based data model.

  • A record of the artist's evolution. Because Collections and Artworks are timestamped and organised chronologically, the platform is naturally positioned to show how an artist's style and body of work developed over their career, not just a snapshot of current pieces.
  • Public exhibition beyond the site itself. Artwork catalogued here could be surfaced into real public spaces — museum displays, electronic street billboards, screens on public transport — in the spirit of how platforms like Unsplash license imagery for public use, but for original, human-made art rather than stock photography.
  • Authenticity and provenance. As AI-generated imagery becomes harder to distinguish from human work, a documented, timestamped record of an artwork's creation — tied to a verified artist — becomes valuable in itself: proof that a piece is genuinely human-made. This could evolve into part of the platform's core value, not just a cataloguing convenience.
  • Multi-artist mode. The data model is already shaped for this (Collection.owner is a User foreign key), but public multi-artist sign-up is explicitly not part of this submission — see Features below for the full MoSCoW breakdown of what is and isn't included now.
  • An integration layer, not a walled garden. Rather than being the only place an artist's work lives, the catalogued archive could be exposed (via an API or embeddable widget) so other platforms — social media, online stores, third-party portfolio sites — can plug into it and reuse the same catalogued data, instead of the artist re-uploading their work separately everywhere.
  • The public gallery as an exhibition in motion. The public side is meant to be experienced, not browsed: artworks drift slowly through a dark, mostly empty viewport, like objects floating in an exhibition space, with almost invisible controls. Movement can respond to scroll, pointer/touch or discreet navigation, but the visitor can also simply stay and contemplate. The conceptual reference is an earlier Raigon Lab project, raigon-mmxi — an inspiration for the motion and atmosphere, not something to copy; it will be redesigned in the RaigonOS visual language (deep black, soft contrast, subtle type, very restrained gold). A first version now lives on the public home (see Existing Features); the rest of the public site (Collections, Collection and Artwork pages) still uses the earlier cream design and is the next step towards the same atmosphere.
  • A way in for street artists. Street art is ephemeral — pieces are painted over and documentation ends up scattered across phone galleries and feeds. A free, low-barrier place to catalogue and organise that work (a Collection per wall, series or crew project) is a natural audience for the platform.
  • A toolbox and a storefront. Beyond the archive, optional "toolbox" modules could reuse the same Collections and Artworks data — the first being a storefront that turns catalogued work into a shop (originals, prints, digital files) without re-uploading anything. Payments is shown as "Coming soon" in the dashboard sidebar, next to The Maker, to make that direction visible.
  • The Maker. A dedicated page for the person behind the work, in keeping with a system about documenting and preserving work rather than building a public persona. Shown as "Coming soon" in the dashboard sidebar so the direction is visible without spending development time on it.

The guiding principle behind all of the above: a platform artists can trust with their life's work, without extractive fees or unnecessary bureaucracy standing between them and presenting it to the world.

Comparable platforms (researched for direction, not copied from)

  • Artwork Archive — art inventory software artists use to catalogue their full body of work: location, exhibition history, sales, condition — closest existing parallel to the "lifelong record" idea above.
  • Niio — streams digital art to screens (TVs, business/public displays), the closest existing model for the public exhibition idea, though its catalogue includes AI-generated art, which runs counter to this project's "verified human-made" principle.
  • Content Credentials / C2PA — an industry standard (backed by Adobe, Microsoft, Google, Meta, the BBC and 500+ others) for attaching verifiable creation/edit history to a file, distinguishing authentic from synthetically generated content. The technical model closest to the authenticity idea above.
  • No existing platform combines all of the above (lifelong archive + public exhibition + authenticity + third-party integration) in one place — as far as this research found, that combination is an open gap rather than something already solved elsewhere.

UX

The 5 Planes of UX

1. Strategy

Purpose

  • Give an artist full control over publishing and organising their digital gallery, without needing to write code.
  • Present that gallery to the public in a clean, distraction-free way that puts the artwork first.

Primary User Needs

  • (Owner) Add, edit and remove Collections and Artworks quickly and safely.
  • (Owner) Be confident that only they can modify their content.
  • (Visitor) Browse Collections and view individual Artworks with clear detail (title, medium, year, description).

Business Goals

  • Prove full CRUD competency against a real relational schema (Collection ↔ Artwork), satisfying the Back End Development unit's core requirements.
  • Deliver a genuinely usable tool, not just a coursework demo — one the developer can keep using for their own gallery after submission.

2. Scope

Features (MVP — must-have)

  • Authentication: signup, login, logout.
  • Full CRUD on Collection (owner only).
  • Full CRUD on Artwork, linked to a Collection (owner only).
  • Public, read-only pages listing Collections and their Artworks.

Features (documented, not built this cycle — see MoSCoW table)

  • Invitation system for private/invite-only Collections.
  • Direct contact/inquiry form.
  • Multi-artist public sign-up.
  • Search and filtering.

Content Requirements

  • Collection: title, description, cover image, status.
  • Artwork: title, image, medium, year, description.

3. Structure

Information Architecture

  • / — public gallery home: every published Artwork, across all Collections (the default a visitor lands on)
  • /collections/ — browse published Artworks grouped by Collection instead
  • /collection/<slug>/ — Collection detail (its Artworks)
  • /artwork/<id>/ — Artwork detail
  • /dashboard/ — owner-only management area: list of the owner's Collections
  • /dashboard/collections/<slug>/ — one Collection's own dashboard page (its Artworks; View/Edit/Archive as a quiet action row, Delete in a "..." overflow menu)
  • /dashboard/archive/ — owner's archived Collections (kept on record, never public); archive/unarchive are single-click POST actions
  • /dashboard/artworks/ — owner's Artworks across all Collections
  • /dashboard/collections/bulk/, /dashboard/artworks/bulk-delete/ — POST-only endpoints behind the Select mode's bulk actions
  • /dashboard/artworks/<id>/ — Artwork preview (full page, not a modal), with Previous/Next through the rest of its Collection
  • /accounts/login/, /accounts/logout/, /accounts/signup/

Two surfaces

RaigonOS is built as two separate surfaces today:

  1. Public artist gallery — what visitors see of an artist's published work. Today a conventional page (/, /collections/, /collection/<slug>/, /artwork/<id>/); planned to become the moving exhibition described under the long-term vision. The dashboard's "Public Gallery" link opens this surface.
  2. Private dashboard — where the artist manages Collections, Artworks and the Archive (/dashboard/...).

A third surface — a RaigonOS landing page introducing the product/platform itself, separate from any one artist's gallery — is explicitly out of scope for this submission and tracked as backlog: see Issue #19.

User Flow

Two flows, one per audience. Visitors never need an account; the owner only ever leaves the dashboard deliberately, through its "Public Gallery" link.

flowchart TD
    V([Visitor]) --> H["Gallery home /<br/>Exhibition or Grid view"]
    H --> C["Collections<br/>/collections/"]
    C --> CD["Collection detail<br/>/collection/#lt;slug#gt;/"]
    H --> AD["Artwork detail<br/>/artwork/#lt;id#gt;/"]
    CD --> AD
    AD -- "Previous / Next" --> AD

    O([Owner]) --> L["Log in / Sign up"]
    L --> D["Dashboard — Collections<br/>/dashboard/"]
    D --> CM["Collection page<br/>/dashboard/collections/#lt;slug#gt;/"]
    D -- "New Collection" --> CF["Collection form"]
    CM -- "Edit" --> CF
    CM -- "Add Artwork" --> AF["Artwork form"]
    CM -- "Delete" --> CDEL["Confirm delete page"]
    CM -- "Archive" --> AR["Archive<br/>/dashboard/archive/"]
    AR -- "Restore (to Draft)" --> CM
    D --> AL["All Artworks<br/>/dashboard/artworks/"]
    AL --> AM["Artwork preview<br/>/dashboard/artworks/#lt;id#gt;/"]
    AM -- "Edit" --> AF
    AM -- "Delete" --> ADEL["Confirm delete page"]
    CF & AF & CDEL & ADEL -- "success message" --> D
    D -- "Public Gallery" --> H
Loading
  • Visitor: lands on the gallery home, which shows every published Artwork as a drifting exhibition, or as a plain grid (switchable from the site menu). From there they can browse by Collection, and step through a Collection one Artwork at a time with Previous/Next (or the arrow keys) without going back to a list.
  • Owner: logs in and lands in the dashboard. Each Collection has its own page, where the owner adds, edits and deletes its Artworks. A new Collection starts as a Draft; the owner publishes it when it's ready. Every create, update and delete returns to the dashboard with a success message. Deletes always go through a confirmation page first. Archived Collections move to the Archive page, and restoring one puts it back to Draft, never straight to Published.

The screens along each flow are shown in Wireframes and, as built, in the responsiveness screenshots in TESTING.md.


4. Skeleton

Two passes per surface (see Wireframes below): a hand-drawn sketch first, covering the full screen set — the public gallery's four core screens (Grid, Collections, Exhibition and the shared top navigation) and the dashboard's two list layouts (thumbnail/ grid and list view) plus its shared header controls — then higher-fidelity wireframes for the key individual screens of each surface. Produced to document the structure already settled on through development, rather than drafted upfront before templates existed.


5. Surface

Visual Design (implemented)

  • Warm, cream gallery atmosphere — white cards and form elements float on a soft cream background, rather than a stark white/black contrast.
  • Large, consistently rounded corners on cards, images and form elements; fully pill-shaped buttons.
  • Generous whitespace between elements.
  • A serif display face (headings, with italic used for emphasis within a heading) paired with a clean sans-serif for body text and UI.
  • Small, uppercase, letter-spaced "eyebrow" labels above page titles (e.g. "Collection", "Dashboard") for wayfinding.
  • The RaigonOS logo mark: a bold "R" monogram (simplified from an earlier circular-seal design, which read as illegible noise at the sizes the logo is actually displayed).
  • Tagline: Create · Collect · Legacy — mirroring the actual user flow (create an Artwork, collect it into a Collection, build a lasting portfolio).

Visual language is inspired by raigon.ch — for aesthetic direction only; no content, copy, or functionality from that site is used here.

Dashboard (owner-only) visual system — deliberately distinct from the public gallery above

  • App-like rather than editorial: a persistent left sidebar (Collections, All Artworks), a top bar with a breadcrumb, and a small hand-drawn line-icon set (in the spirit of Notion/Linear/macOS) instead of text buttons.
  • Light by default — the same cream palette as the public site — with an explicit dark-mode toggle, remembered per browser. The public gallery itself has no dark mode; only the dashboard does.
  • Sidebar and breadcrumb are present on every dashboard screen, including the Artwork preview page — nothing (not even a "quick look") ever covers or hides them, so the owner always knows which workspace they're in and can navigate away at any time.
  • Grid/List is one underlying list rendered two ways (CSS only, same markup), not two different interfaces — the owner's choice is remembered per browser too.

Colour Scheme

Warm, neutral palette — cream background with white cards, so elements feel like they're placed on the page rather than boxed in:

Token Value
Background #f7f4ee
Surface (cards, inputs) #ffffff
Text #121212
Muted text / labels #6b6657
Border #e6e1d6

Accessibility note: every text colour pairing above meets WCAG AA (4.5:1) against both the background and surface colours. The muted tone was deliberately darkened from an earlier, lighter draft (#8a8578, 3.35:1) after checking contrast ratios directly, since the lighter version failed AA for body-sized text. The one deliberate exception is the Border colour against the background (~1.2:1) — it's a decorative divider, not a text colour, and card/input boundaries are primarily conveyed through the white-surface-on-cream background contrast rather than the border line itself.


Typography

  • Playfair Display — used for page headings, including an italic weight for emphasis within a heading (e.g. "My Collections"), on public pages only.
  • Inter — used for body text, navigation, forms and UI labels everywhere, and for headings in the dashboard (kept sans-serif there to read as an app, not a gallery page). A light (300) weight is used for the small captions under public gallery cards.

Both are sourced from Google Fonts. The pairing gives the same editorial, gallery-catalogue feel as the raigon.ch reference: a confident serif voice for titles, a clean sans-serif for everything functional.


Wireframes

Two passes per surface: a hand-drawn sketch covering the full screen set first, then higher-fidelity wireframes for the key individual screens.

Public gallery

Sketch — Grid, Collections, Exhibition and the shared top navigation:

Public gallery sketch

Exhibition (free view) with the site menu open:

Exhibition wireframe

Grid view:

Grid wireframe

Dashboard

Sketch — thumbnail/list layouts and the shared header controls:

Dashboard sketch

Collections list, with one row expanded to show its Artworks inline:

Collections list wireframe

A single Collection's own page (its Artworks):

Collection detail wireframe


User Stories

Target Expectation Outcome
As the site owner I want to sign up and log in securely So only I can manage my gallery content
As the site owner I want to create a Collection So I can organise my Artworks by theme or series
As the site owner I want to edit or delete a Collection So I can keep my gallery accurate and current
As the site owner I want to add an Artwork to a Collection So visitors can see it presented in context
As the site owner I want to edit or delete an Artwork So I can correct mistakes or retire pieces
As a visitor I want to browse public Collections So I can view an artist's body of work
As a visitor I want to view an Artwork's detail So I can see its title, medium, year and description
As a visitor I want the site to work on any device So I have a consistent experience on mobile and desktop

Full backlog (including could-have / won't-have items) is tracked as GitHub Issues using MoSCoW prioritisation — see Agile Development Process.


Features

Existing Features

Public gallery

  • Home page (/) lists every published Artwork across all Collections in a uniform 4:5 portrait grid (1/2/4 columns depending on screen width) — the default a visitor lands on. It is the artist's storefront, so the whole page is deep black and opens as an exhibition: three rows of the published artworks, in random order, drifting slowly past each other in a mostly empty room, with the ones far from the centre softened, faded and shrunk for depth. Drag, swipe or scroll to move them, or just watch. There are no controls on the artwork: everything lives in one site menu, a single round button fixed at the top-right of every public page (exhibition, grid, Collections, Artwork pages). It opens a panel with the exhibition tools (pause/play, a direction switch — rows drifting sideways, or columns falling top to bottom — and full screen, shown only while the exhibition is on screen), a light/dark theme switch, the views (Exhibition, Grid, Collections) and the account links (Dashboard when signed in; Log in / Sign up otherwise). Theme and direction are remembered in the browser. The motion is a small vanilla JavaScript file (static/js/exhibition.js) whose concept follows an earlier Raigon Lab project, raigon-mmxi, rewritten for RaigonOS. Without JavaScript, or after choosing Grid, the page is the plain grid, so every published artwork is always reachable.
  • /collections/ lists published Collections instead, for browsing by series rather than a flat feed. It is one Collection per row, each with its cover, title, how many artworks it holds, the year (or span of years, taken from its artworks) and a short note (the Collection's description, shortened), so a visitor knows what they are about to open.
  • Collection and Artwork detail pages.
  • A persistent path bar fixed to the bottom of every public page except the home (Gallery / Collections / <Collection> / <Artwork>) shows exactly where you are, in the spirit of the Finder path bar — deliberately not an inline breadcrumb, which shifted page content between pages of different depth. The home has none: it is the root, so the bar would only repeat the page you're already on.
  • The public header is deliberately bare: just the logo. Everything else (views, theme, Dashboard / Log in / Sign up) is in the site menu, one button at the top-right, so it is always in the same place. The public gallery pages are deep black by default with a light (cream) option; Log in and Sign up keep the cream look. The dashboard has its own shell and no site menu.
  • Artwork detail: image and metadata/description side by side on wider screens (image capped at 70vh so it never dominates the page). Previous / next arrows (and the left / right keys) step through the Collection's artworks, with a "2 / 6" position; on narrow screens they become two inline links.
  • There is no page footer: the slogan and copyright line added nothing to a gallery whose pages should be the work itself.

Authentication

  • Signup, login, logout, with every mutating view behind @login_required and an ownership check.

Dashboard (owner-only)

  • Full CRUD on Collection and Artwork, always scoped to request.user — editing or deleting someone else's content 404s rather than 403s.
  • A dedicated page per Collection (/dashboard/collections/<slug>/) showing just its Artworks — reached by clicking anywhere on the Collection's card, not just a small icon.
  • All Artworks — every Artwork the owner has, across Collections, flattened into one list.
  • Archive — a third Collection status alongside Draft and Published, for work stored in the catalogue but never published. Archived Collections disappear from the main Collections list and from the public site, and live in their own sidebar section; restoring one returns it to Draft (never straight to Published, so nothing goes public without a deliberate re-publish).
  • Artwork preview — a full page (not a modal) showing one Artwork large with its metadata, plus Previous/Next links to browse the rest of its Collection without returning to the list. The Escape key and the Collection link in the breadcrumb both lead back to the Collection.
  • Title search (?q=, server-rendered, no JS) on Collections, All Artworks, and within a single Collection.
  • Grid/List toggle, remembered per browser, available everywhere Artworks or Collections are listed — both are the same underlying list, rendered two ways in CSS. List view is Finder-style: borderless rows that alternate between the page colour and a faint tint (zebra striping), so no outline competes with the artwork; Grid view keeps its cards.
  • Bulk management — a "Select" button next to the search box turns the current list (Collections, All Artworks, or one Collection's Artworks) into selectable cards with a contextual action bar: Move to Draft / Publish / Archive for Collections, Delete for both. Deleting goes through the usual server-rendered confirmation page (no JS confirm dialog), and every selected id is re-checked against the logged-in owner, so other users' items are silently ignored. Artworks have no status of their own (it comes from their Collection), so Draft/Publish apply to Collections only.
  • Light/dark theme toggle for the dashboard (light by default, matching the public site's warm palette). The public gallery has its own theme switch in the site menu (dark by default, so the artwork stands out). Each choice is remembered separately in the browser. Three tiny scripts are the only JavaScript kept inline: two in the <head> that apply a saved theme (base.html, dashboard_base.html), and one placed right after the dashboard's list (_view_init_script.html) that applies a saved Grid/List preference. This is a deliberate exception to keeping JS in external files linked at the end of <body>. Each one has to run before the page is first painted, otherwise a returning user sees the default theme or layout flash before switching to their saved one. Everything else lives in static/js/.
  • A persistent left sidebar and a breadcrumb in the top bar are present on every dashboard screen, including the Artwork preview — nothing ever hides them.
  • A small hand-drawn SVG icon set replaces text buttons for repeated row actions, keeping rows usable on small screens. Artwork cards stay image-first: they show no persistent Edit/Delete icons, only a subtle "..." overflow menu (Edit artwork / Delete artwork) revealed on hover or focus.
  • The Edit Artwork and Edit Collection forms show the current image as a thumbnail (a custom ImagePreviewInput widget) instead of Django's raw "Currently: path" text, so the owner can see what they are replacing.
  • The public home opens with the project's sentence as its centrepiece — large editorial type, no stock imagery — plus one line on the problem it solves (work scattered across drives, phones and feeds). Public Artwork/Collection cards are frameless: no box around the work, just the image and a quiet caption, so the art is what you see.

Planned Features (MoSCoW)

Priority Feature
Must-have Django project, PostgreSQL and initial deployment
Must-have Collection model with full CRUD (owner only)
Must-have Artwork model with full CRUD (linked to Collection)
Must-have Authentication: login, logout, signup and ownership restriction
Must-have Public gallery pages: browse Collections and Artworks
Should-have Templates and CSS: warm editorial identity (public) plus a separate app-like dashboard system (sidebar, path bar, icons, light/dark)
Should-have Title search across Collections and Artworks
Could-have Invitation system for private Collections
Could-have Contact/inquiry form for direct messages to the artist
Won't-have (this cycle) Multi-artist public sign-up (marketplace mode)
Could-have Public home as a moving exhibition (built for the home page; other public pages still to follow)
Won't-have (this cycle) "The Maker" profile page (shown as "Coming soon" in the sidebar)
Won't-have (this cycle) Street-artist onboarding, toolbox and storefront
Won't-have (this cycle) Advanced filtering (by medium, year, status) beyond title search

Data Schema

Two related models, deliberately kept minimal for the MVP: Collection belongs to a User (owner), and Artwork belongs to a Collection — a one-to-many relationship in each case. Deleting a Collection cascades to delete its Artworks.

erDiagram
    USER ||--o{ COLLECTION : owns
    COLLECTION ||--o{ ARTWORK : contains

    USER {
        int id PK
        string username
        string email
        string password
    }

    COLLECTION {
        int id PK
        int owner_id FK
        string title
        string slug
        text description
        image cover_image
        string status
        datetime created_at
        datetime updated_at
    }

    ARTWORK {
        int id PK
        int collection_id FK
        string title
        image image
        string medium
        int year
        text description
        int display_order
        datetime created_at
        datetime updated_at
    }
Loading

Collection

Field Type Notes
owner ForeignKey → User on_delete=CASCADE; who manages this Collection
title CharField
slug SlugField (unique) Auto-generated from title; used in public URLs
description TextField Optional
cover_image ImageField Optional; stored on Cloudinary in production
status CharField (choices) draft (owner-only, not ready yet), published (public) or archived (kept on record in the owner's Archive section, never public)
created_at / updated_at DateTimeField Auto-managed

Artwork

Field Type Notes
collection ForeignKey → Collection on_delete=CASCADE
title CharField
image ImageField Required; stored on Cloudinary in production
medium CharField Optional, e.g. "Digital painting"
year PositiveIntegerField Optional
description TextField Optional
display_order PositiveIntegerField Controls ordering within a Collection
created_at / updated_at DateTimeField Auto-managed

User is Django's built-in auth model — no custom user model was needed for this domain.


Security

  • Secrets — SECRET_KEY, DATABASE_URL, Cloudinary credentials and all other secrets are read from environment variables, never hardcoded. .env (local secrets) is listed in .gitignore and has never been committed; .env.example documents the required keys with empty/placeholder values only.
  • DEBUG is False in production, confirmed by two real incidents during development: with it correctly off, a misconfiguration once produced only a generic error page rather than a stack trace; separately, the DEBUG environment variable on Render was later found set back to True (rendering Django's technical error page, URLconf and all, on every 404) and corrected (see TESTING.md for both accounts).
  • SECRET_KEY is env-var-only in every commit that matters, but the very first scaffold commit (4a73b9f) contains Django's auto-generated django-insecure-... placeholder from startproject — never the real production key, which has only ever lived in Render's environment variables, but still technically a key sitting in git history. Left as-is rather than rewriting commit history this close to submission, which risks breaking the granular commit trail the assessment itself asks to see.
  • Authentication & ownership — every create/edit/delete view is behind Django's @login_required. Editing or deleting another user's Collection or Artwork returns 404 Not Found rather than 403 Forbidden, so a logged-in user can't even confirm that another user's private content exists. Covered by automated tests (DashboardPermissionTests).
  • Passwords are never stored in plain text — Django's default PBKDF2 password hashing is used unchanged.
  • CSRF protection is active project-wide via Django's CsrfViewMiddleware (enabled by default, never disabled); every form includes {% csrf_token %}, including the logout action, which is a POST rather than a plain link.
  • ALLOWED_HOSTS is restricted to the actual production hostname, not left open — this was verified the hard way when a typo in it caused every production request to be rejected (see TESTING.md).
  • File uploads are validated as real images by Django's ImageField (backed by Pillow) before being accepted, and are stored on Cloudinary rather than the app server's own disk.

Tools & Technologies

  • Python
  • Django
  • PostgreSQL (production) / SQLite (local development)
  • HTML5
  • CSS3 (custom)
  • JavaScript
  • Gunicorn, WhiteNoise (production serving)
  • Pillow (image handling)
  • Git & GitHub
  • Render (deployment)
  • Google Fonts

Agile Development Process

GitHub Issues are used to plan and track development, prioritised with MoSCoW labels (must have, should have, could have, wont-have). All eight must-have issues (project setup; the Collection and Artwork models with CRUD; authentication; the public gallery pages; visual identity, responsive layout and custom error pages; password reset; and this README itself) are closed, matching the features actually shipped; should/could/won't-have issues record everything considered beyond the MVP, including the long-term ideas discussed under Rationale.

Issues board →

Project board → — the same 20 issues tracked on a Kanban-style board (Todo / In progress / Done).


Testing

Full manual and automated testing procedure, plus a log of bugs found and fixed during development, is documented in TESTING.md.


Deployment

Live Website

Deployed on Render, with a managed PostgreSQL instance in the same region.

Deployment steps:

  1. Create a PostgreSQL instance on Render and copy its Internal Database URL.
  2. Create a Web Service on Render, connected to this GitHub repository (main branch).
  3. Set the Build Command:
    pip install -r requirements.txt && python manage.py collectstatic --noinput && python manage.py migrate
    
  4. Set the Start Command:
    gunicorn raigonos.wsgi:application
    
  5. In the Web Service's Environment settings, set:
    • SECRET_KEY — generated via Render's own secure value generator
    • DEBUG=False
    • ALLOWED_HOSTS — the Render service hostname (e.g. raigonos.onrender.com)
    • CSRF_TRUSTED_ORIGINS — https://raigonos.onrender.com
    • DATABASE_URL — the Internal Database URL from step 1
    • ARTIST_NAME — optional; the name in the home page's welcome line (defaults to the site owner's)
    • CLOUDINARY_CLOUD_NAME, CLOUDINARY_API_KEY, CLOUDINARY_API_SECRET — copied individually from your Cloudinary dashboard's Account Details. Required so uploaded Collection/Artwork images persist — Render's filesystem is wiped on every deploy, so without these, uploaded images work until the next deploy and then disappear.
  6. Deploy. Render builds the app, runs migrations, and starts Gunicorn automatically on every push to main.

A Procfile is also kept at the repo root (web: gunicorn raigonos.wsgi:application, release: python manage.py migrate) for Heroku-style platforms that read it directly. Render's own Web Service doesn't use it — it runs the Build/Start Commands set in step 3–4 above instead — but the Procfile documents the same two commands in the platform-agnostic format the assessment brief expects, and keeps the option open to redeploy on a Procfile-based host without changes.

Live link: https://raigonos.onrender.com

Availability: the production PostgreSQL database is on a paid Render plan, paid until 2026-11-06. The live site is guaranteed to be available, with all its data, until that date. Render's free database plan expires after 90 days, which is why it was upgraded (see Known Issues in TESTING.md).

Local Development

To run the project locally:

  1. Clone the repository:
    git clone https://github.com/raigonlab/raigonos.git
    
  2. Navigate into the project folder:
    cd raigonos
    
  3. Create and activate a virtual environment:
    python3 -m venv venv
    source venv/bin/activate
    
  4. Install dependencies:
    pip install -r requirements.txt
    
  5. Copy .env.example to .env and fill in local values (a local SECRET_KEY, DEBUG=True, etc.).
  6. Apply migrations:
    python manage.py migrate
    
  7. Run the development server:
    python manage.py runserver
    
  8. Open http://127.0.0.1:8000 in your browser.

Credits

Content

  • Markdown.2bn.dev — documentation
  • MDN Web Docs — DOM API reference
  • Code Institute materials
  • Django documentation
  • Claude — coding assistant, used throughout development for planning, debugging and project support, and specifically to help design and build the owner-only dashboard's UI/UX system in collaboration with the developer: the persistent sidebar/breadcrumb navigation model, the light/dark theme, the Grid/List toggle, the per-Collection and Artwork preview pages, the fixed path bar on public pages, search, and the hand-drawn icon set. This involvement is reflected in the project's commit history.
  • ChatGPT — debugging and explanations
  • Gemini — image generation
  • fonts.google.com — Playfair Display and Inter
  • fireship.dev
  • TinyPNG — image compression
  • ResponsivelyApp — managing screenshots

Media

  • All artwork pieces by Railson Gonçalves (© Raigon Lab, MMXXIII)
  • Logo and symbol images are original works by Railson

Acknowledgements

Special thanks to my mentor, Marko Tot, for guidance and support throughout the project, and to Tim Nelson and Fernando for their support and help debugging along the way.

About

Full-stack gallery management platform for artists — built with Django and PostgreSQL

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages