Browse Source

Fixed #33507 -- Used UUID data type on MariaDB 10.7+.

Co-Authored-By: Mariusz Felisiak <felisiak.mariusz@gmail.com>
Albert Defler 2 years ago
parent
commit
7cd187a5ba

+ 1 - 0
AUTHORS

@@ -40,6 +40,7 @@ answer newbie questions, and generally made Django that much better:
     Akshesh Doshi <aksheshdoshi+django@gmail.com>
     alang@bright-green.com
     Alasdair Nicol <https://al.sdair.co.uk/>
+    Albert Defler <albert.defler@gmail.com>
     Albert Wang <https://github.com/albertyw/>
     Alcides Fonseca
     Aldian Fazrihady <mobile@aldian.net>

+ 9 - 1
django/db/backends/mysql/base.py

@@ -103,7 +103,8 @@ class DatabaseWrapper(BaseDatabaseWrapper):
     # types, as strings. Column-type strings can contain format strings; they'll
     # be interpolated against the values of Field.__dict__ before being output.
     # If a column type is set to None, it won't be included in the output.
-    data_types = {
+
+    _data_types = {
         "AutoField": "integer AUTO_INCREMENT",
         "BigAutoField": "bigint AUTO_INCREMENT",
         "BinaryField": "longblob",
@@ -133,6 +134,13 @@ class DatabaseWrapper(BaseDatabaseWrapper):
         "UUIDField": "char(32)",
     }
 
+    @cached_property
+    def data_types(self):
+        _data_types = self._data_types.copy()
+        if self.features.has_native_uuid_field:
+            _data_types["UUIDField"] = "uuid"
+        return _data_types
+
     # For these data types:
     # - MySQL < 8.0.13 doesn't accept default values and implicitly treats them
     #   as nullable

+ 5 - 0
django/db/backends/mysql/features.py

@@ -349,3 +349,8 @@ class DatabaseFeatures(BaseDatabaseFeatures):
         if self.connection.mysql_is_mariadb:
             return True
         return self.connection.mysql_version >= (8, 0, 13)
+
+    @cached_property
+    def has_native_uuid_field(self):
+        is_mariadb = self.connection.mysql_is_mariadb
+        return is_mariadb and self.connection.mysql_version >= (10, 7)

+ 5 - 1
django/db/backends/mysql/introspection.py

@@ -11,7 +11,8 @@ from django.utils.datastructures import OrderedSet
 
 FieldInfo = namedtuple(
     "FieldInfo",
-    BaseFieldInfo._fields + ("extra", "is_unsigned", "has_json_constraint", "comment"),
+    BaseFieldInfo._fields
+    + ("extra", "is_unsigned", "has_json_constraint", "comment", "data_type"),
 )
 InfoLine = namedtuple(
     "InfoLine",
@@ -62,6 +63,8 @@ class DatabaseIntrospection(BaseDatabaseIntrospection):
                 return "PositiveIntegerField"
             elif field_type == "SmallIntegerField":
                 return "PositiveSmallIntegerField"
+        if description.data_type.upper() == "UUID":
+            return "UUIDField"
         # JSON data type is an alias for LONGTEXT in MariaDB, use check
         # constraints clauses to introspect JSONField.
         if description.has_json_constraint:
@@ -172,6 +175,7 @@ class DatabaseIntrospection(BaseDatabaseIntrospection):
                     info.is_unsigned,
                     line[0] in json_constraints,
                     info.comment,
+                    info.data_type,
                 )
             )
         return fields

+ 4 - 1
django/db/models/fields/related_descriptors.py

@@ -1118,7 +1118,10 @@ def create_forward_many_to_many_manager(superclass, rel, reverse):
             return (
                 queryset,
                 lambda result: tuple(
-                    getattr(result, "_prefetch_related_val_%s" % f.attname)
+                    f.get_db_prep_value(
+                        getattr(result, f"_prefetch_related_val_{f.attname}"),
+                        connection,
+                    )
                     for f in fk.local_related_fields
                 ),
                 lambda inst: tuple(

+ 36 - 0
docs/releases/5.0.txt

@@ -377,6 +377,10 @@ Models
 * :meth:`.QuerySet.aiterator` now supports previous calls to
   ``prefetch_related()``.
 
+* On MariaDB 10.7+, ``UUIDField`` is now created as ``UUID`` column rather than
+  ``CHAR(32)`` column. See the migration guide above for more details on
+  :ref:`migrating-uuidfield`.
+
 Pagination
 ~~~~~~~~~~
 
@@ -483,6 +487,38 @@ Using ``create_defaults__exact`` may now be required with ``QuerySet.update_or_c
 ``create_defaults`` that are used with an ``update_or_create()`` should specify
 the field in the lookup with ``create_defaults__exact``.
 
+.. _migrating-uuidfield:
+
+Migrating existing ``UUIDField`` on MariaDB 10.7+
+-------------------------------------------------
+
+On MariaDB 10.7+, ``UUIDField`` is now created as ``UUID`` column rather than
+``CHAR(32)`` column. As a consequence, any ``UUIDField`` created in
+Django < 5.0 should be replaced with a ``UUIDField`` subclass backed by
+``CHAR(32)``::
+
+    class Char32UUIDField(models.UUIDField):
+        def db_type(self, connection):
+            return "char(32)"
+
+For example::
+
+    class MyModel(models.Model):
+        uuid = models.UUIDField(primary_key=True, default=uuid.uuid4)
+
+Should become::
+
+    class Char32UUIDField(models.UUIDField):
+        def db_type(self, connection):
+            return "char(32)"
+
+
+    class MyModel(models.Model):
+        uuid = Char32UUIDField(primary_key=True, default=uuid.uuid4)
+
+Running the :djadmin:`makemigrations` command will generate a migration
+containing a no-op ``AlterField`` operation.
+
 Miscellaneous
 -------------