Fix Django ImportError After Upgrading: force_text, ugettext, url() and Others
Fix ImportError cannot import name force_text, smart_text, ugettext_lazy, url, or ifequal after a Django upgrade: map each removed API to its replacement, upgrade third-party packages that still use them, and catch removals in CI before deploying.
Problem statement
After upgrading Django, the app fails at startup with errors like:
ImportError: cannot import name 'force_text' from 'django.utils.encoding'
ImportError: cannot import name 'ugettext_lazy' from 'django.utils.translation'
ImportError: cannot import name 'url' from 'django.conf.urls'
ImportError: cannot import name 'available_attrs' from 'django.utils.decorators'
Django deprecates APIs for two feature releases before removing them. Django 4.0 removed a large batch that had been deprecated since 3.0, and later releases removed more. Your code, or more often a third-party package you have not upgraded, still imports one of them. The traceback shows which file does.
- Read the traceback's file path: your code or
site-packages/<package>. - In your code, replace removed names with their modern equivalents (table below).
- For a third-party package, upgrade it to a release that supports your Django version.
- If a package is abandoned, replace it or vendor a patched copy.
- Run tests with
python -W error::DeprecationWarningbefore the next upgrade to catch these early.
Step-by-step solution
1. Find who imports the removed name
python -X importtime -c "import django; django.setup()" 2>&1 | tail -1
grep -rn "force_text\|ugettext\|from django.conf.urls import url" --include=*.py .
If the traceback's last frame is in site-packages, the package is the problem, not your project.
2. Replace removed APIs in your code
Most removals have a direct replacement:
# encoding
from django.utils.encoding import force_str, smart_str # was force_text, smart_text
# translation
from django.utils.translation import gettext_lazy as _ # was ugettext_lazy
from django.utils.translation import gettext, ngettext # was ugettext, ungettext
# URLs
from django.urls import path, re_path, include # was django.conf.urls.url
# http
from django.utils.http import url_has_allowed_host_and_scheme # was is_safe_url
from urllib.parse import quote, unquote # was django.utils.http.urlquote
Change url(r"^articles/(?P<pk>\d+)/$", view) to re_path(...) with the same regex, or convert it to path("articles/<int:pk>/", view).
3. Upgrade third-party packages
pip list --outdated
pip install --upgrade djangorestframework django-filter django-crispy-forms
Check each package's changelog for the Django version it supports. Upgrade packages before or together with Django, not after. A lock file makes the combination reproducible across environments.
4. Handle abandoned packages
If a package has no release for your Django version:
- Look for a maintained fork or a modern alternative.
- Pin a commit from a fork that fixed the imports.
- As a last resort, vendor it into your project and patch the imports yourself.
Monkeypatching django.utils.encoding.force_text = force_str at startup works but hides the problem and breaks again on the next removal.
5. Catch removals before they ship
Deprecation warnings appear one or two releases before a removal. Turn them into errors in CI:
python -W error::DeprecationWarning -W error::PendingDeprecationWarning manage.py test
Or in pytest.ini: filterwarnings = error::DeprecationWarning. Then upgrade one feature release at a time, fixing warnings at each step.
Explanation
Django's deprecation policy keeps an API working with a warning for at least two feature releases, then removes it. Python hides DeprecationWarning by default outside of __main__ and tests, so warnings scroll by unnoticed in production logs. When the removal lands, the import fails during startup, before any view runs. Third-party packages are the most common source because they are upgraded less often than Django itself.
Edge cases / notes
- Templates. Removed template tags such as
{% ifequal %}raiseTemplateSyntaxErroronly when the template renders, so they may surface later than import errors. - Python version too. A Django upgrade may also need a newer Python; check the supported versions table first.
- django-upgrade. The
django-upgradetool rewrites many deprecated patterns automatically and is a fast first pass.
Next steps
- Plan the whole process with the Django Upgrade Checklist.
- Keep dependencies current with Automate Django Dependency Updates.
- Watch for the new default primary key type in Fix Django models.W042.
FAQ
Which Django version removed force_text?
Django 4.0 removed force_text, smart_text, the ugettext family, and django.conf.urls.url, all deprecated since 3.0.
Can I add an alias instead of changing the code?
You can monkeypatch, but it is fragile and hides other problems. Replace the imports or upgrade the package.
Why does the error come from site-packages?
A third-party package still uses the removed API. Upgrade it to a version that supports your Django release.
How do I find all deprecated usages before upgrading?
Run your tests with deprecation warnings turned into errors on the current Django version.