To begin, this is my best theory to date. I’m not positive this is the problem and I’m open to being corrected by anyone more knowledgeable.
Summary;
I’m upgrading from Django 3.2 to Django 4.2.7. Everything went fine, I figured out library conflicts and “climbed the ladder” of release numbers, tested and got all unit tests passing. However, when I try to run the application, it fails on connecting to the database with the following error: AttributeError. Did you mean: ‘mysql_server_info’?
Detail:
The error: AttributeError. Did you mean: ‘mysql_server_info’? is coming from here which is the class DatabaseWrapper(BaseDatabaseWrapper)
venv_Django_4.2.7/lib/python3.11/site-packages/mysql/connector/django/base.py", line 290, in getattr
The getattr appears to be triggered from here:
venv_Django_4.2.8/lib/python3.11/site-packages/django/db/backends/mysql/features.py", line 228, in is_sql_auto_is_null_enabled
return self.connection.mysql_server_data[“sql_auto_is_null”]
When I look in debug mode, that features.py has a class DatabaseFeatures(BaseDatabaseFeatures): that contains that method: is_sql_auto_is_null_enabled.
However, when the method is called, it hits the getattr because, I assume, the mysql_server_data dictionary is NOT found in the connection object referenced by the call above. (I have verified the dict is not present in debug mode). The connection object is labeled as “DatabaseWrapper” in debug mode and I can see breakpoints being hit in the DatabaseWrapper class mentioned above at runtime.
However - I notice that there is another DatabaseWrapper(BaseDatabaseWrapper): here…
venv_Django_4.2.8/lib/python3.11/site-packages/django/db/backends/mysql/base.py
And this one seems to be creating the exact dictionary that is being called above.
Like this:
@cached_property
def mysql_server_data(self):
with self.temporary_connection() as cursor:
# Select some server variables and test if the time zone
# definitions are installed. CONVERT_TZ returns NULL if 'UTC'
# timezone isn't loaded into the mysql.time_zone table.
cursor.execute(
"""
SELECT VERSION(),
@@sql_mode,
@@default_storage_engine,
@@sql_auto_is_null,
@@lower_case_table_names,
CONVERT_TZ('2001-01-01 01:00:00', 'UTC', 'UTC') IS NOT NULL
"""
)
row = cursor.fetchone()
return {
"version": row[0],
"sql_mode": row[1],
"default_storage_engine": row[2],
"sql_auto_is_null": bool(row[3]),
"lower_case_table_names": bool(row[4]),
"has_zoneinfo_database": bool(row[5]),
}
It also seems to me that the following entry in settings.py is driving the choice of the first connector (DatabaseWrapper) class (venv_Django_4.2.7/lib/python3.11/site-packages/mysql/connector/django/base.py)
DATABASES = {
‘default’: {
‘ENGINE’: ‘mysql.connector.django’,
And that maybe, if the other DatabaseWrapper class was used, that dictionary would be present in the “connection” object when it was called in the DatabaseFeatures class…
So, if I’m right - what would be the reason for needing to change that ‘ENGINE’ string in settings.py on an upgrade to Django 4.2? I can’t see anything in the docs for that version… It seems to be the right choice, but, as I’ve described above, it is failing on a non-existent dictionary.
Isn’t the DatabaseWrapper class I’m using for MySQL and Django the correct one?
(venv_Django_4.2.7/lib/python3.11/site-packages/mysql/connector/django/base.py)
Any ideas would be appreciated - as I said at the top, this is my best theory at the moment, but I could be wrong.
Thanks
PS: This is the trace…
Exception in thread django-main-thread:
Traceback (most recent call last):
File "/usr/local/Cellar/python@3.11/3.11.6_1/Frameworks/Python.framework/Versions/3.11/lib/python3.11/threading.py", line 1045, in _bootstrap_inner
self.run()
File "/usr/local/Cellar/python@3.11/3.11.6_1/Frameworks/Python.framework/Versions/3.11/lib/python3.11/threading.py", line 982, in run
self._target(*self._args, **self._kwargs)
File "/venv_Django_4.2.7/lib/python3.11/site-packages/django/utils/autoreload.py", line 64, in wrapper
fn(*args, **kwargs)
File "/venv_Django_4.2.7/lib/python3.11/site-packages/django/core/management/commands/runserver.py", line 136, in inner_run
self.check_migrations()
File "/venv_Django_4.2.7/lib/python3.11/site-packages/django/core/management/base.py", line 574, in check_migrations
executor = MigrationExecutor(connections[DEFAULT_DB_ALIAS])
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/venv_Django_4.2.7/lib/python3.11/site-packages/django/db/migrations/executor.py", line 18, in __init__
self.loader = MigrationLoader(self.connection)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/venv_Django_4.2.7/lib/python3.11/site-packages/django/db/migrations/loader.py", line 58, in __init__
self.build_graph()
File "/venv_Django_4.2.7/lib/python3.11/site-packages/django/db/migrations/loader.py", line 235, in build_graph
self.applied_migrations = recorder.applied_migrations()
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/venv_Django_4.2.7/lib/python3.11/site-packages/django/db/migrations/recorder.py", line 81, in applied_migrations
if self.has_table():
^^^^^^^^^^^^^^^^
File "/venv_Django_4.2.7/lib/python3.11/site-packages/django/db/migrations/recorder.py", line 57, in has_table
with self.connection.cursor() as cursor:
^^^^^^^^^^^^^^^^^^^^^^^^
File "/venv_Django_4.2.7/lib/python3.11/site-packages/ddtrace/contrib/trace_utils.py", line 341, in wrapper
return func(mod, pin, wrapped, instance, args, kwargs)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/venv_Django_4.2.7/lib/python3.11/site-packages/ddtrace/contrib/django/patch.py", line 120, in cursor
cursor = func(*args, **kwargs)
^^^^^^^^^^^^^^^^^^^^^
File "/venv_Django_4.2.7/lib/python3.11/site-packages/django/utils/asyncio.py", line 26, in inner
return func(*args, **kwargs)
^^^^^^^^^^^^^^^^^^^^^
File "/venv_Django_4.2.7/lib/python3.11/site-packages/django/db/backends/base/base.py", line 330, in cursor
return self._cursor()
^^^^^^^^^^^^^^
File "/venv_Django_4.2.7/lib/python3.11/site-packages/django/db/backends/base/base.py", line 306, in _cursor
self.ensure_connection()
File "/venv_Django_4.2.7/lib/python3.11/site-packages/django/utils/asyncio.py", line 26, in inner
return func(*args, **kwargs)
^^^^^^^^^^^^^^^^^^^^^
File "/venv_Django_4.2.7/lib/python3.11/site-packages/django/db/backends/base/base.py", line 289, in ensure_connection
self.connect()
File "/venv_Django_4.2.7/lib/python3.11/site-packages/django/utils/asyncio.py", line 26, in inner
return func(*args, **kwargs)
^^^^^^^^^^^^^^^^^^^^^
File "/venv_Django_4.2.7/lib/python3.11/site-packages/django/db/backends/base/base.py", line 272, in connect
self.init_connection_state()
File "/venv_Django_4.2.8/lib/python3.11/site-packages/mysql/connector/django/base.py", line 341, in init_connection_state
if self.features.is_sql_auto_is_null_enabled:
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/venv_Django_4.2.7/lib/python3.11/site-packages/django/utils/functional.py", line 57, in __get__
res = instance.__dict__[self.name] = self.func(instance)
^^^^^^^^^^^^^^^^^^^
File "venv_Django_4.2.7/lib/python3.11/site-packages/django/db/backends/mysql/features.py", line 228, in is_sql_auto_is_null_enabled
return self.connection.mysql_server_data["sql_auto_is_null"]
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "venv_Django_4.2.7/lib/python3.11/site-packages/mysql/connector/django/base.py", line 290, in __getattr__
raise AttributeError
AttributeError. Did you mean: 'mysql_server_info'?
DATABASES = {
'default': {
'ENGINE': 'mysql.connector.django',
'NAME': os.environ.get('AGENT_DIRECTORY_DATABASE_NAME', 'employee'),
'USER': os.environ.get('AGENT_DIRECTORY_DATABASE_USER'),
'PASSWORD': os.environ.get('AGENT_DIRECTORY_DATABSE_PASSWORD'),
'HOST': os.environ.get('AGENT_DIRECTORY_DATABSE_HOST'),
'PORT': os.environ.get('AGENT_DIRECTORY_DATABSE_PORT', 3306)
}
}