Skip to content

Commit c4e62ef

Browse files
committed
Fixed django#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. git-svn-id: http://code.djangoproject.com/svn/django/trunk@17933 bcc190cf-cafb-0310-a4f2-bffc1f526a37
1 parent 612247b commit c4e62ef

File tree

5 files changed

+49
-3
lines changed

5 files changed

+49
-3
lines changed

django/db/backends/__init__.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -770,6 +770,14 @@ def prep_for_like_query(self, x):
770770
# need not necessarily be implemented using "LIKE" in the backend.
771771
prep_for_iexact_query = prep_for_like_query
772772

773+
def validate_autopk_value(self, value):
774+
"""
775+
Certain backends do not accept some values for "serial" fields
776+
(for example zero in MySQL). This method will raise a ValueError
777+
if the value is invalid, otherwise returns validated value.
778+
"""
779+
return value
780+
773781
def value_to_db_date(self, value):
774782
"""
775783
Transform a date value to an object compatible with what is expected

django/db/backends/mysql/base.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -276,6 +276,13 @@ def sql_flush(self, style, tables, sequences):
276276
else:
277277
return []
278278

279+
def validate_autopk_value(self, value):
280+
# MySQLism: zero in AUTO_INCREMENT field does not work. Refs #17653.
281+
if value == 0:
282+
raise ValueError('The database backend does not accept 0 as a '
283+
'value for AutoField.')
284+
return value
285+
279286
def value_to_db_datetime(self, value):
280287
if value is None:
281288
return None

django/db/models/fields/__init__.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -531,6 +531,12 @@ def to_python(self, value):
531531
def validate(self, value, model_instance):
532532
pass
533533

534+
def get_db_prep_value(self, value, connection, prepared=False):
535+
if not prepared:
536+
value = self.get_prep_value(value)
537+
value = connection.ops.validate_autopk_value(value)
538+
return value
539+
534540
def get_prep_value(self, value):
535541
if value is None:
536542
return None

tests/regressiontests/backends/tests.py

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,8 @@
1313
from django.db.backends.signals import connection_created
1414
from django.db.backends.postgresql_psycopg2 import version as pg_version
1515
from django.db.utils import ConnectionHandler, DatabaseError, load_backend
16-
from django.test import TestCase, skipUnlessDBFeature, TransactionTestCase
16+
from django.test import (TestCase, skipUnlessDBFeature, skipIfDBFeature,
17+
TransactionTestCase)
1718
from django.test.utils import override_settings
1819
from django.utils import unittest
1920

@@ -642,3 +643,15 @@ def test_old_style_backends_raise_useful_exception(self):
642643
self.assertRaisesRegexp(ImproperlyConfigured,
643644
"Try using django.db.backends.sqlite3 instead",
644645
load_backend, 'sqlite3')
646+
647+
648+
class MySQLPKZeroTests(TestCase):
649+
"""
650+
Zero as id for AutoField should raise exception in MySQL, because MySQL
651+
does not allow zero for automatic primary key.
652+
"""
653+
654+
@skipIfDBFeature('allows_primary_key_0')
655+
def test_zero_as_autoval(self):
656+
with self.assertRaises(ValueError):
657+
models.Square.objects.create(id=0, root=0, square=1)

tests/regressiontests/bulk_create/tests.py

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
from operator import attrgetter
44

5-
from django.test import TestCase, skipUnlessDBFeature
5+
from django.test import TestCase, skipIfDBFeature, skipUnlessDBFeature
66

77
from .models import Country, Restaurant, Pizzeria, State
88

@@ -56,4 +56,16 @@ def test_non_auto_increment_pk(self):
5656
])
5757
self.assertQuerysetEqual(State.objects.order_by("two_letter_code"), [
5858
"CA", "IL", "ME", "NY",
59-
], attrgetter("two_letter_code"))
59+
], attrgetter("two_letter_code"))
60+
61+
@skipIfDBFeature('allows_primary_key_0')
62+
def test_zero_as_autoval(self):
63+
"""
64+
Zero as id for AutoField should raise exception in MySQL, because MySQL
65+
does not allow zero for automatic primary key.
66+
"""
67+
68+
valid_country = Country(name='Germany', iso_two_letter='DE')
69+
invalid_country = Country(id=0, name='Poland', iso_two_letter='PL')
70+
with self.assertRaises(ValueError):
71+
Country.objects.bulk_create([valid_country, invalid_country])

0 commit comments

Comments
 (0)