Going DeeperExtra· 45 min read

Static Files & Media Uploads

Serve your CSS, JS and images (static files), and let users upload pictures and documents (media files) with ImageField.

What you will learn

  • Serve CSS/JS/images as static files
  • Accept user uploads with FileField / ImageField
  • Understand static vs media and collectstatic

Two kinds of files

Every real site serves files that are not HTML pages. Django splits them into two clearly different groups, and mixing them up is a classic beginner trip-up:

Static filesMedia files
WhatCSS, JS, logo, iconsfiles users upload
Who makes themyou, the developeryour visitors
Examplestyle.css, logo.pnga profile photo, a PDF
SettingSTATIC_URL / STATICFILES_DIRSMEDIA_URL / MEDIA_ROOT

Static files — your own CSS and JS

Static files are the assets you ship with the app. You put them in a static/ folder, then load them in templates with the {% static %} tag (which builds the correct URL for you). First, the setting and the folder:

settings.py — where static files live
# settings.py
STATIC_URL = "static/"                     # the URL prefix for static files
STATICFILES_DIRS = [BASE_DIR / "static"]   # project-wide static folder

# folder:  static/css/style.css

Now use it in a template. The {% load static %} line turns on the tag, and {% static "..." %} produces the right path:

Load a stylesheet and image with {% static %}
<!-- templates/base.html -->
{% load static %}
<link rel="stylesheet" href="{% static 'css/style.css' %}">
<img src="{% static 'images/logo.png' %}" alt="Logo">

Note: Output: {% static 'css/style.css' %} becomes a URL like /static/css/style.css. Never hard-code that path yourself — the tag adjusts it correctly for development and production.

collectstatic — gathering files for production

In development Django serves static files automatically. In production it does not — instead you run one command that copies every static file (yours plus Django’s own, like the admin’s CSS) into a single folder a web server can serve:

collectstatic copies all static files into one folder
# settings.py
STATIC_ROOT = BASE_DIR / "staticfiles"   # the single output folder

# then, once before deploying:
python manage.py collectstatic

Note: Output: 128 static files copied to '/app/staticfiles'. collectstatic gathered every app’s static files into STATIC_ROOT so the production server can serve them from one place. (We use the WhiteNoise tool to serve them in the deployment lesson.)

Media files — letting users upload

Media files are uploads from your visitors. To accept them you add a FileField (any file) or ImageField (pictures, with extra checks) to a model, and set two settings telling Django where to store them:

settings.py — where uploads are stored
# settings.py
MEDIA_URL = "/media/"               # URL prefix for uploaded files
MEDIA_ROOT = BASE_DIR / "media"     # folder on disk where they are saved
ImageField stores an uploaded picture
# models.py
class Profile(models.Model):
    name = models.CharField(max_length=100)
    avatar = models.ImageField(upload_to="avatars/")
    # files land in  MEDIA_ROOT/avatars/

upload_to="avatars/" is the sub-folder inside MEDIA_ROOT where these images are saved. ImageField needs the Pillow image library — install it with pip install Pillow. (Remember to makemigrations and migrate after adding the field.)

The upload form needs one special attribute

A form that sends files must include enctype="multipart/form-data" — this tells the browser to package the file bytes, not just text. And the view must read request.FILES (not request.POST) for the file:

Upload forms need enctype="multipart/form-data"
<!-- the upload form -->
<form method="post" enctype="multipart/form-data">
  {% csrf_token %}
  <input type="file" name="avatar">
  <button type="submit">Upload</button>
</form>

Putting the upload flow together, step by step:

  1. The user picks a file and submits the form (which has enctype="multipart/form-data").
  2. The browser sends the file bytes; Django puts them in request.FILES.
  3. You save the model — Django writes the file into MEDIA_ROOT/avatars/ and stores its path in the database.
  4. In a template you display it with <img src="{{ profile.avatar.url }}">, which uses MEDIA_URL to build the link.
  5. During development add the media URL route so the file actually serves (the docs’ one-liner with static(settings.MEDIA_URL, ...) in urls.py).

Watch out: Never put uploaded media inside your static folder, and never commit the media/ folder to Git — user uploads are runtime data, not source code.

Tip: Static = files you ship (run collectstatic before deploy). Media = files users upload (stored under MEDIA_ROOT). Keeping the two straight is what trips up most beginners.

Q. A user uploads a profile photo through a form. Which field type and which request attribute handle it?

Answer: Uploaded pictures use an ImageField on the model, and the uploaded file arrives in request.FILES (not request.POST). The form also needs enctype="multipart/form-data".

✍️ Practice

  1. Add a static/css/style.css file and load it in a base template with {% static %}.
  2. Add an ImageField to a model, build an upload form with the right enctype, and display the uploaded image with .url.

🏠 Homework

  1. Run collectstatic and inspect the STATIC_ROOT folder. Then let users upload an image and show it back on a page.
Want to learn this with a mentor?

CodingClave runs guided, project-based training (28-day, 45-day & 6-month batches).

Explore Training →