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.


git-svn-id: http://code.djangoproject.com/svn/django/trunk@17933 bcc190cf-cafb-0310-a4f2-bffc1f526a37
This commit is contained in:
Anssi Kääriäinen 2012-04-24 18:03:14 +00:00
parent 612247b3a0
commit c4e62eff90
5 changed files with 49 additions and 3 deletions

View File

@ -770,6 +770,14 @@ class BaseDatabaseOperations(object):
# need not necessarily be implemented using "LIKE" in the backend. # need not necessarily be implemented using "LIKE" in the backend.
prep_for_iexact_query = prep_for_like_query 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): def value_to_db_date(self, value):
""" """
Transform a date value to an object compatible with what is expected Transform a date value to an object compatible with what is expected

View File

@ -276,6 +276,13 @@ class DatabaseOperations(BaseDatabaseOperations):
else: else:
return [] 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): def value_to_db_datetime(self, value):
if value is None: if value is None:
return None return None

View File

@ -531,6 +531,12 @@ class AutoField(Field):
def validate(self, value, model_instance): def validate(self, value, model_instance):
pass 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): def get_prep_value(self, value):
if value is None: if value is None:
return None return None

View File

@ -13,7 +13,8 @@ from django.db import (backend, connection, connections, DEFAULT_DB_ALIAS,
from django.db.backends.signals import connection_created from django.db.backends.signals import connection_created
from django.db.backends.postgresql_psycopg2 import version as pg_version from django.db.backends.postgresql_psycopg2 import version as pg_version
from django.db.utils import ConnectionHandler, DatabaseError, load_backend 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.test.utils import override_settings
from django.utils import unittest from django.utils import unittest
@ -642,3 +643,15 @@ class BackendLoadingTests(TestCase):
self.assertRaisesRegexp(ImproperlyConfigured, self.assertRaisesRegexp(ImproperlyConfigured,
"Try using django.db.backends.sqlite3 instead", "Try using django.db.backends.sqlite3 instead",
load_backend, 'sqlite3') 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)

View File

@ -2,7 +2,7 @@ from __future__ import absolute_import
from operator import attrgetter from operator import attrgetter
from django.test import TestCase, skipUnlessDBFeature from django.test import TestCase, skipIfDBFeature, skipUnlessDBFeature
from .models import Country, Restaurant, Pizzeria, State from .models import Country, Restaurant, Pizzeria, State
@ -56,4 +56,16 @@ class BulkCreateTests(TestCase):
]) ])
self.assertQuerysetEqual(State.objects.order_by("two_letter_code"), [ self.assertQuerysetEqual(State.objects.order_by("two_letter_code"), [
"CA", "IL", "ME", "NY", "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])