Skip to content

Commit bd24fee

Browse files
committed
BUG#37047789: Python connector does not support Django enum
This patch fixes the issue where pure-python mode of Connector/Python does not work properly while executing queries via Django ORM when Django Enum objects or ChoiceField is being used in a django model. Change-Id: I6f8fcfde23b34c94cdc7f4486c2c614d843389cd
1 parent cffa5ab commit bd24fee

File tree

4 files changed

+63
-2
lines changed

4 files changed

+63
-2
lines changed

CHANGES.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ v9.4.0
1313

1414
- BUG#37820231: Text based django ORM filters doesn't work with Connector/Python
1515
- BUG#37806057: Rename extra option (when installing wheel package) to install webauthn functionality dependencies
16+
- BUG#37047789: Python connector does not support Django enum
1617
- BUG#36452514: Missing version info resources
1718
- BUG#34950958: MySQL Python Connector doesn't work with ssh in the same process
1819
- BUG#34844347: Freezes on connection via sshtunnel

mysql-connector-python/lib/mysql/connector/conversion.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@
3535
import time
3636

3737
from decimal import Decimal
38+
from enum import Enum
3839
from typing import Any, Callable, Dict, List, Optional, Set, Tuple, Union
3940

4041
from .constants import (
@@ -233,6 +234,8 @@ def quote(buf: Optional[Union[float, int, Decimal, HexLiteral, bytes]]) -> bytes
233234

234235
def to_mysql(self, value: MySQLConvertibleType) -> MySQLProducedType:
235236
"""Convert Python data type to MySQL"""
237+
if isinstance(value, Enum):
238+
value = value.value
236239
type_name = value.__class__.__name__.lower()
237240
try:
238241
converted: MySQLProducedType = getattr(self, f"_{type_name}_to_mysql")(

mysql-connector-python/lib/mysql/connector/types.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333

3434
from datetime import date, datetime, time, timedelta
3535
from decimal import Decimal
36+
from enum import Enum
3637
from time import struct_time
3738
from typing import (
3839
TYPE_CHECKING,
@@ -105,7 +106,7 @@ class MySQLScriptPartition(TypedDict):
105106
"""
106107

107108
# pylint: disable=invalid-name
108-
MySQLConvertibleType = Union[BinaryProtocolType, bool, struct_time]
109+
MySQLConvertibleType = Union[BinaryProtocolType, Enum, bool, struct_time]
109110
"""
110111
MySQL convertible Python types - Python types consumed by the built-in converter that
111112
can be converted to MySQL. It's a superset of `BinaryProtocolType`.

mysql-connector-python/tests/test_django.py

Lines changed: 57 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -105,14 +105,15 @@
105105
import django.db # pylint: disable=W0611
106106

107107
from django.core.exceptions import ImproperlyConfigured
108-
from django.db import connection
108+
from django.db import connection, models
109109
from django.db.backends.signals import connection_created
110110
from django.db.utils import DEFAULT_DB_ALIAS, load_backend
111111
from django.utils.safestring import SafeText
112112

113113
import mysql.connector
114114

115115
from mysql.connector.conversion import MySQLConverter
116+
from mysql.connector.django.base import CursorWrapper
116117
from mysql.connector.django.introspection import FieldInfo
117118
from mysql.connector.errors import ProgrammingError
118119

@@ -627,3 +628,58 @@ def test_missing_config(self):
627628
cnx_params = cnx.get_connection_params()
628629
self.assertTrue(cnx_params["raise_on_warnings"])
629630
del settings.DATABASES["default"]["OPTIONS"]
631+
632+
633+
class BugOra37047789(tests.MySQLConnectorTests):
634+
"""BUG#37047789: Python connector does not support Django enum
635+
636+
Django Enumeration types are not getting converted into MySQLConvertibleType
637+
thus, query execution via Django ORM using Connector/Python is failing when
638+
a model field with enum choices are being used.
639+
640+
This patch fixes the issue by changing the Enum object being passed to the
641+
conversation module to its underlying value before conversion to MySQL type
642+
takes place using the built-in property `value`, which already exists in every
643+
Enum objects.
644+
"""
645+
646+
table_name = "BugOra37047789"
647+
648+
class Priority(models.IntegerChoices):
649+
LOW = 1, 'Low'
650+
MEDIUM = 2, 'Medium'
651+
HIGH = 3, 'High'
652+
653+
class Status(models.TextChoices):
654+
DRAFT = 'draft', 'Draft'
655+
PUBLISHED = 'published', 'Published'
656+
657+
dbconfig = tests.get_mysql_config()
658+
659+
def setUp(self):
660+
with mysql.connector.connect(**self.dbconfig) as cnx:
661+
cnx.cmd_query(f"""
662+
CREATE TABLE {self.table_name} (
663+
priority INT NOT NULL DEFAULT 1,
664+
status varchar(10) NOT NULL DEFAULT 'draft'
665+
)
666+
""")
667+
cnx.commit()
668+
669+
def tearDown(self):
670+
with mysql.connector.connect(**self.dbconfig) as cnx:
671+
cnx.cmd_query(f"DROP TABLE {self.table_name}")
672+
673+
@tests.foreach_cnx()
674+
def test_django_enum_support(self):
675+
cnx_cur = self.cnx.cursor()
676+
django_cur = CursorWrapper(cnx_cur)
677+
django_cur.execute(
678+
f"INSERT INTO {self.table_name} VALUES (%s, %s)",
679+
(self.Priority.HIGH, self.Status.PUBLISHED)
680+
)
681+
django_cur.execute(
682+
f"INSERT INTO {self.table_name} VALUES (%(priority)s, %(status)s)",
683+
{'priority': self.Priority.HIGH, 'status': self.Status.PUBLISHED}
684+
)
685+
cnx_cur.close()

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy