Index: django/test/testcases.py
===================================================================
--- django/test/testcases.py	(revision 4223)
+++ django/test/testcases.py	(working copy)
@@ -1,5 +1,7 @@
 import re, doctest, unittest
 from django.db import transaction
+from django.core import management
+from django.db.models import get_apps
     
 normalize_long_ints = lambda s: re.sub(r'(?<![\w])(\d+)L(?![\w])', '\\1', s)
 
@@ -19,6 +21,7 @@
     def __init__(self, *args, **kwargs):
         doctest.DocTestRunner.__init__(self, *args, **kwargs)
         self.optionflags = doctest.ELLIPSIS
+        management.flush(verbosity=0, interactive=False)
         
     def report_unexpected_exception(self, out, test, example, exc_info):
         doctest.DocTestRunner.report_unexpected_exception(self,out,test,example,exc_info)
@@ -28,3 +31,22 @@
         from django.db import transaction
         transaction.rollback_unless_managed()
 
+class TestCase(unittest.TestCase):    
+    def install_fixtures(self):
+        """If the Test Case class has a 'fixtures' member, clear the database and
+        install the named fixtures at the start of each test.
+        
+        """
+        management.flush(verbosity=0, interactive=False)
+        if hasattr(self, 'fixtures'):
+            for format,fixtures in self.fixtures.items():
+                management.install_fixtures(fixtures, format=format, verbosity=0)
+
+    def run(self, result=None):
+        """Wrapper around default run method so that user-defined Test Cases 
+        automatically call install_fixtures without having to include a call to 
+        super().
+        
+        """
+        self.install_fixtures()
+        super(TestCase, self).run(result)
Index: django/test/__init__.py
===================================================================
--- django/test/__init__.py	(revision 4223)
+++ django/test/__init__.py	(working copy)
@@ -0,0 +1,6 @@
+"""
+Django Unit Test and Doctest framework.
+"""
+
+from django.test.client import Client
+from django.test.testcases import TestCase
Index: django/conf/global_settings.py
===================================================================
--- django/conf/global_settings.py	(revision 4223)
+++ django/conf/global_settings.py	(working copy)
@@ -315,3 +315,10 @@
 # The name of the database to use for testing purposes.
 # If None, a name of 'test_' + DATABASE_NAME will be assumed
 TEST_DATABASE_NAME = None
+
+############
+# FIXTURES #
+############
+
+# The list of directories to search for fixtures
+FIXTURE_DIRS = ()
Index: django/db/backends/ado_mssql/base.py
===================================================================
--- django/db/backends/ado_mssql/base.py	(revision 4223)
+++ django/db/backends/ado_mssql/base.py	(working copy)
@@ -134,6 +134,19 @@
 def get_pk_default_value():
     return "DEFAULT"
 
