Fixed #3790 -- Fixed a problem with sequence resetting during fixture loads when using Postgres. Thanks to Jon Ballard and scott@staplefish.com for the report, and to Zach Thompson for suggesting a solution.

git-svn-id: http://code.djangoproject.com/svn/django/trunk@4937 bcc190cf-cafb-0310-a4f2-bffc1f526a37
This commit is contained in:
Russell Keith-Magee 2007-04-06 02:25:58 +00:00
parent e339a41222
commit dabd96646c
14 changed files with 121 additions and 26 deletions

View File

@ -192,6 +192,7 @@ answer newbie questions, and generally made Django that much better:
Ville Säävuori <http://www.unessa.net/> Ville Säävuori <http://www.unessa.net/>
Tyson Tate <tyson@fallingbullets.com> Tyson Tate <tyson@fallingbullets.com>
thebjorn <bp@datakortet.no> thebjorn <bp@datakortet.no>
Zach Thompson <zthompson47@gmail.com>
Tom Tobin Tom Tobin
Joe Topjian <http://joe.terrarum.net/geek/code/python/django/> Joe Topjian <http://joe.terrarum.net/geek/code/python/django/>
torne-django@wolfpuppy.org.uk torne-django@wolfpuppy.org.uk

View File

@ -410,30 +410,10 @@ get_sql_initial_data.help_doc = "RENAMED: see 'sqlcustom'"
get_sql_initial_data.args = '' get_sql_initial_data.args = ''
def get_sql_sequence_reset(app): def get_sql_sequence_reset(app):
"Returns a list of the SQL statements to reset PostgreSQL sequences for the given app." "Returns a list of the SQL statements to reset sequences for the given app."
from django.db import backend, models from django.db import backend, models
output = [] return backend.get_sql_sequence_reset(style, models.get_models(app))
for model in models.get_models(app): get_sql_sequence_reset.help_doc = "Prints the SQL statements for resetting sequences for the given app name(s)."
for f in model._meta.fields:
if isinstance(f, models.AutoField):
output.append("%s setval('%s', (%s max(%s) %s %s));" % \
(style.SQL_KEYWORD('SELECT'),
style.SQL_FIELD('%s_%s_seq' % (model._meta.db_table, f.column)),
style.SQL_KEYWORD('SELECT'),
style.SQL_FIELD(backend.quote_name(f.column)),
style.SQL_KEYWORD('FROM'),
style.SQL_TABLE(backend.quote_name(model._meta.db_table))))
break # Only one AutoField is allowed per model, so don't bother continuing.
for f in model._meta.many_to_many:
output.append("%s setval('%s', (%s max(%s) %s %s));" % \
(style.SQL_KEYWORD('SELECT'),
style.SQL_FIELD('%s_id_seq' % f.m2m_db_table()),
style.SQL_KEYWORD('SELECT'),
style.SQL_FIELD(backend.quote_name('id')),
style.SQL_KEYWORD('FROM'),
style.SQL_TABLE(f.m2m_db_table())))
return output
get_sql_sequence_reset.help_doc = "Prints the SQL statements for resetting PostgreSQL sequences for the given app name(s)."
get_sql_sequence_reset.args = APP_ARGS get_sql_sequence_reset.args = APP_ARGS
def get_sql_indexes(app): def get_sql_indexes(app):
@ -1333,12 +1313,13 @@ def load_data(fixture_labels, verbosity=1):
"Installs the provided fixture file(s) as data in the database." "Installs the provided fixture file(s) as data in the database."
from django.db.models import get_apps from django.db.models import get_apps
from django.core import serializers from django.core import serializers
from django.db import connection, transaction from django.db import connection, transaction, backend
from django.conf import settings from django.conf import settings
import sys import sys
# Keep a count of the installed objects and fixtures # Keep a count of the installed objects and fixtures
count = [0,0] count = [0,0]
models = set()
humanize = lambda dirname: dirname and "'%s'" % dirname or 'absolute path' humanize = lambda dirname: dirname and "'%s'" % dirname or 'absolute path'
@ -1401,6 +1382,7 @@ def load_data(fixture_labels, verbosity=1):
objects = serializers.deserialize(format, fixture) objects = serializers.deserialize(format, fixture)
for obj in objects: for obj in objects:
count[0] += 1 count[0] += 1
models.add(obj.object.__class__)
obj.save() obj.save()
label_found = True label_found = True
except Exception, e: except Exception, e:
@ -1422,6 +1404,12 @@ def load_data(fixture_labels, verbosity=1):
else: else:
if verbosity > 0: if verbosity > 0:
print "Installed %d object(s) from %d fixture(s)" % tuple(count) print "Installed %d object(s) from %d fixture(s)" % tuple(count)
sequence_sql = backend.get_sql_sequence_reset(style, models)
if sequence_sql:
if verbosity > 1:
print "Resetting sequences"
for line in sequence_sql:
cursor.execute(line)
transaction.commit() transaction.commit()
transaction.leave_transaction_management() transaction.leave_transaction_management()

