Allow Whitespace to be a Valid CharField Value in Django Admin

By Aaron O. Ellis

Monday, October 12, 2020

Let’s attempt to create a basic Django model that allows whitespace as a valid value. Here’s an example models.py:

class Example(models.Model):
    title = models.CharField(max_length=100)

    def __str__(self):
        return self.title

Using the Django shell, everything works as expected:

>>> from example.models import Example
>>> created = Example.objects.create(title=' ')
>>> created.title == ' '
True

But if we add the Example model to admin.py:

from django.contrib import admin
from .models import Example

@admin.register(Example)
class ExampleAdmin(admin.ModelAdmin):
    ordering = ('title',)

And then try to create the same model via the Django admin, using an empty space for the title, we receive an error:

Please correct the error below

Adding blank=True to the title property makes the result even more confusing:

class Example(models.Model):
    title = models.CharField(max_length=100, blank=True)

The model will be created, but the title property is empty!

Empty field

The Django admin is doing something hidden and non-obvious when creating the example model’s admin form: it’s enabling whitespace stripping by default! This stripping process removes our whitespace character and - in the case of blank=False (the default for fields) - fails validation!

This default can be seen in the keyword arguments of the form CharField that the Django admin uses to represent the title model field.

Even worse, it’s not clear how to change this behavior. Adding a strip=False keyword to the model CharField results in an unexpected keyword argument error. We could build a custom form for the admin, but we’d be re-creating an entire ModelForm to change a single keyword of one property. The ModelAdmin has a property called formfield_overrides, but since it operates via classes, we’d be altering the behavior of all CharField inputs - and still need to create a custom field input class!

The easiest solution is to override the behavior of the ModelAdmin method formfield_for_dbfield in admin.py, which determines which form class should be used to represent each model property. We just need to add strip=False to the field keywords when it encounters our title property:

@admin.register(Example)
class ExampleAdmin(admin.ModelAdmin):
    ordering = ('title',)

    def formfield_for_dbfield(self, db_field, request, **kwargs):
        if db_field.name == 'title':
            kwargs['strip'] = False
        return super().formfield_for_dbfield(db_field, request, **kwargs)
Success

Success! The admin has an undesirable behavior, and that’s where our override lives - no changes to models.py needed!