Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Fixed #10164 -- Made AutoField increase monotonically on SQLite

Thanks malte for the report.
  • Loading branch information...
commit eade315da1c8372ac1dfcf1fd20ea87f454d71ac 1 parent 630eb05
Chris Wilson qris authored timgraham committed
4 django/db/backends/creation.py
View
@@ -23,6 +23,7 @@ class BaseDatabaseCreation(object):
destruction of test databases.
"""
data_types = {}
+ data_types_suffix = {}
data_type_check_constraints = {}
def __init__(self, connection):
@@ -53,6 +54,7 @@ def sql_create_model(self, model, style, known_models=set()):
qn = self.connection.ops.quote_name
for f in opts.local_fields:
col_type = f.db_type(connection=self.connection)
+ col_type_suffix = f.db_type_suffix(connection=self.connection)
tablespace = f.db_tablespace or opts.db_tablespace
if col_type is None:
# Skip ManyToManyFields, because they're not represented as
@@ -88,6 +90,8 @@ def sql_create_model(self, model, style, known_models=set()):
(model, f))
else:
field_output.extend(ref_output)
+ if col_type_suffix:
+ field_output.append(style.SQL_KEYWORD(col_type_suffix))
table_output.append(' '.join(field_output))
for field_constraints in opts.unique_together:
table_output.append(style.SQL_KEYWORD('UNIQUE') + ' (%s)' %
1  django/db/backends/sqlite3/base.py
View
@@ -106,6 +106,7 @@ class DatabaseFeatures(BaseDatabaseFeatures):
supports_check_constraints = False
autocommits_when_autocommit_is_off = True
supports_paramstyle_pyformat = False
+ supports_sequence_reset = False
@cached_property
def uses_savepoints(self):
3  django/db/backends/sqlite3/creation.py
View
@@ -34,6 +34,9 @@ class DatabaseCreation(BaseDatabaseCreation):
'TextField': 'text',
'TimeField': 'time',
}
+ data_types_suffix = {
+ 'AutoField': 'AUTOINCREMENT',
+ }
def sql_for_pending_references(self, model, style, pending_references):
"SQLite3 doesn't support constraints"
2  django/db/backends/sqlite3/introspection.py
View
@@ -175,7 +175,7 @@ def get_primary_key_column(self, cursor, table_name):
results = results[results.index('(') + 1:results.rindex(')')]
for field_desc in results.split(','):
field_desc = field_desc.strip()
- m = re.search('"(.*)".*PRIMARY KEY$', field_desc)
+ m = re.search('"(.*)".*PRIMARY KEY( AUTOINCREMENT)?$', field_desc)
if m:
return m.groups()[0]
return None
3  django/db/models/fields/__init__.py
View
@@ -395,6 +395,9 @@ def db_parameters(self, connection):
"check": check_string,
}
+ def db_type_suffix(self, connection):
+ return connection.creation.data_types_suffix.get(self.get_internal_type())
+
@property
def unique(self):
return self._unique or self.primary_key
8 docs/releases/1.7.txt
View
@@ -336,6 +336,14 @@ Miscellaneous
when called on an instance without a primary key value. This is done to
avoid mutable ``__hash__`` values in containers.
+* The :meth:`django.db.backends.sqlite3.DatabaseCreation.sql_create_model`
+ will now create :class:`~django.db.models.AutoField` columns in SQLite
+ databases using the ``AUTOINCREMENT`` option, which guarantees monotonic
+ increments. This will cause primary key numbering behavior to change on
+ SQLite, becoming consistent with most other SQL databases. If you have a
+ database created with an older version of Django, you will need to migrate
+ it to take advantage of this feature. See ticket #10164 for details.
Aymeric Augustin Owner

If we want to point to the Trac ticket, we should include an http:// link. But in fact the ticket doesn't provide any migration instructions, so we're really being mean to our users.

I would prefer that we say:

  • this only applies to new databases, not to existing ones;
  • the easiest way to migrate is to dumpdata, rename the database file (keep it as a backup), migrate, loaddata.

It's not hard to explain!

Tim Graham Owner

thanks for the poke, updated in 910a576

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
+
* ``django.contrib.auth.models.AbstractUser`` no longer defines a
:meth:`~django.db.models.Model.get_absolute_url()` method. The old definition
returned ``"/users/%s/" % urlquote(self.username)`` which was arbitrary
20 tests/backends/tests.py
View
@@ -4,6 +4,7 @@
import datetime
from decimal import Decimal
+import re
import threading
import unittest
@@ -108,6 +109,25 @@ def test_order_of_nls_parameters(self):
self.assertEqual(c.fetchone()[0], 1)
+class SQLiteTests(TestCase):
+ longMessage = True
+
+ @unittest.skipUnless(connection.vendor == 'sqlite',
+ "Test valid only for SQLite")
+ def test_autoincrement(self):
+ """
+ Check that auto_increment fields are created with the AUTOINCREMENT
+ keyword in order to be monotonically increasing. Refs #10164.
+ """
+ statements = connection.creation.sql_create_model(models.Square,
+ style=no_style())
+ match = re.search('"id" ([^,]+),', statements[0][0])
+ self.assertIsNotNone(match)
+ self.assertEqual('integer NOT NULL PRIMARY KEY AUTOINCREMENT',
+ match.group(1), "Wrong SQL used to create an auto-increment "
+ "column on SQLite")
+
+
class MySQLTests(TestCase):
@unittest.skipUnless(connection.vendor == 'mysql',
"Test valid only for MySQL")
Aymeric Augustin

If we want to point to the Trac ticket, we should include an http:// link. But in fact the ticket doesn't provide any migration instructions, so we're really being mean to our users.

I would prefer that we say:

  • this only applies to new databases, not to existing ones;
  • the easiest way to migrate is to dumpdata, rename the database file (keep it as a backup), migrate, loaddata.

It's not hard to explain!

Tim Graham

thanks for the poke, updated in 910a576

Please sign in to comment.
Something went wrong with that request. Please try again.