Repository: adamchainz/django-mysql

Issue:
(django_mysql.E016) MySQL 5.7+ is required to use JSONField on AWS RDS MySQL 5.7.19
Summary: 
(django_mysql.E016) MySQL 5.7+ is required to use JSONField on AWS RDS MySQL 5.7.19

Django Version: 1.11.6

Database and version used: MySQL 5.7.19

Version: Django-MySQL 2.1.1

Yes, RDS is 5.7+ but django-mysql says "MySQL 5.7+ is required to use JSONField"


Discussion:
Found Reason why.
I use multiple databases and there are 2 MySQL DB, 5.6 and 5.7(default), so `django-mysql` attempted to check if all the databases' version are above 5.7 or not.

I'll try and PR for this :)

Resolving patch:
diff --git a/django_mysql/models/fields/dynamic.py b/django_mysql/models/fields/dynamic.py
--- a/django_mysql/models/fields/dynamic.py
+++ b/django_mysql/models/fields/dynamic.py
@@ -64,22 +64,23 @@ def _check_mariadb_dyncol(self):
     def _check_mariadb_version(self):
         errors = []
 
-        any_conn_works = True
+        any_conn_works = False
         conn_names = ['default'] + list(set(connections) - {'default'})
         for db in conn_names:
             conn = connections[db]
             if (
                 hasattr(conn, 'mysql_version') and
-                (not connection_is_mariadb(conn) or
-                 conn.mysql_version < (10, 0, 1))
+                connection_is_mariadb(conn) and
+                conn.mysql_version >= (10, 0, 1)
             ):
-                any_conn_works = False
+                any_conn_works = True
 
         if not any_conn_works:
             errors.append(
                 checks.Error(
-                    "MariaDB 10.0.1+ is required to use DynamicField",
-                    hint=None,
+                    'MariaDB 10.0.1+ is required to use DynamicField',
+                    hint='At least one of your DB connections should be to '
+                         'MariaDB 10.0.1+',
                     obj=self,
                     id='django_mysql.E013'
                 )
diff --git a/django_mysql/models/fields/json.py b/django_mysql/models/fields/json.py
--- a/django_mysql/models/fields/json.py
+++ b/django_mysql/models/fields/json.py
@@ -58,21 +58,23 @@ def _check_default(self):
     def _check_mysql_version(self):
         errors = []
 
-        any_conn_works = True
+        any_conn_works = False
         conn_names = ['default'] + list(set(connections) - {'default'})
         for db in conn_names:
             conn = connections[db]
             if (
                 hasattr(conn, 'mysql_version') and
-                (connection_is_mariadb(conn) or conn.mysql_version < (5, 7))
+                not connection_is_mariadb(conn) and
+                conn.mysql_version >= (5, 7)
             ):
-                any_conn_works = False
+                any_conn_works = True
 
         if not any_conn_works:
             errors.append(
                 checks.Error(
-                    "MySQL 5.7+ is required to use JSONField",
-                    hint=None,
+                    'MySQL 5.7+ is required to use JSONField',
+                    hint='At least one of your DB connections should be to '
+                         'MySQL 5.7+',
                     obj=self,
                     id='django_mysql.E016'
                 )


Test patch:
diff --git a/tests/testapp/test_dynamicfield.py b/tests/testapp/test_dynamicfield.py
--- a/tests/testapp/test_dynamicfield.py
+++ b/tests/testapp/test_dynamicfield.py
@@ -335,13 +335,9 @@ class ValidDynamicModel1(TemporaryModel):
 
     wrapper_path = 'django.db.backends.mysql.base.DatabaseWrapper'
 
-    @mock.patch(wrapper_path + '.mysql_version', new=(5, 5, 3))
+    @mock.patch.object(connections['default'], 'mysql_version', new=(5, 5, 3))
+    @mock.patch.object(connections['other'], 'mysql_version', new=(5, 5, 3))
     def test_mariadb_old_version(self):
-        # Uncache cached_property
-        for db in connections:
-            if 'mysql_version' in connections[db].__dict__:
-                del connections[db].__dict__['mysql_version']
-
         class ValidDynamicModel2(TemporaryModel):
             field = DynamicField()
 
@@ -350,6 +346,15 @@ class ValidDynamicModel2(TemporaryModel):
         assert errors[0].id == 'django_mysql.E013'
         assert "MariaDB 10.0.1+ is required" in errors[0].msg
 
+    @mock.patch.object(connections['default'], 'mysql_version', new=(5, 5, 3))
+    @mock.patch.object(connections['other'], 'mysql_version', new=(10, 0, 1))
+    def test_mariadb_one_conn_old_version(self):
+        class ValidDynamicModel3(TemporaryModel):
+            field = DynamicField()
+
+        errors = ValidDynamicModel3.check(actually_check=True)
+        assert len(errors) == 0
+
     @mock.patch(DynamicField.__module__ + '.mariadb_dyncol', new=None)
     def test_mariadb_dyncol_missing(self):
         errors = DynamicModel.check()
diff --git a/tests/testapp/test_jsonfield.py b/tests/testapp/test_jsonfield.py
--- a/tests/testapp/test_jsonfield.py
+++ b/tests/testapp/test_jsonfield.py
@@ -501,15 +501,9 @@ class InvalidJSONModel3(TemporaryModel):
         assert errors[0].id == 'django_mysql.E016'
         assert "MySQL 5.7+ is required" in errors[0].msg
 
-    wrapper_path = 'django.db.backends.mysql.base.DatabaseWrapper'
-
-    @mock.patch(wrapper_path + '.mysql_version', new=(5, 5, 3))
+    @mock.patch.object(connections['default'], 'mysql_version', new=(5, 5, 3))
+    @mock.patch.object(connections['other'], 'mysql_version', new=(5, 5, 1))
     def test_mysql_old_version(self):
-        # Uncache cached_property
-        for db in connections:
-            if 'mysql_version' in connections[db].__dict__:
-                del connections[db].__dict__['mysql_version']
-
         class InvalidJSONModel4(TemporaryModel):
             field = JSONField()
 
@@ -518,6 +512,15 @@ class InvalidJSONModel4(TemporaryModel):
         assert errors[0].id == 'django_mysql.E016'
         assert "MySQL 5.7+ is required" in errors[0].msg
 
+    @mock.patch.object(connections['default'], 'mysql_version', new=(5, 5, 3))
+    @mock.patch.object(connections['other'], 'mysql_version', new=(5, 7, 1))
+    def test_mysql_one_conn_old_version(self):
+        class InvalidJSONModel5(TemporaryModel):
+            field = JSONField()
+
+        errors = InvalidJSONModel5.check(actually_check=True)
+        assert len(errors) == 0
+
 
 class TestFormField(JSONFieldTestCase):