+def get_sql_flush(sql_styler, full_table_list):
+    """Return a list of SQL statements required to remove all data from
+    all tables in the database (without actually removing the tables
+    themselves) and put the database in an empty 'initial' state
+    """
+    # Return a list of 'TRUNCATE x;', 'TRUNCATE y;', 'TRUNCATE z;'... style SQL statements
+    # TODO - SQL not actually tested against ADO MSSQL yet!
+    # TODO - autoincrement indices reset required? See other get_sql_flush() implementations
+    sql_list = ['%s %s;' % \
+                (sql_styler.SQL_KEYWORD('TRUNCATE'),
+                 sql_styler.SQL_FIELD(quote_name(table))
+                 )  for table in full_table_list]
+
 OPERATOR_MAPPING = {
     'exact': '= %s',
     'iexact': 'LIKE %s',
Index: django/db/backends/postgresql/base.py
===================================================================
--- django/db/backends/postgresql/base.py	(revision 4223)
+++ django/db/backends/postgresql/base.py	(working copy)
@@ -112,6 +112,52 @@
 def get_pk_default_value():
     return "DEFAULT"
 
+def get_sql_flush(style, tables, sequences):
+    """Return a list of SQL statements required to remove all data from
+    all tables in the database (without actually removing the tables
+    themselves) and put the database in an empty 'initial' state
+    
+    """
+    # Postgres can do 'TRUNCATE x, y, z...;'. In fact, it *has to* in order to be able to
+    # truncate tables referenced by a foreign key in any other table. The result is a
+    # single SQL TRUNCATE statement.
+    if tables:
+        sql = ['%s %s;' % \
+            (style.SQL_KEYWORD('TRUNCATE'),
+             style.SQL_FIELD(', '.join(quote_name(table) for table in tables))
+        )]
+    
+        # 'ALTER SEQUENCE sequence_name RESTART WITH 1;'... style SQL statements
+        # to reset sequence indices
+        for sequence_info in sequences:
+            table_name = sequence_info['table']
+            column_name = sequence_info['column']
+            if column_name and len(column_name)>0:
+                # sequence name in this case will be <table>_<column>_seq
+                sql.append("%s %s %s %s %s %s;" % \
+                    (style.SQL_KEYWORD('ALTER'),
+                    style.SQL_KEYWORD('SEQUENCE'),
+                    style.SQL_FIELD('%s_%s_seq' % (table_name, column_name)),
+                    style.SQL_KEYWORD('RESTART'),
+                    style.SQL_KEYWORD('WITH'),
+                    style.SQL_FIELD('1')
+                    )
+                )
+            else:
+                # sequence name in this case will be <table>_id_seq
+                sql.append("%s %s %s %s %s %s;" % \
+                    (style.SQL_KEYWORD('ALTER'),
+                     style.SQL_KEYWORD('SEQUENCE'),
+                     style.SQL_FIELD('%s_id_seq' % table_name),
+                     style.SQL_KEYWORD('RESTART'),
+                     style.SQL_KEYWORD('WITH'),
+                     style.SQL_FIELD('1')
+                     )
+                )
+        return sql
+    else:
+        return []
+        
 # Register these custom typecasts, because Django expects dates/times to be
 # in Python's native (standard-library) datetime/time format, whereas psycopg
 # use mx.DateTime by default.
Index: django/db/backends/sqlite3/base.py
===================================================================
--- django/db/backends/sqlite3/base.py	(revision 4223)
+++ django/db/backends/sqlite3/base.py	(working copy)
@@ -148,6 +148,24 @@
 def get_pk_default_value():
     return "NULL"
 
+def get_sql_flush(style, tables, sequences):
+    """Return a list of SQL statements required to remove all data from
+    all tables in the database (without actually removing the tables
+    themselves) and put the database in an empty 'initial' state
+    
+    """
+    # NB: The generated SQL below is specific to SQLite
+    # Note: The DELETE FROM... SQL generated below works for SQLite databases
+    # because constraints don't exist
+    sql = ['%s %s %s;' % \
+            (style.SQL_KEYWORD('DELETE'),
+             style.SQL_KEYWORD('FROM'),
+             style.SQL_FIELD(quote_name(table))
+             ) for table in tables]
+    # Note: No requirement for reset of auto-incremented indices (cf. other
+    # get_sql_flush() implementations). Just return SQL at this point
+    return sql
+
 def _sqlite_date_trunc(lookup_type, dt):
     try:
         dt = util.typecast_timestamp(dt)
Index: django/db/backends/mysql/base.py
===================================================================
--- django/db/backends/mysql/base.py	(revision 4223)
+++ django/db/backends/mysql/base.py	(working copy)
@@ -181,6 +181,33 @@
 def get_pk_default_value():
     return "DEFAULT"
 
+def get_sql_flush(style, tables, sequences):
+    """Return a list of SQL statements required to remove all data from
+    all tables in the database (without actually removing the tables
+    themselves) and put the database in an empty 'initial' state
+    
+    """
+    # NB: The generated SQL below is specific to MySQL
+    # 'TRUNCATE x;', 'TRUNCATE y;', 'TRUNCATE z;'... style SQL statements
+    # to clear all tables of all data
+    if tables:
+        sql = ['%s %s;' % \
+                (style.SQL_KEYWORD('TRUNCATE'),
+                 style.SQL_FIELD(quote_name(table))
+                )  for table in tables]
+        # 'ALTER TABLE table AUTO_INCREMENT = 1;'... style SQL statements
+        # to reset sequence indices
+        sql.extend(["%s %s %s %s %s;" % \
+            (style.SQL_KEYWORD('ALTER'),
+             style.SQL_KEYWORD('TABLE'),
+             style.SQL_TABLE(quote_name(sequence['table'])),
+             style.SQL_KEYWORD('AUTO_INCREMENT'),
+             style.SQL_FIELD('= 1'),
+            ) for sequence in sequences])
+        return sql
+    else:
+        return []
+
 OPERATOR_MAPPING = {
     'exact': '= %s',
     'iexact': 'LIKE %s',
Index: django/db/backends/oracle/base.py
===================================================================
--- django/db/backends/oracle/base.py	(revision 4223)
+++ django/db/backends/oracle/base.py	(working copy)
@@ -117,6 +117,20 @@
 def get_pk_default_value():
     return "DEFAULT"
 
+def get_sql_flush(style, tables, sequences):
+    """Return a list of SQL statements required to remove all data from
+    all tables in the database (without actually removing the tables
+    themselves) and put the database in an empty 'initial' state
+    """
+    # Return a list of 'TRUNCATE x;', 'TRUNCATE y;', 'TRUNCATE z;'... style SQL statements
+    # TODO - SQL not actually tested against Oracle yet!
+    # TODO - autoincrement indices reset required? See other get_sql_flush() implementations
+    sql = ['%s %s;' % \
+            (style.SQL_KEYWORD('TRUNCATE'),
+             style.SQL_FIELD(quote_name(table))
+             )  for table in tables]
+
+
 OPERATOR_MAPPING = {
     'exact': '= %s',
     'iexact': 'LIKE %s',
Index: django/db/backends/postgresql_psycopg2/base.py
===================================================================
--- django/db/backends/postgresql_psycopg2/base.py	(revision 4223)
+++ django/db/backends/postgresql_psycopg2/base.py	(working copy)
@@ -105,6 +105,50 @@
 def get_pk_default_value():
     return "DEFAULT"
 
+def get_sql_flush(style, tables, sequences):
+    """Return a list of SQL statements required to remove all data from
+    all tables in the database (without actually removing the tables
+    themselves) and put the database in an empty 'initial' state
+    """
+    # Postgres can do 'TRUNCATE x, y, z...;'. In fact, it *has to* in order to be able to
+    # truncate tables referenced by a foreign key in any other table. The result is a
+    # single SQL TRUNCATE statement
+    if tables:
+        sql = ['%s %s;' % \
+                (style.SQL_KEYWORD('TRUNCATE'),
+                 style.SQL_FIELD(', '.join(quote_name(table) for table in tables))
+                )]
+        # 'ALTER SEQUENCE sequence_name RESTART WITH 1;'... style SQL statements
+        # to reset sequence indices
+        for sequence in sequences:
+            table_name = sequence['table']
+            column_name = sequence['column']
+            if column_name and len(column_name) > 0:
+                # sequence name in this case will be <table>_<column>_seq
+                sql.append("%s %s %s %s %s %s;" % \
+                    (style.SQL_KEYWORD('ALTER'),
+                     style.SQL_KEYWORD('SEQUENCE'),
+                     style.SQL_FIELD('%s_%s_seq' % (table_name, column_name)),
+                     style.SQL_KEYWORD('RESTART'),
+                     style.SQL_KEYWORD('WITH'),
+                     style.SQL_FIELD('1')
+                     )
+                )
+            else:
+                # sequence name in this case will be <table>_id_seq
+                sql.append("%s %s %s %s %s %s;" % \
+                    (style.SQL_KEYWORD('ALTER'),
+                     style.SQL_KEYWORD('SEQUENCE'),
+                     style.SQL_FIELD('%s_id_seq' % table_name),
+                     style.SQL_KEYWORD('RESTART'),
+                     style.SQL_KEYWORD('WITH'),
+                     style.SQL_FIELD('1')
+                     )
+                )
+        return sql
+    else:
+        return []
+        
 OPERATOR_MAPPING = {
     'exact': '= %s',
     'iexact': 'ILIKE %s',
Index: django/db/backends/dummy/base.py
===================================================================
--- django/db/backends/dummy/base.py	(revision 4223)
+++ django/db/backends/dummy/base.py	(working copy)
@@ -38,4 +38,6 @@
 get_random_function_sql = complain
 get_fulltext_search_sql = complain
 get_drop_foreignkey_sql = complain
+get_sql_flush = complain
+
 OPERATOR_MAPPING = {}
Index: django/core/serializers/base.py
===================================================================
--- django/core/serializers/base.py	(revision 4223)
+++ django/core/serializers/base.py	(working copy)
@@ -141,7 +141,7 @@
 
 class DeserializedObject(object):
     """
-    A deserialzed model.
+    A deserialized model.
 
     Basically a container for holding the pre-saved deserialized data along
     with the many-to-many data saved with the object.
Index: django/core/management.py
===================================================================
--- django/core/management.py	(revision 4223)
+++ django/core/management.py	(working copy)
@@ -68,6 +68,25 @@
     cursor = connection.cursor()
     return get_introspection_module().get_table_list(cursor)
 
+def _get_sequence_list():
+    "Returns a list of information about all DB sequences for all models in all apps"
+    from django.db import models
+
+    apps = models.get_apps()
+    sequence_list = []
+
+    for app in apps:
+        for model in models.get_models(app):
+            for f in model._meta.fields:
+                if isinstance(f, models.AutoField):
+                    sequence_list.append({'table':model._meta.db_table,'column':f.column,})
+                    break # Only one AutoField is allowed per model, so don't bother continuing.
+
+            for f in model._meta.many_to_many:
+                sequence_list.append({'table':f.m2m_db_table(),'column':None,})
+
+    return sequence_list
+
 # If the foreign key points to an AutoField, a PositiveIntegerField or a
 # PositiveSmallIntegerField, the foreign key should be an IntegerField, not the
 # referred field type. Otherwise, the foreign key should be the same type of
@@ -330,7 +349,15 @@
 get_sql_reset.help_doc = "Prints the DROP TABLE SQL, then the CREATE TABLE SQL, for the given app name(s)."
 get_sql_reset.args = APP_ARGS
 
-def get_sql_initial_data_for_model(model):
+def get_sql_flush():
+    "Returns a list of the SQL statements used to flush the database"
+    from django.db import backend
+    statements = backend.get_sql_flush(style, _get_table_list(), _get_sequence_list())
+    return statements
+get_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."
+get_sql_flush.args = ''
+
+def get_custom_sql_for_model(model):
     from django.db import models
     from django.conf import settings
 
@@ -357,8 +384,8 @@
 
     return output
 
-def get_sql_initial_data(app):
-    "Returns a list of the initial INSERT SQL statements for the given app."
+def get_custom_sql(app):
+    "Returns a list of the custom table modifying SQL statements for the given app."
     from django.db.models import get_models
     output = []
 
@@ -366,11 +393,11 @@
     app_dir = os.path.normpath(os.path.join(os.path.dirname(app.__file__), 'sql'))
 
     for model in app_models:
-        output.extend(get_sql_initial_data_for_model(model))
+        output.extend(get_custom_sql_for_model(model))
 
     return output
-get_sql_initial_data.help_doc = "Prints the initial INSERT SQL statements for the given app name(s)."
-get_sql_initial_data.args = APP_ARGS
+get_custom_sql.help_doc = "Prints the custom table modifying SQL statements for the given app name(s)."
+get_custom_sql.args = APP_ARGS
 
 def get_sql_sequence_reset(app):
     "Returns a list of the SQL statements to reset PostgreSQL sequences for the given app."
@@ -428,7 +455,7 @@
 
 def get_sql_all(app):
     "Returns a list of CREATE TABLE SQL, initial-data inserts, and CREATE INDEX SQL for the given module."
-    return get_sql_create(app) + get_sql_initial_data(app) + get_sql_indexes(app)
+    return get_sql_create(app) + get_custom_sql(app) + get_sql_indexes(app)
 get_sql_all.help_doc = "Prints the CREATE TABLE, initial-data and CREATE INDEX SQL statements for the given model module name(s)."
 get_sql_all.args = APP_ARGS
 
@@ -511,10 +538,10 @@
         # just created)
         for model in models.get_models(app):
             if model in created_models:
-                initial_sql = get_sql_initial_data_for_model(model)
+                initial_sql = get_custom_sql_for_model(model)
                 if initial_sql:
                     if verbosity >= 1:
-                        print "Installing initial data for %s.%s model" % (app_name, model._meta.object_name)
+                        print "Installing custom SQL for %s.%s model" % (app_name, model._meta.object_name)
                     try:
                         for sql in initial_sql:
                             cursor.execute(sql)
@@ -670,6 +697,70 @@
 reset.help_doc = "Executes ``sqlreset`` for the given app(s) in the current database."
 reset.args = APP_ARGS
 
+def flush(verbosity=1, interactive=True):
+    "Returns all tables in the database to the same state they were in immediately after syncdb."
+    from django.db import connection, transaction, models
+    from django.conf import settings
+    from django.dispatch import dispatcher
+    
+    disable_termcolors()
+
+    # First, try validating the models.
+    _check_for_validation_errors()
+
+    # Import the 'management' module within each installed app, to register
+    # dispatcher events.
+    for app_name in settings.INSTALLED_APPS:
+        try:
+            __import__(app_name + '.management', {}, {}, [''])
+        except ImportError:
+            pass
+    
+    sql_list = get_sql_flush()
+
+    if interactive:
+        confirm = raw_input("""
+You have requested a flush of the database.
+This will IRREVERSIBLY DESTROY all data currently in the database,
+and return each table to the state it was in after syncdb.
+Are you sure you want to do this?
+
+Type 'yes' to continue, or 'no' to cancel: """)
+    else:
+        confirm = 'yes'
+
+    if confirm == 'yes':
+        try:
+            cursor = connection.cursor()
+            for sql in sql_list:
+                cursor.execute(sql)
+        except Exception, e:
+            sys.stderr.write(style.ERROR("""Error: Database %s couldn't be flushed. Possible reasons:
+  * The database isn't running or isn't configured correctly.
+  * At least one of the expected database tables doesn't exist.
+  * The SQL was invalid.
+Hint: Look at the output of 'django-admin.py sqlflush'. That's the SQL this command wasn't able to run.
+The full error: """ % settings.DATABASE_NAME + style.ERROR_OUTPUT(str(e)) + '\n'))
+            transaction.rollback_unless_managed()
+            sys.exit(1)
+        transaction.commit_unless_managed()
+
+        # Emit the post-syncdb signal for every application.
+        # This simulates the effect of installing every application clean.
+        for app in models.get_apps():
+            app_name = app.__name__.split('.')[-2]
+            model_list = models.get_models(app)
+            if verbosity >= 2:
+                print "Running post-sync handlers for application", app_name
+            dispatcher.send(signal=models.signals.post_syncdb, sender=app,
+                app=app, created_models=model_list,
+                verbosity=verbosity, interactive=interactive)
+        
+    else:
+        print "Flush cancelled."
+flush.help_doc = "Executes ``sqlflush`` on the current database."
+flush.args = ''
+
 def _start_helper(app_or_project, name, directory, other_name=''):
     other = {'project': 'app', 'app': 'project'}[app_or_project]
     if not _is_valid_dir_name(name):
@@ -751,7 +842,7 @@
     yield "#     * Make sure each model has one field with primary_key=True"
     yield "# Feel free to rename the models, but don't rename db_table values or field names."
     yield "#"
-    yield "# Also note: You'll have to insert the output of 'django-admin.py sqlinitialdata [appname]'"
+    yield "# Also note: You'll have to insert the output of 'django-admin.py sqlcustom [appname]'"
     yield "# into your database."
     yield ''
     yield 'from django.db import models'
@@ -1239,6 +1330,86 @@
 test.help_doc = 'Runs the test suite for the specified applications, or the entire site if no apps are specified'
 test.args = '[--verbosity] ' + APP_ARGS
 
+def install_fixtures(fixture_labels, format='json', verbosity=1):
+    "Installs the fixture(s) with the provided name(s)"
+    from django.db.models import get_apps
+    from django.core import serializers
+    from django.db import transaction
+    from django.conf import settings
+    import sys
+     
+    # Keep a count of the installed objects and fixtures
+    count = [0,0]
+    
+    serializer = serializers.get_serializer(format)
+    
+    if verbosity > 1:
+        print "Looking for %s fixtures..." % format
+        
+    app_fixtures = [os.path.join(os.path.dirname(app.__file__),'fixtures') for app in get_apps()]
+    for fixture_dir in app_fixtures + list(settings.FIXTURE_DIRS):
+        if verbosity > 1:
+            print "Checking '%s' for %s fixtures..." % (fixture_dir, format)
+        for fixture_name in fixture_labels:
+            try:
+                if verbosity > 1:
+                    print "Trying %s fixture '%s' from '%s'." % (format, fixture_name, fixture_dir)
+                fixture = open(os.path.join(fixture_dir,
+                                            '.'.join([fixture_name, format])),
+                               'r')
+                count[1] += 1
+                if verbosity > 0:
+                    print "Installing %s fixture '%s' from '%s'." % (format, fixture_name, fixture_dir)
+                try:
+                    for obj in serializers.deserialize(format, fixture):
+                        count[0] += 1
+                        obj.save()
+                except Exception, e:
+                    sys.stderr.write(
+                        style.ERROR("Problem installing %s fixture '%s' from '%s': %s\n" % 
+                             (format, fixture_name, fixture_dir, str(e))))
+                fixture.close()
+            except:
+                if verbosity > 1:
+                    print "No %s fixture '%s' in '%s'" % (format, fixture_name, fixture_dir)
+    if count[0] == 0:
+        if verbosity > 0:
+            print "No fixtures found"
+    else:
+        if verbosity > 0:
+            print "Installed %d objects from %d fixtures" % tuple(count)
+        transaction.commit_unless_managed()
+install_fixtures.help_doc = 'Installs the named fixture(s) in the database'
+install_fixtures.args = "[--format] [--verbosity] fixture_name, fixture_name, ..."
+ 
+def dumpdb(app_labels, format='json'):
+    "Output the current contents of the database as a fixture of the given format"
+    from django.db.models import get_app, get_apps, get_models
+    from django.core import serializers
+ 
+    if len(app_labels) == 0:
+        app_list = get_apps()
+    else:
+        app_list = [get_app(app_label) for app_label in app_labels]
+ 
+    # Check that the serialization format exists; this is a shortcut to
+    # avoid collating all the objects and _then_ failing.
+    try:
+        serializers.get_serializer(format)
+    except KeyError:
+        sys.stderr.write(style.ERROR("Unknown serialization format: %s\n" % format))        
+    
+    objects = []
+    for app in app_list:
+        for model in get_models(app):
+            objects.extend(model.objects.all())
+    try:
+        print serializers.serialize(format, objects)
+    except Exception, e:
+     sys.stderr.write(style.ERROR("Unable to serialize database: %s\n" % e))
+dumpdb.help_doc = 'Output the contents of the database as a fixture of the given format'
+dumpdb.args = '[--format]' + APP_ARGS
+
 # Utilities for command-line script
 
 DEFAULT_ACTION_MAPPING = {
@@ -1246,8 +1417,11 @@
     'createcachetable' : createcachetable,
     'dbshell': dbshell,
     'diffsettings': diffsettings,
+    'dumpdb': dumpdb,
+    'flush': flush,
     'inspectdb': inspectdb,
     'install': install,
+    'installfixture': install_fixtures,
     'reset': reset,
     'runfcgi': runfcgi,
     'runserver': runserver,
@@ -1255,8 +1429,9 @@
     'sql': get_sql_create,
     'sqlall': get_sql_all,
     'sqlclear': get_sql_delete,
+    'sqlcustom': get_custom_sql,
+    'sqlflush': get_sql_flush,
     'sqlindexes': get_sql_indexes,
-    'sqlinitialdata': get_sql_initial_data,
     'sqlreset': get_sql_reset,
     'sqlsequencereset': get_sql_sequence_reset,
     'startapp': startapp,
@@ -1318,6 +1493,8 @@
         help='Tells Django to NOT prompt the user for input of any kind.')
     parser.add_option('--noreload', action='store_false', dest='use_reloader', default=True,
         help='Tells Django to NOT use the auto-reloader when running the development server.')
+    parser.add_option('--format', default='json', dest='format',
+        help='Specifies the serialization format for fixtures')
     parser.add_option('--verbosity', action='store', dest='verbosity', default='1',
         type='choice', choices=['0', '1', '2'],
         help='Verbosity level; 0=minimal output, 1=normal output, 2=all output'),
@@ -1351,7 +1528,7 @@
         action_mapping[action](options.plain is True)
     elif action in ('validate', 'diffsettings', 'dbshell'):
         action_mapping[action]()
-    elif action == 'syncdb':
+    elif action in ('flush', 'syncdb'):
         action_mapping[action](int(options.verbosity), options.interactive)
     elif action == 'inspectdb':
         try:
@@ -1370,6 +1547,16 @@
             action_mapping[action](args[1:], int(options.verbosity))
         except IndexError:
             parser.print_usage_and_exit()
+    elif action == 'installfixture':
+        try:
+            action_mapping[action](args[1:], options.format, int(options.verbosity))
+        except IndexError:
+            parser.print_usage_and_exit()
+    elif action == 'dumpdb':
+        try:
+            action_mapping[action](args[1:], options.format)
+        except IndexError:
+            parser.print_usage_and_exit()        
     elif action in ('startapp', 'startproject'):
         try:
             name = args[1]
@@ -1388,6 +1575,8 @@
         action_mapping[action](addr, port, options.use_reloader, options.admin_media_path)
     elif action == 'runfcgi':
         action_mapping[action](args[1:])
+    elif action == 'sqlflush':
+        print '\n'.join(action_mapping[action]())
     else:
         from django.db import models
         validate(silent_success=True)

Property changes on: tests/modeltests/fixtures
___________________________________________________________________
Name: svn:ignore
   + *.pyc


Index: tests/modeltests/fixtures/__init__.py
===================================================================

Property changes on: tests/modeltests/fixtures/fixtures
___________________________________________________________________
Name: svn:ignore
   + *.pyc


Index: tests/modeltests/fixtures/fixtures/fixture1.json
===================================================================
--- tests/modeltests/fixtures/fixtures/fixture1.json	(revision 0)
+++ tests/modeltests/fixtures/fixtures/fixture1.json	(revision 0)
@@ -0,0 +1,18 @@
+[
+    {
+        "pk": "1", 
+        "model": "fixtures.article", 
+        "fields": {
+            "headline": "Poker has no place on ESPN", 
+            "pub_date": "2006-06-16 11:00:00"
+        }
+    }, 
+    {
+        "pk": "2", 
+        "model": "fixtures.article", 
+        "fields": {
+            "headline": "Time to reform copyright", 
+            "pub_date": "2006-06-16 13:00:00"
+        }
+    }
+]
\ No newline at end of file
Index: tests/modeltests/fixtures/fixtures/fixture2.json
===================================================================
--- tests/modeltests/fixtures/fixtures/fixture2.json	(revision 0)
+++ tests/modeltests/fixtures/fixtures/fixture2.json	(revision 0)
@@ -0,0 +1,18 @@
+[
+    {
+        "pk": "2", 
+        "model": "fixtures.article", 
+        "fields": {
+            "headline": "Copyright is fine the way it is", 
+            "pub_date": "2006-06-16 14:00:00"
+        }
+    }, 
+    {
+        "pk": "3", 
+        "model": "fixtures.article", 
+        "fields": {
+            "headline": "Django conquers world!", 
+            "pub_date": "2006-06-16 15:00:00"
+        }
+    }
+]
\ No newline at end of file
Index: tests/modeltests/fixtures/fixtures/fixture3.xml
===================================================================
--- tests/modeltests/fixtures/fixtures/fixture3.xml	(revision 0)
+++ tests/modeltests/fixtures/fixtures/fixture3.xml	(revision 0)
@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="utf-8"?>
+<django-objects version="1.0">
+    <object pk="1" model="fixtures.article">
+        <field type="CharField" name="headline">Poker on TV is great!</field>
+        <field type="DateTimeField" name="pub_date">2006-06-16 11:00:00</field>
+    </object>
+    <object pk="4" model="fixtures.article">
+        <field type="CharField" name="headline">XML identified as leading cause of cancer</field>
+        <field type="DateTimeField" name="pub_date">2006-06-16 16:00:00</field>
+    </object>
+</django-objects>
\ No newline at end of file
Index: tests/modeltests/fixtures/models.py
===================================================================
--- tests/modeltests/fixtures/models.py	(revision 0)
+++ tests/modeltests/fixtures/models.py	(revision 0)
@@ -0,0 +1,66 @@
+"""
+39. Fixtures.
+
+Fixtures are a way of loading data into the database in bulk. Fixure data 
+can be stored in any serializable format (including JSON and XML). Fixtures 
+are identified by name, and are stored in either a directory named 'fixtures'
+in the application directory, on in one of the directories named in the 
+FIXTURE_DIRS setting.
+"""
+
+from django.db import models
+
+class Article(models.Model):
+    headline = models.CharField(maxlength=100, default='Default headline')
+    pub_date = models.DateTimeField()
+
+    def __str__(self):
+        return self.headline
+        
+    class Meta:
+        ordering = ('-pub_date', 'headline')
+        
+__test__ = {'API_TESTS': """
+>>> from django.core import management
+>>> from django.db.models import get_app
+
+# Load fixture 1. Single JSON file, with two objects.
+>>> management.install_fixtures(['fixture1'], verbosity=0)
+>>> Article.objects.all()
+[<Article: Time to reform copyright>, <Article: Poker has no place on ESPN>]
+
+# Load fixture 2. JSON file imported by default. Overwrites some existing objects
+>>> management.install_fixtures(['fixture2'], verbosity=0)
+>>> Article.objects.all()
+[<Article: Django conquers world!>, <Article: Copyright is fine the way it is>, <Article: Poker has no place on ESPN>]
+
+# Load fixture 3, XML format. 
+>>> management.install_fixtures(['fixture3'], format='xml', verbosity=0)
+>>> Article.objects.all()
+[<Article: XML identified as leading cause of cancer>, <Article: Django conquers world!>, <Article: Copyright is fine the way it is>, <Article: Poker on TV is great!>]
+
+# Reset the database representation of this app. This will delete all data.
+>>> management.flush(interactive=False)
+>>> management.syncdb(verbosity=0, interactive=False)
+>>> Article.objects.all()
+[]
+
+# Load fixture 1 again
+>>> management.install_fixtures(['fixture1'], verbosity=0)
+>>> Article.objects.all()
+[<Article: Time to reform copyright>, <Article: Poker has no place on ESPN>]
+
+# Dump the current contents of the database as a JSON fixture
+>>> management.dumpdb(['fixtures'], format='json')
+[{"pk": "2", "model": "fixtures.article", "fields": {"headline": "Time to reform copyright", "pub_date": "2006-06-16 13:00:00"}}, {"pk": "1", "model": "fixtures.article", "fields": {"headline": "Poker has no place on ESPN", "pub_date": "2006-06-16 11:00:00"}}]
+"""}
+
+from django.test import TestCase
+
+class SampleTestCase(TestCase):
+    fixtures = { 'json': ['fixture1', 'fixture2'] }
+        
+    def testClassFixtures(self):
+        "Check that test case has installed 3 fixture objects"
+        self.assertEqual(Article.objects.count(), 3)
+        self.assertEquals(str(Article.objects.all()), "[<Article: Django conquers world!>, <Article: Copyright is fine the way it is>, <Article: Poker has no place on ESPN>]")
Index: tests/modeltests/test_client/management.py
===================================================================
--- tests/modeltests/test_client/management.py	(revision 4223)
+++ tests/modeltests/test_client/management.py	(working copy)
@@ -1,10 +0,0 @@
-from django.dispatch import dispatcher
-from django.db.models import signals
-import models as test_client_app
-from django.contrib.auth.models import User
-
-def setup_test(app, created_models, verbosity):
-    # Create a user account for the login-based tests
-    User.objects.create_user('testclient','testclient@example.com', 'password')
-
-dispatcher.connect(setup_test, sender=test_client_app, signal=signals.post_syncdb)

Property changes on: tests/modeltests/test_client/fixtures
___________________________________________________________________
Name: svn:ignore
   + *.pyc


Index: tests/modeltests/test_client/fixtures/testdata.json
===================================================================
--- tests/modeltests/test_client/fixtures/testdata.json	(revision 0)
+++ tests/modeltests/test_client/fixtures/testdata.json	(revision 0)
@@ -0,0 +1,20 @@
+[
+    {
+        "pk": "1", 
+        "model": "auth.user", 
+        "fields": {
+            "username": "testclient", 
+            "first_name": "Test", 
+            "last_name": "Client", 
+            "is_active": true, 
+            "is_superuser": false, 
+            "is_staff": false, 
+            "last_login": "2006-12-17 07:03:31", 
+            "groups": [], 
+            "user_permissions": [], 
+            "password": "sha1$6efc0$f93efe9fd7542f25a7be94871ea45aa95de57161", 
+            "email": "testclient@example.com", 
+            "date_joined": "2006-12-17 07:03:31"
+        }
+    }
+]
\ No newline at end of file
Index: tests/modeltests/test_client/models.py
===================================================================
--- tests/modeltests/test_client/models.py	(revision 4223)
+++ tests/modeltests/test_client/models.py	(working copy)
@@ -19,10 +19,11 @@
 rather than the HTML rendered to the end-user.
 
 """
-from django.test.client import Client
-import unittest
+from django.test import Client, TestCase
 
-class ClientTest(unittest.TestCase):
+class ClientTest(TestCase):
+    fixtures = { 'json': ['testdata'] }
+    
     def setUp(self):
         "Set up test environment"
         self.client = Client()
Index: tests/urls.py
===================================================================
--- tests/urls.py	(revision 4223)
+++ tests/urls.py	(working copy)
@@ -6,5 +6,5 @@
 
     # Always provide the auth system login and logout views
     (r'^accounts/login/$', 'django.contrib.auth.views.login', {'template_name': 'login.html'}),
-    (r'^accounts/logout/$', 'django.contrib.auth.views.login'),
+    (r'^accounts/logout/$', 'django.contrib.auth.views.logout'),
 )
