Commits

Anonymous committed b6db1c1

Fixed #17653 -- Changed MySQL backend to raise a ValueError if zero is used as an AutoField value.

Thanks to Sylvain Lebon for the report, krzysiumed for the patch and charettes and claudep for reviews.

  • Participants
  • Parent commits c612bfc

Comments (0)

Files changed (5)

File django/db/backends/__init__.py

     # need not necessarily be implemented using "LIKE" in the backend.
     prep_for_iexact_query = prep_for_like_query
 
+    def validate_autopk_value(self, value):
+        """
+        Certain backends do not accept some values for "serial" fields
+        (for example zero in MySQL). This method will raise a ValueError
+        if the value is invalid, otherwise returns validated value.
+        """
+        return value
+
     def value_to_db_date(self, value):
         """
         Transform a date value to an object compatible with what is expected

File django/db/backends/mysql/base.py

         else:
             return []
 
+    def validate_autopk_value(self, value):
+        # MySQLism: zero in AUTO_INCREMENT field does not work. Refs #17653.
+        if value == 0:
+            raise ValueError('The database backend does not accept 0 as a '
+                             'value for AutoField.')
+        return value
+
     def value_to_db_datetime(self, value):
         if value is None:
             return None

File django/db/models/fields/__init__.py

     def validate(self, value, model_instance):
         pass
 
+    def get_db_prep_value(self, value, connection, prepared=False):
+        if not prepared:
+            value = self.get_prep_value(value)
+            value = connection.ops.validate_autopk_value(value)
+        return value
+
     def get_prep_value(self, value):
         if value is None:
             return None

File tests/regressiontests/backends/tests.py

 from django.db.backends.signals import connection_created
 from django.db.backends.postgresql_psycopg2 import version as pg_version
 from django.db.utils import ConnectionHandler, DatabaseError, load_backend
-from django.test import TestCase, skipUnlessDBFeature, TransactionTestCase
+from django.test import (TestCase, skipUnlessDBFeature, skipIfDBFeature,
+    TransactionTestCase)
 from django.test.utils import override_settings
 from django.utils import unittest
 
         self.assertRaisesRegexp(ImproperlyConfigured,
             "Try using django.db.backends.sqlite3 instead",
             load_backend, 'sqlite3')
+
+
+class MySQLPKZeroTests(TestCase):
+    """
+    Zero as id for AutoField should raise exception in MySQL, because MySQL
+    does not allow zero for automatic primary key.
+    """
+
+    @skipIfDBFeature('allows_primary_key_0')
+    def test_zero_as_autoval(self):
+        with self.assertRaises(ValueError):
+            models.Square.objects.create(id=0, root=0, square=1)

File tests/regressiontests/bulk_create/tests.py

 
 from operator import attrgetter
 
-from django.test import TestCase, skipUnlessDBFeature
+from django.test import TestCase, skipIfDBFeature, skipUnlessDBFeature
 
 from .models import Country, Restaurant, Pizzeria, State
 
             ])
         self.assertQuerysetEqual(State.objects.order_by("two_letter_code"), [
             "CA", "IL", "ME", "NY",
-        ], attrgetter("two_letter_code"))
+        ], attrgetter("two_letter_code"))
+
+    @skipIfDBFeature('allows_primary_key_0')
+    def test_zero_as_autoval(self):
+        """
+        Zero as id for AutoField should raise exception in MySQL, because MySQL
+        does not allow zero for automatic primary key.
+        """
+
+        valid_country = Country(name='Germany', iso_two_letter='DE')
+        invalid_country = Country(id=0, name='Poland', iso_two_letter='PL')
+        with self.assertRaises(ValueError):
+            Country.objects.bulk_create([valid_country, invalid_country])