Django

Code

Changeset 4659

Show
Ignore:
Timestamp:
03/01/07 07:11:08 (1 year ago)
Author:
russellm
Message:

Fixes #2333 -- Added test fixtures framework.

Files:

Legend:

Unmodified
Added
Removed
Modified
Copied
Moved
  • django/trunk/django/conf/global_settings.py

    r4631 r4659  
    320320# If None, a name of 'test_' + DATABASE_NAME will be assumed 
    321321TEST_DATABASE_NAME = None 
     322 
     323############ 
     324# FIXTURES # 
     325############ 
     326 
     327# The list of directories to search for fixtures 
     328FIXTURE_DIRS = () 
  • django/trunk/django/core/management.py

    r4610 r4659  
    6868    cursor = connection.cursor() 
    6969    return get_introspection_module().get_table_list(cursor) 
     70 
     71def _get_sequence_list(): 
     72    "Returns a list of information about all DB sequences for all models in all apps" 
     73    from django.db import models 
     74 
     75    apps = models.get_apps() 
     76    sequence_list = [] 
     77 
     78    for app in apps: 
     79        for model in models.get_models(app): 
     80            for f in model._meta.fields: 
     81                if isinstance(f, models.AutoField): 
     82                    sequence_list.append({'table':model._meta.db_table,'column':f.column,}) 
     83                    break # Only one AutoField is allowed per model, so don't bother continuing. 
     84 
     85            for f in model._meta.many_to_many: 
     86                sequence_list.append({'table':f.m2m_db_table(),'column':None,}) 
     87 
     88    return sequence_list 
    7089 
    7190# If the foreign key points to an AutoField, a PositiveIntegerField or a 
     
    335354get_sql_reset.args = APP_ARGS 
    336355 
    337 def get_sql_initial_data_for_model(model): 
     356def get_sql_flush(): 
     357    "Returns a list of the SQL statements used to flush the database" 
     358    from django.db import backend 
     359    statements = backend.get_sql_flush(style, _get_table_list(), _get_sequence_list()) 
     360    return statements 
     361get_sql_flush.help_doc = "Returns a list of the SQL statements required to return all tables in the database to the state they were in just after they were installed." 
     362get_sql_flush.args = '' 
     363 
     364def get_custom_sql_for_model(model): 
    338365    from django.db import models 
    339366    from django.conf import settings 
     
    362389    return output 
    363390 
    364 def get_sql_initial_data(app): 
    365     "Returns a list of the initial INSERT SQL statements for the given app." 
     391def get_custom_sql(app): 
     392    "Returns a list of the custom table modifying SQL statements for the given app." 
    366393    from django.db.models import get_models 
    367394    output = [] 
     
    371398 
    372399    for model in app_models: 
    373         output.extend(get_sql_initial_data_for_model(model)) 
     400        output.extend(get_custom_sql_for_model(model)) 
    374401 
    375402    return output 
    376 get_sql_initial_data.help_doc = "Prints the initial INSERT SQL statements for the given app name(s)." 
    377 get_sql_initial_data.args = APP_ARGS 
     403get_custom_sql.help_doc = "Prints the custom table modifying SQL statements for the given app name(s)." 
     404get_custom_sql.args = APP_ARGS 
     405 
     406def get_sql_initial_data(apps): 
     407    "Returns a list of the initial INSERT SQL statements for the given app." 
     408    return style.ERROR("This action has been renamed. Try './manage.py sqlcustom %s'." % ' '.join(apps and apps or ['app1', 'app2'])) 
     409get_sql_initial_data.help_doc = "RENAMED: see 'sqlcustom'" 
     410get_sql_initial_data.args = '' 
    378411 
    379412def get_sql_sequence_reset(app): 
     
    433466def get_sql_all(app): 
    434467    "Returns a list of CREATE TABLE SQL, initial-data inserts, and CREATE INDEX SQL for the given module." 
    435     return get_sql_create(app) + get_sql_initial_data(app) + get_sql_indexes(app) 
     468    return get_sql_create(app) + get_custom_sql(app) + get_sql_indexes(app) 
    436469get_sql_all.help_doc = "Prints the CREATE TABLE, initial-data and CREATE INDEX SQL statements for the given model module name(s)." 
    437470get_sql_all.args = APP_ARGS 
     471 
     472def _emit_post_sync_signal(created_models, verbosity, interactive): 
     473    from django.db import models 
     474    from django.dispatch import dispatcher 
     475    # Emit the post_sync signal for every application. 
     476    for app in models.get_apps(): 
     477        app_name = app.__name__.split('.')[-2] 
     478        if verbosity >= 2: 
     479            print "Running post-sync handlers for application", app_name 
     480        dispatcher.send(signal=models.signals.post_syncdb, sender=app, 
     481            app=app, created_models=created_models, 
     482            verbosity=verbosity, interactive=interactive) 
    438483 
    439484def syncdb(verbosity=1, interactive=True): 
    440485    "Creates the database tables for all apps in INSTALLED_APPS whose tables haven't already been created." 
    441486    from django.db import connection, transaction, models, get_creation_module 
    442     from django.db.models import signals 
    443487    from django.conf import settings 
    444     from django.dispatch import dispatcher 
    445488 
    446489    disable_termcolors() 
     
    504547    # Send the post_syncdb signal, so individual apps can do whatever they need 
    505548    # to do at this point. 
     549    _emit_post_sync_signal(created_models, verbosity, interactive) 
     550 
     551    # Install custom SQL for the app (but only if this  
     552    # is a model we've just created) 
    506553    for app in models.get_apps(): 
    507         app_name = app.__name__.split('.')[-2] 
    508         if verbosity >= 2: 
    509             print "Running post-sync handlers for application", app_name 
    510         dispatcher.send(signal=signals.post_syncdb, sender=app, 
    511             app=app, created_models=created_models, 
    512             verbosity=verbosity, interactive=interactive) 
    513  
    514         # Install initial data for the app (but only if this is a model we've 
    515         # just created) 
    516554        for model in models.get_models(app): 
    517555            if model in created_models: 
    518                 initial_sql = get_sql_initial_data_for_model(model) 
    519                 if initial_sql: 
     556                custom_sql = get_custom_sql_for_model(model) 
     557                if custom_sql: 
    520558                    if verbosity >= 1: 
    521                         print "Installing initial data for %s.%s model" % (app_name, model._meta.object_name) 
     559                        print "Installing custom SQL for %s.%s model" % (app_name, model._meta.object_name) 
    522560                    try: 
    523                         for sql in initial_sql: 
     561                        for sql in custom_sql: 
    524562                            cursor.execute(sql) 
    525563                    except Exception, e: 
    526                         sys.stderr.write("Failed to install initial SQL data for %s.%s model: %s" % \ 
     564                        sys.stderr.write("Failed to install custom SQL for %s.%s model: %s" % \ 
    527565                                            (app_name, model._meta.object_name, e)) 
    528566                        transaction.rollback_unless_managed() 
     
    549587                        transaction.commit_unless_managed() 
    550588 
    551 syncdb.args = '' 
     589    # Install the 'initialdata' fixture, using format discovery 
     590    load_data(['initial_data'], verbosity=verbosity) 
     591syncdb.help_doc = "Create the database tables for all apps in INSTALLED_APPS whose tables haven't already been created." 
     592syncdb.args = '[--verbosity] [--interactive]' 
    552593 
    553594def get_admin_index(app): 
     
    602643diffsettings.args = "" 
    603644 
    604 def install(app): 
    605     "Executes the equivalent of 'get_sql_all' in the current database." 
    606     from django.db import connection, transaction 
    607  
    608     app_name = app.__name__.split('.')[-2] 
    609  
    610     disable_termcolors() 
    611  
    612     # First, try validating the models. 
    613     _check_for_validation_errors(app) 
    614  
    615     sql_list = get_sql_all(app) 
    616  
    617     try: 
    618         cursor = connection.cursor() 
    619         for sql in sql_list: 
    620             cursor.execute(sql) 
    621     except Exception, e: 
    622         sys.stderr.write(style.ERROR("""Error: %s couldn't be installed. Possible reasons: 
    623   * The database isn't running or isn't configured correctly. 
    624   * At least one of the database tables already exists. 
    625   * The SQL was invalid. 
    626 Hint: Look at the output of 'django-admin.py sqlall %s'. That's the SQL this command wasn't able to run. 
    627 The full error: """ % (app_name, app_name)) + style.ERROR_OUTPUT(str(e)) + '\n') 
    628         transaction.rollback_unless_managed() 
    629         sys.exit(1) 
    630     transaction.commit_unless_managed() 
    631 install.help_doc = "Executes ``sqlall`` for the given app(s) in the current database." 
    632 install.args = APP_ARGS 
    633  
    634645def reset(app, interactive=True): 
    635646    "Executes the equivalent of 'get_sql_reset' in the current database." 
     
    673684        print "Reset cancelled." 
    674685reset.help_doc = "Executes ``sqlreset`` for the given app(s) in the current database." 
    675 reset.args = APP_ARGS 
     686reset.args = '[--interactive]' + APP_ARGS 
     687 
     688def flush(verbosity=1, interactive=True): 
     689    "Returns all tables in the database to the same state they were in immediately after syncdb." 
     690    from django.conf import settings 
     691    from django.db import connection, transaction, models 
     692    from django.dispatch import dispatcher 
     693     
     694    disable_termcolors() 
     695 
     696    # First, try validating the models. 
     697    _check_for_validation_errors() 
     698 
     699    # Import the 'management' module within each installed app, to register 
     700    # dispatcher events. 
     701    for app_name in settings.INSTALLED_APPS: 
     702        try: 
     703            __import__(app_name + '.management', {}, {}, ['']) 
     704        except ImportError: 
     705            pass 
     706     
     707    sql_list = get_sql_flush() 
     708 
     709    if interactive: 
     710        confirm = raw_input(""" 
     711You have requested a flush of the database. 
     712This will IRREVERSIBLY DESTROY all data currently in the database, 
     713and return each table to the state it was in after syncdb. 
     714Are you sure you want to do this? 
     715 
     716Type 'yes' to continue, or 'no' to cancel: """) 
     717    else: 
     718        confirm = 'yes' 
     719 
     720    if confirm == 'yes': 
     721        try: 
     722            cursor = connection.cursor() 
     723            for sql in sql_list: 
     724                cursor.execute(sql) 
     725        except Exception, e: 
     726            sys.stderr.write(style.ERROR("""Error: Database %s couldn't be flushed. Possible reasons: 
     727  * The database isn't running or isn't configured correctly. 
     728  * At least one of the expected database tables doesn't exist. 
     729  * The SQL was invalid. 
     730Hint: Look at the output of 'django-admin.py sqlflush'. That's the SQL this command wasn't able to run. 
     731The full error: """ % settings.DATABASE_NAME + style.ERROR_OUTPUT(str(e)) + '\n')) 
     732            transaction.rollback_unless_managed() 
     733            sys.exit(1) 
     734        transaction.commit_unless_managed() 
     735 
     736        # Emit the post sync signal. This allows individual 
     737        # applications to respond as if the database had been 
     738        # sync'd from scratch. 
     739        _emit_post_sync_signal(models.get_models(), verbosity, interactive) 
     740         
     741        # Reinstall the initial_data fixture 
     742        load_data(['initial_data'], verbosity=verbosity) 
     743         
     744    else: 
     745        print "Flush cancelled." 
     746flush.help_doc = "Executes ``sqlflush`` on the current database." 
     747flush.args = '[--verbosity] [--interactive]' 
    676748 
    677749def _start_helper(app_or_project, name, directory, other_name=''): 
     
    756828    yield "# Feel free to rename the models, but don't rename db_table values or field names." 
    757829    yield "#" 
    758     yield "# Also note: You'll have to insert the output of 'django-admin.py sqlinitialdata [appname]'" 
     830    yield "# Also note: You'll have to insert the output of 'django-admin.py sqlcustom [appname]'" 
    759831    yield "# into your database." 
    760832    yield '' 
     
    12521324test.args = '[--verbosity] ' + APP_ARGS 
    12531325 
     1326def load_data(fixture_labels, verbosity=1): 
     1327    "Installs the provided fixture file(s) as data in the database." 
     1328    from django.db.models import get_apps 
     1329    from django.core import serializers 
     1330    from django.db import connection, transaction 
     1331    from django.conf import settings 
     1332    import sys 
     1333      
     1334    # Keep a count of the installed objects and fixtures 
     1335    count = [0,0] 
     1336     
     1337    humanize = lambda dirname: dirname and "'%s'" % dirname or 'absolute path' 
     1338 
     1339    # Get a cursor (even though we don't need one yet). This has 
     1340    # the side effect of initializing the test database (if  
     1341    # it isn't already initialized). 
     1342    cursor = connection.cursor() 
     1343     
     1344    # Start transaction management. All fixtures are installed in a  
     1345    # single transaction to ensure that all references are resolved. 
     1346    transaction.commit_unless_managed() 
     1347    transaction.enter_transaction_management() 
     1348    transaction.managed(True) 
     1349     
     1350    app_fixtures = [os.path.join(os.path.dirname(app.__file__),'fixtures') for app in get_apps()] 
     1351    for fixture_label in fixture_labels: 
     1352        if verbosity > 0: 
     1353            print "Loading '%s' fixtures..." % fixture_label 
     1354        for fixture_dir in app_fixtures + list(settings.FIXTURE_DIRS) + ['']: 
     1355            if verbosity > 1: 
     1356                print "Checking %s for fixtures..." % humanize(fixture_dir) 
     1357            try: 
     1358                fixture_name, format = fixture_label.rsplit('.', 1) 
     1359                formats = [format] 
     1360            except ValueError: 
     1361                fixture_name = fixture_label 
     1362                formats = serializers.get_serializer_formats() 
     1363             
     1364            label_found = False 
     1365            for format in formats: 
     1366                serializer = serializers.get_serializer(format) 
     1367                if verbosity > 1: 
     1368                    print "Trying %s for %s fixture '%s'..." % \ 
     1369                        (humanize(fixture_dir), format, fixture_name) 
     1370                try: 
     1371                    full_path = os.path.join(fixture_dir, '.'.join([fixture_name, format])) 
     1372                    fixture = open(full_path, 'r') 
     1373                    if label_found: 
     1374                        fixture.close() 
     1375                        print style.ERROR("Multiple fixtures named '%s' in %s. Aborting." %  
     1376                            (fixture_name, humanize(fixture_dir))) 
     1377                        transaction.rollback() 
     1378                        transaction.leave_transaction_management() 
     1379                        return 
     1380                    else: 
     1381                        count[1] += 1 
     1382                        if verbosity > 0: 
     1383                            print "Installing %s fixture '%s' from %s." % \ 
     1384                                (format, fixture_name, humanize(fixture_dir)) 
     1385                        try: 
     1386                            objects =  serializers.deserialize(format, fixture) 
     1387                            for obj in objects: 
     1388                                count[0] += 1 
     1389                                obj.save() 
     1390                            label_found = True 
     1391                        except Exception, e: 
     1392                            fixture.close() 
     1393                            sys.stderr.write( 
     1394                                style.ERROR("Problem installing fixture '%s': %s\n" %  
     1395                                     (full_path, str(e)))) 
     1396                            transaction.rollback() 
     1397                            transaction.leave_transaction_management() 
     1398                            return 
     1399                        fixture.close() 
     1400                except: 
     1401                    if verbosity > 1: 
     1402                        print "No %s fixture '%s' in %s." % \ 
     1403                            (format, fixture_name, humanize(fixture_dir)) 
     1404    if count[0] == 0: 
     1405        if verbosity > 0: 
     1406            print "No fixtures found." 
     1407    else: 
     1408        if verbosity > 0: 
     1409            print "Installed %d object(s) from %d fixture(s)" % tuple(count) 
     1410    transaction.commit() 
     1411    transaction.leave_transaction_management() 
     1412         
     1413load_data.help_doc = 'Installs the named fixture(s) in the database' 
     1414load_data.args = "[--verbosity] fixture, fixture, ..." 
     1415  
     1416def dump_data(app_labels, format='json'): 
     1417    "Output the current contents of the database as a fixture of the given format" 
     1418    from django.db.models import get_app, get_apps, get_models 
     1419    from django.core import serializers 
     1420  
     1421    if len(app_labels) == 0: 
     1422        app_list = get_apps() 
     1423    else: 
     1424        app_list = [get_app(app_label) for app_label in app_labels] 
     1425  
     1426    # Check that the serialization format exists; this is a shortcut to 
     1427    # avoid collating all the objects and _then_ failing. 
     1428    try: 
     1429        serializers.get_serializer(format) 
     1430    except KeyError: 
     1431        sys.stderr.write(style.ERROR("Unknown serialization format: %s\n" % format))         
     1432     
     1433    objects = [] 
     1434    for app in app_list: 
     1435        for model in get_models(app): 
     1436            objects.extend(model.objects.all()) 
     1437    try: 
     1438        print serializers.serialize(format, objects) 
     1439    except Exception, e: 
     1440        sys.stderr.write(style.ERROR("Unable to serialize database: %s\n" % e)) 
     1441dump_data.help_doc = 'Output the contents of the database as a fixture of the given format' 
     1442dump_data.args = '[--format]' + APP_ARGS 
     1443 
    12541444# Utilities for command-line script 
    12551445 
     
    12591449    'dbshell': dbshell, 
    12601450    'diffsettings': diffsettings, 
     1451    'dumpdata': dump_data, 
     1452    'flush': flush, 
    12611453    'inspectdb': inspectdb, 
    1262     'install': install
     1454    'loaddata': load_data
    12631455    'reset': reset, 
    12641456    'runfcgi': runfcgi, 
     
    12681460    'sqlall': get_sql_all, 
    12691461    'sqlclear': get_sql_delete, 
     1462    'sqlcustom': get_custom_sql, 
     1463    'sqlflush': get_sql_flush, 
    12701464    'sqlindexes': get_sql_indexes, 
    12711465    'sqlinitialdata': get_sql_initial_data, 
     
    12841478    'dbshell', 
    12851479    'diffsettings', 
    1286     'install', 
    12871480    'reset', 
    12881481    'sqlindexes', 
     
    13311524    parser.add_option('--noreload', action='store_false', dest='use_reloader', default=True, 
    13321525        help='Tells Django to NOT use the auto-reloader when running the development server.') 
     1526    parser.add_option('--format', default='json', dest='format', 
     1527        help='Specifies the output serialization format for fixtures')     
    13331528    parser.add_option('--verbosity', action='store', dest='verbosity', default='1', 
    13341529        type='choice', choices=['0', '1', '2'], 
     
    13641559    elif action in ('validate', 'diffsettings', 'dbshell'): 
    13651560        action_mapping[action]() 
    1366     elif action == 'syncdb'
     1561    elif action in ('flush', 'syncdb')
    13671562        action_mapping[action](int(options.verbosity), options.interactive) 
    13681563    elif action == 'inspectdb': 
     
    13781573        except IndexError: 
    13791574            parser.print_usage_and_exit() 
    1380     elif action == 'test'
     1575    elif action in ('test', 'loaddata')
    13811576        try: 
    13821577            action_mapping[action](args[1:], int(options.verbosity)) 
     1578        except IndexError: 
     1579            parser.print_usage_and_exit() 
     1580    elif action == 'dumpdata': 
     1581        try: 
     1582            action_mapping[action](args[1:], options.format) 
    13831583        except IndexError: 
    13841584            parser.print_usage_and_exit() 
     
    14011601    elif action == 'runfcgi': 
    14021602        action_mapping[action](args[1:]) 
     1603    elif action == 'sqlinitialdata': 
     1604        print action_mapping[action](args[1:]) 
     1605    elif action == 'sqlflush': 
     1606        print '\n'.join(action_mapping[action]()) 
    14031607    else: 
    14041608        from django.db import models 
  • django/trunk/django/core/serializers/base.py

    r4265 r4659  
    142142class DeserializedObject(object): 
    143143    """ 
    144     A deserialzed model. 
     144    A deserialized model. 
    145145 
    146146    Basically a container for holding the pre-saved deserialized data along 
  • django/trunk/django/core/serializers/__init__.py

    r4265 r4659  
    4141        _load_serializers() 
    4242    return _serializers[format].Serializer 
     43 
     44def get_serializer_formats(): 
     45    if not _serializers: 
     46        _load_serializers() 
     47    return _serializers.keys() 
    4348     
    4449def get_deserializer(format): 
  • django/trunk/django/db/backends/ado_mssql/base.py

    r4610 r4659  
    138138    return "DEFAULT" 
    139139 
     140def get_sql_flush(sql_styler, full_table_list): 
     141    """Return a list of SQL statements required to remove all data from 
     142    all tables in the database (without actually removing the tables 
     143    themselves) and put the database in an empty 'initial' state 
     144    """ 
     145    # Return a list of 'TRUNCATE x;', 'TRUNCATE y;', 'TRUNCATE z;'... style SQL statements 
     146    # TODO - SQL not actually tested against ADO MSSQL yet! 
     147    # TODO - autoincrement indices reset required? See other get_sql_flush() implementations 
     148    sql_list = ['%s %s;' % \ 
     149                (sql_styler.SQL_KEYWORD('TRUNCATE'), 
     150                 sql_styler.SQL_FIELD(quote_name(table)) 
     151                 )  for table in full_table_list] 
     152 
    140153OPERATOR_MAPPING = { 
    141154    'exact': '= %s', 
  • django/trunk/django/db/backends/dummy/base.py

    r4610 r4659  
    4040get_fulltext_search_sql = complain 
    4141get_drop_foreignkey_sql = complain 
     42get_sql_flush = complain 
     43 
    4244OPERATOR_MAPPING = {} 
  • django/trunk/django/db/backends/mysql/base.py

    r4610 r4659  
    187187    return "DEFAULT" 
    188188 
     189def get_sql_flush(style, tables, sequences): 
     190    """Return a list of SQL statements required to remove all data from 
     191    all tables in the database (without actually removing the tables 
     192    themselves) and put the database in an empty 'initial' state 
     193     
     194    """ 
     195    # NB: The generated SQL below is specific to MySQL 
     196    # 'TRUNCATE x;', 'TRUNCATE y;', 'TRUNCATE z;'... style SQL statements 
     197    # to clear all tables of all data 
     198    if tables: 
     199        sql = ['SET FOREIGN_KEY_CHECKS = 0;'] + \ 
     200              ['%s %s;' % \ 
     201                (style.SQL_KEYWORD('TRUNCATE'), 
     202                 style.SQL_FIELD(quote_name(table)) 
     203                )  for table in tables] + \ 
     204              ['SET FOREIGN_KEY_CHECKS = 1;'] 
     205               
     206        # 'ALTER TABLE table AUTO_INCREMENT = 1;'... style SQL statements 
     207        # to reset sequence indices 
     208        sql.extend(["%s %s %s %s %s;" % \ 
     209            (style.SQL_KEYWORD('ALTER'), 
     210             style.SQL_KEYWORD('TABLE'), 
     211             style.SQL_TABLE(quote_name(sequence['table'])), 
     212             style.SQL_KEYWORD('AUTO_INCREMENT'), 
     213             style.SQL_FIELD('= 1'), 
     214            ) for sequence in sequences]) 
     215        return sql 
     216    else: 
     217        return [] 
     218 
    189219OPERATOR_MAPPING = { 
    190220    'exact': '= %s', 
  • django/trunk/django/db/backends/oracle/base.py

    r4610 r4659  
    121121    return "DEFAULT" 
    122122 
     123def get_sql_flush(style, tables, sequences): 
     124    """Return a list of SQL statements required to remove all data from 
     125    all tables in the database (without actually removing the tables 
     126    themselves) and put the database in an empty 'initial' state 
     127    """ 
     128    # Return a list of 'TRUNCATE x;', 'TRUNCATE y;', 'TRUNCATE z;'... style SQL statements 
     129    # TODO - SQL not actually tested against Oracle yet! 
     130    # TODO - autoincrement indices reset required? See other get_sql_flush() implementations 
     131    sql = ['%s %s;' % \ 
     132            (style.SQL_KEYWORD('TRUNCATE'), 
     133             style.SQL_FIELD(quote_name(table)) 
     134             )  for table in tables] 
     135 
     136 
    123137OPERATOR_MAPPING = { 
    124138    'exact': '= %s', 
  • django/trunk/django/db/backends/postgresql/base.py

    r4610 r4659  
    5252        else: 
    5353            return getattr(self.cursor, attr) 
     54 
     55postgres_version = None 
    5456 
    5557class DatabaseWrapper(local): 
     
    8284            cursor.execute("SET TIME ZONE %s", [settings.TIME_ZONE]) 
    8385        cursor = UnicodeCursorWrapper(cursor, settings.DEFAULT_CHARSET) 
     86        global postgres_version 
     87        if not postgres_version: 
     88            cursor.execute("SELECT version()") 
     89            postgres_version = [int(val) for val in cursor.dictfetchone()['version'].split()[1].split('.')]         
    8490        if settings.DEBUG: 
    8591            return util.CursorDebugWrapper(cursor, self) 
     
    152158    return "DEFAULT" 
    153159 
     160def get_sql_flush(style, tables, sequences): 
     161    """Return a list of SQL statements required to remove all data from 
     162    all tables in the database (without actually removing the tables 
     163    themselves) and put the database in an empty 'initial' state 
     164     
     165    """     
     166    if tables: 
     167        if postgres_version[0] >= 8 and postgres_version[1] >= 1: 
     168            # Postgres 8.1+ can do 'TRUNCATE x, y, z...;'. In fact, it *has to* in order to be able to 
     169            # truncate tables referenced by a foreign key in any other table. The result is a 
     170            # single SQL TRUNCATE statement. 
     171            sql = ['%s %s;' % \ 
     172                (style.SQL_KEYWORD('TRUNCATE'), 
     173                 style.SQL_FIELD(', '.join(quote_name(table) for table in tables)) 
     174            )] 
     175        else: 
     176            # Older versions of Postgres can't do TRUNCATE in a single call, so they must use  
     177            # a simple delete. 
     178            sql = ['%s %s %s;' % \ 
     179                    (style.SQL_KEYWORD('DELETE'), 
     180                     style.SQL_KEYWORD('FROM'), 
     181                     style.SQL_FIELD(quote_name(table)) 
     182                     ) for table in tables] 
     183 
     184        # 'ALTER SEQUENCE sequence_name RESTART WITH 1;'... style SQL statements 
     185        # to reset sequence indices 
     186        for sequence_info in sequences: 
     187            table_name = sequence_info['table'] 
     188            column_name = sequence_info['column'] 
     189            if column_name and len(column_name)>0: 
     190                # sequence name in this case will be <table>_<column>_seq 
     191                sql.append("%s %s %s %s %s %s;" % \ 
     192                    (style.SQL_KEYWORD('ALTER'), 
     193                    style.SQL_KEYWORD('SEQUENCE'), 
     194                    style.SQL_FIELD('%s_%s_seq' % (table_name, column_name)), 
     195                    style.SQL_KEYWORD('RESTART'), 
     196                    style.SQL_KEYWORD('WITH'), 
     197                    style.SQL_FIELD('1') 
     198                    ) 
     199                ) 
     200            else: 
     201                # sequence name in this case will be <table>_id_seq 
     202                sql.append("%s %s %s %s %s %s;" % \ 
     203                    (style.SQL_KEYWORD('ALTER'), 
     204                     style.SQL_KEYWORD('SEQUENCE'), 
     205                     style.SQL_FIELD('%s_id_seq' % table_name), 
     206                     style.SQL_KEYWORD('RESTART'), 
     207                     style.SQL_KEYWORD('WITH'), 
     208                     style.SQL_FIELD('1') 
     209                     ) 
     210                ) 
     211        return sql 
     212    else: 
     213        return [] 
     214 
     215         
    154216# Register these custom typecasts, because Django expects dates/times to be 
    155217# in Python's native (standard-library) datetime/time format, whereas psycopg 
  • django/trunk/django/db/backends/postgresql_psycopg2/base.py

    r4610 r4659  
    2020    # Import copy of _thread_local.py from Python 2.4 
    2121    from django.utils._threading_local import local 
     22 
     23postgres_version = None 
    2224 
    2325class DatabaseWrapper(local): 
     
    5052        if set_tz: 
    5153            cursor.execute("SET TIME ZONE %s", [settings.TIME_ZONE]) 
     54        global postgres_version 
     55        if not postgres_version: 
     56            cursor.execute("SELECT version()") 
     57            postgres_version = [int(val) for val in cursor.dictfetchone()['version'].split()[1].split('.')]         
    5258        if settings.DEBUG: 
    5359            return util.CursorDebugWrapper(cursor, self) 
     
    112118    return "DEFAULT" 
    113119 
     120def get_sql_flush(style, tables, sequences): 
     121    """Return a list of SQL statements required to remove all data from 
     122    all tables in the database (without actually removing the tables 
     123    themselves) and put the database in an empty 'initial' state 
     124    """ 
     125    if tables: 
     126        if postgres_version[0] >= 8 and postgres_version[1] >= 1: 
     127            # Postgres 8.1+ can do 'TRUNCATE x, y, z...;'. In fact, it *has to* in order to be able to 
     128            # truncate tables referenced by a foreign key in any other table. The result is a 
     129            # single SQL TRUNCATE statement 
     130            sql = ['%s %s;' % \ 
     131                    (style.SQL_KEYWORD('TRUNCATE'), 
     132                     style.SQL_FIELD(', '.join(quote_name(table) for table in tables)) 
     133                    )] 
     134        else: 
     135            sql = ['%s %s %s;' % \ 
     136                    (style.SQL_KEYWORD('DELETE'), 
     137                     style.SQL_KEYWORD('FROM'), 
     138                     style.SQL_FIELD(quote_name(table)) 
     139                     ) for table in tables] 
     140                      
     141        # 'ALTER SEQUENCE sequence_name RESTART WITH 1;'... style SQL statements 
     142        # to reset sequence indices 
     143        for sequence in sequences: 
     144            table_name = sequence['table'] 
     145            column_name = sequence['column'] 
     146            if column_name and len(column_name) > 0: 
     147                # sequence name in this case will be <table>_<column>_seq 
     148                sql.append("%s %s %s %s %s %s;" % \ 
     149                    (style.SQL_KEYWORD('ALTER'), 
     150                     style.SQL_KEYWORD('SEQUENCE'), 
     151                     style.SQL_FIELD('%s_%s_seq' % (table_name, column_name)), 
     152                     style.SQL_KEYWORD('RESTART'), 
     153                     style.SQL_KEYWORD('WITH'), 
     154                     style.SQL_FIELD('1') 
     155                     ) 
     156                ) 
     157            else: 
     158                # sequence name in this case will be <table>_id_seq 
     159                sql.append("%s %s %s %s %s %s;" % \ 
     160                    (style.SQL_KEYWORD('ALTER'), 
     161                     style.SQL_KEYWORD('SEQUENCE'), 
     162                     style.SQL_FIELD('%s_id_seq' % table_name), 
     163                     style.SQL_KEYWORD('RESTART'), 
     164                     style.SQL_KEYWORD('WITH'), 
     165                     style.SQL_FIELD('1') 
     166                     ) 
     167                ) 
     168        return sql 
     169    else: 
     170        return [] 
     171         
    114172OPERATOR_MAPPING = { 
    115173    'exact': '= %s', 
  • django/trunk/django/db/backends/sqlite3/base.py

    r4610 r4659  
    152152    return "NULL" 
    153153 
     154def get_sql_flush(style, tables, sequences): 
     155    """Return a list of SQL statements required to remove all data from 
     156    all tables in the database (without actually removing the tables 
     157    themselves) and put the database in an empty 'initial' state 
     158     
     159    """ 
     160    # NB: The generated SQL below is specific to SQLite 
     161    # Note: The DELETE FROM... SQL generated below works for SQLite databases 
     162    # because constraints don't exist 
     163    sql = ['%s %s %s;' % \ 
     164            (style.SQL_KEYWORD('DELETE'), 
     165             style.SQL_KEYWORD('FROM'), 
     166             style.SQL_FIELD(quote_name(table)) 
     167             ) for table in tables] 
     168    # Note: No requirement for reset of auto-incremented indices (cf. other 
     169    # get_sql_flush() implementations). Just return SQL at this point 
     170    return sql 
     171 
    154172def _sqlite_date_trunc(lookup_type, dt): 
    155173    try: 
  • django/trunk/django/test/__init__.py

    r4265 r4659  
     1""" 
     2Django Unit Test and Doctest framework. 
     3""" 
     4 
     5from django.test.client import Client 
     6from django.test.testcases import TestCase 
  • django/trunk/django/test/testcases.py

    r4265 r4659  
    11import re, doctest, unittest 
    22from django.db import transaction 
     3from django.core import management 
     4from django.db.models import get_apps 
    35     
    46normalize_long_ints = lambda s: re.sub(r'(?<![\w])(\d+)L(?![\w])', '\\1', s) 
     
    2931        transaction.rollback_unless_managed() 
    3032 
     33class TestCase(unittest.TestCase):     
     34    def install_fixtures(self): 
     35        """If the Test Case class has a 'fixtures' member, clear the database and 
     36        install the named fixtures at the start of each test. 
     37         
     38        """ 
     39        management.flush(verbosity=0, interactive=False) 
     40        if hasattr(self, 'fixtures'): 
     41            management.load_data(self.fixtures, verbosity=0) 
     42 
     43    def run(self, result=None): 
     44        """Wrapper around default run method so that user-defined Test Cases  
     45        automatically call install_fixtures without having to include a call to  
     46        super(). 
     47         
     48        """ 
     49        self.install_fixtures() 
     50        super(TestCase, self).run(result) 
  • django/trunk/docs/django-admin.txt

    r4651 r4659  
    9797Note that Django's default settings live in ``django/conf/global_settings.py``, 
    9898if you're ever curious to see the full list of defaults. 
     99 
     100dumpdata [appname appname ...] 
     101------------------------------ 
     102 
     103**New in Django development version** 
     104 
     105Output to standard output all data in the database associated with the named  
     106application(s). 
     107 
     108By default, the database will be dumped in JSON format. If you want the output 
     109to be in another format, use the ``--format`` option (e.g., ``format=xml``).  
     110You may specify any Django serialization backend (inclu