bhylu.com

Advertisements

Django File Uploads

Uploaded files are untrusted input. A small access-note feature should limit size, inspect its bytes and store it separately from public application assets.

How it works

The lab accepts only small UTF-8 text without markup-like angle brackets or NUL bytes. It does not trust the filename extension or declared content type as proof of content. The form rewinds the file after validation so storage receives the full content. MEDIA_ROOT is a private temporary directory for standalone lessons, and no public media-serving route is added. This is not a malware scanner or a production document-storage policy.

Run in the supplied local Django project

This lesson uses Django 5.2.18 LTS with Python 3.12. Download the source downloader and run node download-project.mjs http://127.0.0.1:8000 with Node 22 while previewing locally. It creates a new bhylu-django-lab folder and refuses to overwrite an existing one. After deployment, the default base is bhylu.com; until then use your local preview URL.

Inside that new folder, create a virtual environment, install requirements.txt and run the complete lesson file shown below. Every lesson creates a fresh in-memory SQLite database and temporary upload storage. It does not edit the interactive lab’s db.sqlite3 or Bhylu’s database. The visible listing below is the lesson’s core code; its complete downloaded file includes the shared bootstrap.

Django does not run in Bhylu’s current browser playground. No Apache, MySQL or global PHP configuration change is needed. See the setup lesson for the full project and commands.

Example

Run .venv\Scripts\python.exe lessons\uploads.py from the downloaded project root. Download the complete lesson file; keep it under lessons/. The project’s shared models, forms, views and fixture are prerequisites, not a hidden online runtime.

lessons/uploads.py — core code

from django.core.files.uploadedfile import SimpleUploadedFile
from workshops.forms import NoteUploadForm
from django.core.files.storage import default_storage
form = NoteUploadForm(files={"note":SimpleUploadedFile("access.txt", b"Use the side entrance.")})
print(form.is_valid())
name = default_storage.save("notes/access.txt", form.cleaned_data["note"])
print(default_storage.exists(name))
with default_storage.open(name, "rb") as saved:
    print(saved.read().decode())

Expected result

True
True
Use the side entrance.

The file round trip preserves the accepted bytes in owned local storage. The backend may choose a new filename to avoid collisions, so code should use the returned name rather than assume its requested path was retained.

Use Django’s versioned official documentation for exact API contracts and the deployment checklist before a separate release.

Advertisements