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'(?0:
+ # sequence name in this case will be
__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
_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
__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
_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 @@
+
+
+
+
+
\ 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()
+[, ]
+
+# Load fixture 2. JSON file imported by default. Overwrites some existing objects
+>>> management.install_fixtures(['fixture2'], verbosity=0)
+>>> Article.objects.all()
+[, , ]
+
+# Load fixture 3, XML format.
+>>> management.install_fixtures(['fixture3'], format='xml', verbosity=0)
+>>> Article.objects.all()
+[, , , ]
+
+# 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()
+[, ]
+
+# 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()), "[, , ]")
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'),
)