1.MEDIA_ROOT, MEDIA_URL, FileField/ImageField, and upload handlers
MEDIA_ROOT and MEDIA_URL are user-uploaded content's equivalent of STATIC_ROOT/STATIC_URL — MEDIA_ROOT is the filesystem directory uploads are saved into, MEDIA_URL the prefix they're served under. FileField stores an uploaded file and exposes it as a FieldFile (with .url, .path, .size); ImageField is FileField plus dimension validation and (if Pillow is installed) automatic width/height population, and requires Pillow to even validate as an image field. Upload handlers (UploadedFile subclasses, configured via FILE_UPLOAD_HANDLERS) are the pluggable layer deciding HOW an uploaded file's bytes are received during the request — MemoryFileUploadHandler for small files (kept in memory), TemporaryFileUploadHandler for anything over FILE_UPLOAD_MAX_MEMORY_SIZE (streamed to a temp file on disk instead), chosen automatically based on size.
Key features
- MEDIA_ROOT is the filesystem directory FileField/ImageField save uploads into; MEDIA_URL is the URL prefix they're served under — the same two-setting split as STATIC_ROOT/STATIC_URL, for a different kind of content
- FileField.upload_to (a string path template or a callable) controls WHERE within MEDIA_ROOT a given upload is placed — a callable receives (instance, filename) and can build a per-user or per-date path
- A FieldFile (what accessing a FileField/ImageField attribute actually returns) exposes .url, .path, .size, .name — not the raw bytes; .open()/.read() access content, and Django does NOT delete the old file automatically when a FileField is reassigned or the model is deleted
- ImageField requires Pillow to be installed to validate as an image at all — without Pillow, ImageField's validation silently cannot confirm the upload is a real, decodable image
- MemoryFileUploadHandler and TemporaryFileUploadHandler are the two default FILE_UPLOAD_HANDLERS, tried in order — a file at or under FILE_UPLOAD_MAX_MEMORY_SIZE (default 2.5MB) is handled in memory; anything larger streams to a temp file on disk automatically
- runserver conveniently serves MEDIA_ROOT directly when DEBUG=True (via a URLconf helper), but Django explicitly documents this as unsuitable for production — a real deployment needs a web server, CDN, or object-storage backend to serve media
Static vs media, side by side
- Client → Upload handlers: POST multipart file
- Upload handlers → Upload handlers: size ≤ FILE_UPLOAD_MAX_MEMORY_SIZE → MemoryFileUploadHandler
- Upload handlers → Upload handlers: size > threshold → TemporaryFileUploadHandler (disk)
- Upload handlers → FileField: saved under MEDIA_ROOT via upload_to
Command
class Document(models.Model):
file = models.FileField(upload_to="documents/")
instance.file.url # MEDIA_URL + relative path
instance.file.path # absolute filesystem pathMEDIA_ROOT/MEDIA_URL mirror STATIC_ROOT/STATIC_URL for user-uploaded content — never served directly by runserver's convenience in production. upload_to (string or callable) controls placement within MEDIA_ROOT. Django never auto-deletes a FileField's old file on reassignment or model deletion — that cleanup is explicit, application-level responsibility. FILE_UPLOAD_MAX_MEMORY_SIZE picks memory-vs-disk handling, not a size cap.