Upgrade Notes of Django (from v3.0 to v5.0)

2023年12月30日


The post Notes of Site Upgrade - Y2023 Q4 holds the catalog of the whole upgrade's note. 

This post concentrates on upgrading Django from version 3.0 to 4.0 and then from 4.0 to 5.0, specifically addressing how to handle the breaking changes encountered between these two major versions.
The reason that the upgrade process was divided into two phases is mainly because, at the time Django began to upgrade, the Python version is 3.9, which only supports Django up till version 4.

Upgrade Django from version 3 to 4


Fix #1

Error message after upgraded Django version to 4.0:
...
ImportError: cannot import name 'DEFAULT_CIPHERS' from 'urllib3.util.ssl_' (/***/urllib3/util/ssl_.py)

Original Django application related code, in requirements.txt:
...
botocore==1.15.36
...
urllib3
...

Root cause analysis:
1. In the requirements.txt file, previously, the urllib3 package was not pinned to a specific version, meaning that when the application is deployed, the latest urllib3 package will be installed. At the last success deployment time, the urllib3 package has not dumped up to version 2.1.0.
2. Today (Dec. 30, 2023), the latest version of urllib3 package is 2.1.0. When deploying the application, the urllib3 package with version 2.1.0 will be installed.
3. In the requirements.txt file, previously, the botocore package was pinned to version 1.15.36.
4. The botocore version 1.15.36 does not support urllib3 version2.0. When deploying the application today, the incomtability relationship between urllib3 and botocore caused the issue.

Solution:
1. Update requirements.txt. These specific versions have been validated as workable in my environment.
...
botocore==1.34.11
...
urllib3==2.1.0
...

Lesson learnt:
Try to always pin the package version in requirements.txt to ensure stability, regardless of current absence of issues, and plan a roadmap to keep the package versions up-to-date.


Fix #2

Error message after upgraded Django version to 4.0:
...
ModuleNotFoundError: No module named 'typing_extensions'

Original Django application related code:
N/A

Solution:
1. Update requirements.txt to install the typing_extensions package as one of the necessary dependencies:
typing_extensions


Fix #3

Error message after upgraded Django version to 4.0:
...
ImportError: cannot import name 'ugettext_lazy' from 'django.utils.translation' (/xxx/django/utils/translation/__init__.py)

Original Django application related code:
...
from django.utils.translation import ugettext_lazy as _
...

Root cause analysis:
1. In Django 3.0, django.utils.translation.ugettext_lazy() is deprecated in favor of the function that it's alias for django.utils.translation.gettext_lazy().
2. In Django 4.0, django.utils.translation.ugettext_lazy() has been removed.

Solution:
1. Update code in the Django application code to use gettext_lazy instead.
...
from django.utils.translation import gettext_lazy as _
...


Fix #4

Error message after upgraded Django version to 4.0:
...
ImportError: cannot import name 'url' from 'django.conf.urls' (/xxx/django/conf/urls/__init__.py)

Original Django application related code:
...
from django.conf.urls import url
...

Root cause analysis:
1. django.conf.urls.url() was deprecated in Django 3.0, and is removed in Django 4.0+. The easiest fix is to replace url() with re_path()re_path uses regexes like url, so you only have to update the import and replace url with re_path.

Solution:
1. Update the import.
...
from django.urls import re_path
...
2. Replace url with re_path. everywhere in the code.


Fix #5

Error message after upgraded Django version to 4.0:
...
django.core.management.base.SystemCheckError: SystemCheckError: System check identified some issues:

ERRORS:
?: (4_0.E001) As of Django 4.0, the values in the CSRF_TRUSTED_ORIGINS setting must start with a scheme (usually http:// or https://) but found tianzhui.cloud. See the release notes for details.
...

Original Django application related code:
...
CSRF_TRUSTED_ORIGINS = ['tianzhui.cloud']
...

Root cause analysis:
1. The origin and the host are the same domain.
2. My Django application is served over HTTPS.
3. The Django application is behind a proxy, i.e., Nginx.
4. I have already set SECURE_PROXY_SSL_HEADER = ('HTTP_X_FORWARDED_PROTO', 'https') in Django project setting.
In this case:
1. The origin header from the client's browser will be https://www.example.com due to 2.
2. request.is_secure() is returning False due to the header in the Nginx server configuration has not been set.

Changed in Django 4.0:
The values in older versions must only include the hostname (possibly with a leading dot) and not the scheme or an asterisk.
Also, Origin header checking isn't performed in older versions.
CSRF_TRUSTED_ORIGINS (4.0)

Solution:
1. Updated code in the Django settings.py file:
...
CSRF_TRUSTED_ORIGINS = [
    'https://tianzhui.cloud', 'https://*.tianzhui.cloud'
]
...
2. Set the header in the server configuration for Nginx in nginx.conf:
...
server {
    ...
    location / {
        uwsgi_pass  django;
        ...
        proxy_set_header X-Forwarded-Proto https;
    }
}


Upgrade Django from 4.2 to 5.0


Fix #1

Error message after upgraded Django version to 5.0:
...
ModuleNotFoundError: No module named 'tzdata'
...

Solution:
1. Update requirements.txt to install the tzdata package.
...
tzdata==2023.4
...


Fix #2

Error message after upgraded Django version to 5.0:
...
    from django.utils.timezone import utc
ImportError: cannot import name 'utc' from 'django.utils.timezone' (/usr/local/lib/python3.10/dist-packages/django/utils/timezone.py)
...

Reason:
...
from django.utils.timezone import utc
...
datetime.strptime(
                '2010-01-01 00:00:00', "%Y-%m-%d %H:%M:%S").replace(tzinfo=utc)
...

Solution:
1. Remove this import line, and remove "replace(tzinfo=utc)" from "datetime.strptime()".
When support for time zones is enabled, Django stores datetime information in UTC in the database, uses time-zone-aware datetime objects internally, and translates them to the end user’s time zone in templates and forms.

References


Forbidden (403) CSRF verification failed. Request aborted. Reason given for failure: Origin checking failed does not match any trusted origins

ImportError: cannot import name 'url' from 'django.conf.urls' after upgrading to Django 4.0

ImportError: cannot import name 'ugettext_lazy' from 'django.utils.translation'

CSRF_TRUSTED_ORIGINS

proxy_set_header

Features deprecated in 3.0

Django Deprecation Timeline in Django 4.0

Time zones

Category: Django Tags: public

Upvote


Downvote