Skip to content

Adding a Photo Field

People want to browse potential pets by their cute pictures. Let's add a photo to our animals, so our sample data can include a picture of each one.

This chapter's starting branch provides Pillow, the settings for uploaded files, and the sample data. Check it out:

shell
git stash -u
git fetch --all
git checkout -b my-ch04-start --no-track origin/ch04-start
uv sync

If Git prints No local changes to save, there was nothing to set aside.

The Photo Field

Open animals/models.py and add a photo field to Animal:

animals/models.py
    description = models.TextField(blank=True)
    photo = models.ImageField(upload_to="animals/", blank=True)

Django saves the uploaded file to disk, and the ImageField column holds the file's path. upload_to is the folder the files go in, and blank=True means it's an optional field.

An ImageField requires Pillow, an imaging library, which the starting branch added to our dependencies.

Where Uploaded Files Get Stored

Two settings say where on disk uploaded files are saved, and what address they're served from. The starting branch added them at the bottom of config/settings.py:

config/settings.py
MEDIA_ROOT = BASE_DIR / "media"
MEDIA_URL = "media/"
  • MEDIA_ROOT: The folder where Django saves uploaded files. BASE_DIR is the project's root folder, which startproject defines at the top of the settings.
  • MEDIA_URL: The root of the address those files are served from.

With upload_to="animals/" on our field, a photo is saved in media/animals/, and served from an address that starts with /media/animals/.

Django doesn't serve that folder automatically, so the starting branch also added this line at the bottom of config/urls.py:

config/urls.py
urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)

static() returns URL patterns that serve the files in MEDIA_ROOT. It only does that while DEBUG is on. On a production site, a web server or a storage service serves uploaded files.

Migrate the New Field

We changed a model, so let's make a migration, and migrate:

shell
uv run manage.py makemigrations
uv run manage.py migrate