Empty Image Fields in Django

tl;dr

Django FileFields and ImageFields are stored as strings in your database. Don't use null=True with them, instead use blank=True and filter by .filter(image__exact="").

Discovering a Bug

I ran into a bug where I was trying to filter a QuerySet by image__isnull=False to determine if the image field was set. It ended up returning all objects where image was not yet set, so I did some digging.

How FileField and ImageField Look in the Database

The FileField and ImageField are both implemented in your database table as a varchar field. Note the type column from the psql display table below:

                               Table "public.myapp_mymodel"
    Column    |          Type          | Collation | Nullable |             Default
--------------+------------------------+-----------+----------+----------------------------------
 id           | bigint                 |           | not null | generated by default as identity
 image        | character varying(100) |           | not null |

This stores the relative path of the file, including your upload_to and filename.

The problem is that these strings have two values that might mean "empty": None and "".

How to Define a Model with Not Required File Fields

I had defined my model like this:

class MyModel(models.Model):
    image = models.ImageField(
        upload_to="images/",
        null=True,
        blank=True,
    )

But I should have removed the null=True option:

class MyModel(models.Model):
    image = models.ImageField(
        upload_to="images/",
        blank=True,
    )

How to Filter for Empty File Fields

Now, I still cannot filter by image__isnull, because the image field should never be set to NULL in the database and None in Python.

But I can filter by .filter(image__exact=""), which is the empty string.

Or exclude those values with .exclude(image__exact="").

Get Notified of New Posts

Sign up for the newsletter and I'll send you an email when there's a new post.