View File

@ -151,6 +151,11 @@ def get_sql_flush(sql_styler, full_table_list):
sql_styler.SQL_FIELD(quote_name(table)) sql_styler.SQL_FIELD(quote_name(table))
) for table in full_table_list] ) for table in full_table_list]
def get_sql_sequence_reset(style, model_list):
"Returns a list of the SQL statements to reset sequences for the given models."
# No sequence reset required
return []
OPERATOR_MAPPING = { OPERATOR_MAPPING = {
'exact': '= %s', 'exact': '= %s',
'iexact': 'LIKE %s', 'iexact': 'LIKE %s',

View File

@ -40,5 +40,6 @@ get_deferrable_sql = complain
get_fulltext_search_sql = complain get_fulltext_search_sql = complain
get_drop_foreignkey_sql = complain get_drop_foreignkey_sql = complain
get_sql_flush = complain get_sql_flush = complain
get_sql_sequence_reset = complain
OPERATOR_MAPPING = {} OPERATOR_MAPPING = {}

View File

@ -215,6 +215,11 @@ def get_sql_flush(style, tables, sequences):
else: else:
return [] return []
def get_sql_sequence_reset(style, model_list):
"Returns a list of the SQL statements to reset sequences for the given models."
# No sequence reset required
return []
OPERATOR_MAPPING = { OPERATOR_MAPPING = {
'exact': '= %s', 'exact': '= %s',
'iexact': 'LIKE %s', 'iexact': 'LIKE %s',

View File

@ -217,6 +217,11 @@ def get_sql_flush(style, tables, sequences):
else: else:
return [] return []
def get_sql_sequence_reset(style, model_list):
"Returns a list of the SQL statements to reset sequences for the given models."
# No sequence reset required
return []
OPERATOR_MAPPING = { OPERATOR_MAPPING = {
'exact': '= %s', 'exact': '= %s',
'iexact': 'LIKE %s', 'iexact': 'LIKE %s',

View File

@ -134,6 +134,10 @@ def get_sql_flush(style, tables, sequences):
style.SQL_FIELD(quote_name(table)) style.SQL_FIELD(quote_name(table))
) for table in tables] ) for table in tables]
def get_sql_sequence_reset(style, model_list):
"Returns a list of the SQL statements to reset sequences for the given models."
# No sequence reset required
return []
OPERATOR_MAPPING = { OPERATOR_MAPPING = {
'exact': '= %s', 'exact': '= %s',

View File

@ -213,6 +213,30 @@ def get_sql_flush(style, tables, sequences):
else: else:
return [] return []
def get_sql_sequence_reset(style, model_list):
"Returns a list of the SQL statements to reset sequences for the given models."
from django.db import models
output = []
for model in model_list:
for f in model._meta.fields:
if isinstance(f, models.AutoField):
output.append("%s setval('%s', (%s max(%s) %s %s));" % \
(style.SQL_KEYWORD('SELECT'),
style.SQL_FIELD('%s_%s_seq' % (model._meta.db_table, f.column)),
style.SQL_KEYWORD('SELECT'),
style.SQL_FIELD(quote_name(f.column)),
style.SQL_KEYWORD('FROM'),
style.SQL_TABLE(quote_name(model._meta.db_table))))
break # Only one AutoField is allowed per model, so don't bother continuing.
for f in model._meta.many_to_many:
output.append("%s setval('%s', (%s max(%s) %s %s));" % \
(style.SQL_KEYWORD('SELECT'),
style.SQL_FIELD('%s_id_seq' % f.m2m_db_table()),
style.SQL_KEYWORD('SELECT'),
style.SQL_FIELD(quote_name('id')),
style.SQL_KEYWORD('FROM'),
style.SQL_TABLE(f.m2m_db_table())))
return output
# Register these custom typecasts, because Django expects dates/times to be # Register these custom typecasts, because Django expects dates/times to be
# in Python's native (standard-library) datetime/time format, whereas psycopg # in Python's native (standard-library) datetime/time format, whereas psycopg

View File

@ -170,6 +170,31 @@ def get_sql_flush(style, tables, sequences):
else: else:
return [] return []
def get_sql_sequence_reset(style, model_list):
"Returns a list of the SQL statements to reset sequences for the given models."
from django.db import models
output = []
for model in model_list:
for f in model._meta.fields:
if isinstance(f, models.AutoField):
output.append("%s setval('%s', (%s max(%s) %s %s));" % \
(style.SQL_KEYWORD('SELECT'),
style.SQL_FIELD('%s_%s_seq' % (model._meta.db_table, f.column)),
style.SQL_KEYWORD('SELECT'),
style.SQL_FIELD(quote_name(f.column)),
style.SQL_KEYWORD('FROM'),
style.SQL_TABLE(quote_name(model._meta.db_table))))
break # Only one AutoField is allowed per model, so don't bother continuing.
for f in model._meta.many_to_many:
output.append("%s setval('%s', (%s max(%s) %s %s));" % \
(style.SQL_KEYWORD('SELECT'),
style.SQL_FIELD('%s_id_seq' % f.m2m_db_table()),
style.SQL_KEYWORD('SELECT'),
style.SQL_FIELD(quote_name('id')),
style.SQL_KEYWORD('FROM'),
style.SQL_TABLE(f.m2m_db_table())))
return output
OPERATOR_MAPPING = { OPERATOR_MAPPING = {
'exact': '= %s', 'exact': '= %s',
'iexact': 'ILIKE %s', 'iexact': 'ILIKE %s',

View File

@ -170,6 +170,11 @@ def get_sql_flush(style, tables, sequences):
# get_sql_flush() implementations). Just return SQL at this point # get_sql_flush() implementations). Just return SQL at this point
return sql return sql
def get_sql_sequence_reset(style, model_list):
"Returns a list of the SQL statements to reset sequences for the given models."
# No sequence reset required
return []
def _sqlite_date_trunc(lookup_type, dt): def _sqlite_date_trunc(lookup_type, dt):
try: try:
dt = util.typecast_timestamp(dt) dt = util.typecast_timestamp(dt)

View File

@ -366,7 +366,7 @@ Prints the DROP TABLE SQL, then the CREATE TABLE SQL, for the given appnames.
sqlsequencereset [appname appname ...] sqlsequencereset [appname appname ...]
---------------------------------------------- ----------------------------------------------
Prints the SQL statements for resetting PostgreSQL sequences for the given Prints the SQL statements for resetting sequences for the given
appnames. appnames.
See http://simon.incutio.com/archive/2004/04/21/postgres for more information. See http://simon.incutio.com/archive/2004/04/21/postgres for more information.

View File

@ -0,0 +1,10 @@
[
{
"pk": "1",
"model": "fixtures_regress.animal",
"fields": {
"name": "Lion",
"latin_name": "Panthera leo"
}
}
]

View File

@ -0,0 +1,22 @@
from django.db import models
class Animal(models.Model):
name = models.CharField(maxlength=150)
latin_name = models.CharField(maxlength=150)
def __str__(self):
return self.common_name
__test__ = {'API_TESTS':"""
>>> from django.core import management
# Load a fixture that uses PK=1
>>> management.load_data(['sequence'], verbosity=0)
# Create a new animal. Without a sequence reset, this new object
# will take a PK of 1 (on Postgres), and the save will fail.
# This is a regression test for ticket #3790.
>>> animal = Animal(name='Platypus', latin_name='Ornithorhynchus anatinus')
>>> animal.save()
"""}