Python Django: How to Resolve "ImportError: Cannot Import Name 'six' from 'django.utils'"
If you've recently upgraded Django to version 3.0 or later, you may have encountered this error:
ImportError: cannot import name 'six' from 'django.utils'
This happens because django.utils.six (a built-in compatibility module that helped code run on both Python 2 and Python 3) was removed entirely in Django 3.0. Any code that still tries to import six from Django's utilities will fail immediately.
In this guide, you'll learn why this error occurs, how to fix it in your own code, and how to handle it when it comes from third-party packages.
Why Was django.utils.six Removed?
The six library is a Python 2/3 compatibility layer. It provides utilities to write code that works across both Python versions, handling differences in string types, iterators, metaclasses, and more.
Django bundled its own copy of six inside django.utils for years. However, Django 3.0 dropped Python 2 support entirely, so the compatibility layer was no longer needed. The bundled six module was removed as part of this cleanup.
Timeline:
| Django Version | Python 2 Support | django.utils.six |
|---|---|---|
| Django 2.x | ✅ Supported | ✅ Available |
| Django 3.0+ | ❌ Dropped | ❌ Removed |
Reproducing the Error
Any code that imports six from Django's utilities will trigger the error:
from django.utils import six
Output:
ImportError: cannot import name 'six' from 'django.utils'
This can also appear indirectly when importing a third-party package that internally relies on django.utils.six:
Traceback (most recent call last):
File "manage.py", line 22, in <module>
main()
...
File "/path/to/some_package/utils.py", line 3, in <module>
from django.utils import six
ImportError: cannot import name 'six' from 'django.utils'
Fix 1: Replace django.utils.six with the Standalone six Package
The six library is available as a standalone PyPI package. If your code still needs Python 2/3 compatibility utilities (or uses six helpers like six.text_type or six.moves), install it independently and update your imports.
Step 1: Install the standalone six package
pip install six
Step 2: Update your import statements
Before (broken on Django 3.0+):
from django.utils import six
if six.PY3:
string_type = str
else:
string_type = unicode
After (works on all Django versions):
import six
if six.PY3:
string_type = str
else:
string_type = unicode
A simple find and replace across your project can handle most cases:
- Find:
from django.utils import six - Replace with:
import six
And:
- Find:
from django.utils.six - Replace with:
from six
Fix 2: Remove six Entirely (Recommended for Modern Projects)
If your project only targets Python 3 (which it should, since Django 3.0+ requires Python 3), you likely don't need six at all. The compatibility layer exists solely to bridge Python 2 and Python 3 differences.
Replace six utilities with their native Python 3 equivalents:
Before:
from django.utils import six
# Check Python version
if six.PY3:
text = str("hello")
else:
text = unicode("hello")
# String type check
if isinstance(value, six.string_types):
print("It's a string")
# Iterate over dictionary items
for key, val in six.iteritems(my_dict):
print(key, val)
After (pure Python 3: no six needed):
# No version check needed: it's always Python 3
text = "hello"
# str is the only string type in Python 3
if isinstance(value, str):
print("It's a string")
# dict.items() is already efficient in Python 3
for key, val in my_dict.items():
print(key, val)
Common six Utilities and Their Python 3 Replacements
six Usage | Python 3 Equivalent |
|---|---|
six.PY3 | Always True: remove the check |
six.text_type | str |
six.binary_type | bytes |
six.string_types | (str,) |
six.integer_types | (int,) |
six.iteritems(d) | d.items() |
six.itervalues(d) | d.values() |
six.iterkeys(d) | d.keys() |
six.moves.range | range |
six.moves.urllib | urllib |
six.ensure_str(s) | s (if already str) or s.decode() |
six.with_metaclass(Meta, Base) | class MyClass(Base, metaclass=Meta): |
Fix 3: Update Third-Party Packages
Often, the error doesn't come from your own code but from an outdated third-party package that still imports django.utils.six internally.
Step 1: Identify the package causing the error
Read the full traceback carefully. Look for the file path that triggers the import:
File "/path/to/venv/lib/python3.11/site-packages/some_old_package/utils.py", line 3, in <module>
from django.utils import six
ImportError: cannot import name 'six' from 'django.utils'
In this example, some_old_package is the culprit.
Step 2: Upgrade the package
pip install --upgrade some_old_package
Most actively maintained packages have already released Django 3.0+ compatible versions.
Step 3: Check compatibility
If the latest version of the package still uses django.utils.six, the package may be abandoned. In that case:
- Search for a maintained fork on PyPI or GitHub.
- Find an alternative package that provides the same functionality.
- Patch it yourself by forking the repository and replacing
from django.utils import sixwithimport six.
Do not downgrade Django to fix this error. Django 2.x no longer receives security updates. Keeping your Django version current is critical for security.
Step 4: Audit all your dependencies
To proactively find all packages that might use django.utils.six, search your installed packages:
# Search all installed packages for the problematic import
grep -r "django.utils.six" $(python -c "import site; print(site.getsitepackages()[0])")
Or on Windows (PowerShell):
Select-String -Path "$(python -c 'import site; print(site.getsitepackages()[0])')\*\*.py" -Pattern "django.utils.six" -Recurse
Upgrade any packages that appear in the results.
Full Migration Example
Here's a complete before-and-after example showing a Django view that used six and how to modernize it:
Before (Django 2.x style):
from django.utils import six
from django.http import JsonResponse
def process_input(request):
user_input = request.GET.get("value", "")
if isinstance(user_input, six.text_type):
result = user_input.upper()
elif isinstance(user_input, six.binary_type):
result = user_input.decode("utf-8").upper()
else:
result = str(user_input).upper()
return JsonResponse({"result": result})
After (Django 3.0+ / Python 3):
from django.http import JsonResponse
def process_input(request):
user_input = request.GET.get("value", "")
# In Django 3.0+ with Python 3, request.GET values are always str
result = str(user_input).upper()
return JsonResponse({"result": result})
Output (both versions, given ?value=hello):
{"result": "HELLO"}
Prevention: Keep Dependencies Up to Date
To avoid running into this error in the future, adopt these practices:
- Pin your dependencies in
requirements.txtwith compatible version ranges:
Django>=4.2,<5.0
six>=1.16.0 # Only if genuinely needed
- Run
pip list --outdatedregularly to check for available updates. - Test upgrades in a virtual environment before applying them to your project:
python -m venv test_env
source test_env/bin/activate
pip install -r requirements.txt
python manage.py test
Conclusion
The "ImportError: cannot import name 'six' from 'django.utils'" error occurs because Django 3.0 removed the bundled six compatibility module after dropping Python 2 support. The fix depends on where the import originates:
- Your own code: Replace
from django.utils import sixwithimport six(after installing the standalone package), or better yet, removesixentirely and use native Python 3 equivalents. - Third-party packages: Upgrade the package to a version that supports Django 3.0+. If no compatible version exists, find an alternative or fork and patch the package.
For any modern Django project, the cleanest solution is to eliminate six altogether. Since Django 3.0+ requires Python 3, the compatibility layer serves no purpose and removing it makes your code simpler and easier to maintain.