Ticket #2333: fixtures-2.diff

File fixtures-2.diff, 43.4 KB (added by russellm, 9 years ago)

Test Fixtures, version 2

  • django/test/testcases.py

     
    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)
    57
     
    1921    def __init__(self, *args, **kwargs):
    2022        doctest.DocTestRunner.__init__(self, *args, **kwargs)
    2123        self.optionflags = doctest.ELLIPSIS
     24        management.flush(verbosity=0, interactive=False)
    2225       
    2326    def report_unexpected_exception(self, out, test, example, exc_info):
    2427        doctest.DocTestRunner.report_unexpected_exception(self,out,test,example,exc_info)
     
    2831        from django.db import transaction
    2932        transaction.rollback_unless_managed()
    3033
     34class TestCase(unittest.TestCase):   
     35    def install_fixtures(self):
     36        """If the Test Case class has a 'fixtures' member, clear the database and
     37        install the named fixtures at the start of each test.
     38       
     39        """
     40        management.flush(verbosity=0, interactive=False)
     41        if hasattr(self, 'fixtures'):
     42            management.load_data(self.fixtures, verbosity=0)
     43
     44    def run(self, result=None):
     45        """Wrapper around default run method so that user-defined Test Cases
     46        automatically call install_fixtures without having to include a call to
     47        super().
     48       
     49        """
     50        self.install_fixtures()
     51        super(TestCase, self).run(result)
  • django/test/__init__.py

     
     1"""
     2Django Unit Test and Doctest framework.
     3"""
     4
     5from django.test.client import Client
     6from django.test.testcases import TestCase
  • django/db/backends/ado_mssql/base.py

     
    134134def get_pk_default_value():
    135135    return "DEFAULT"
    136136
     137def get_sql_flush(sql_styler, full_table_list):
     138    """Return a list of SQL statements required to remove all data from
     139    all tables in the database (without actually removing the tables
     140    themselves) and put the database in an empty 'initial' state
     141    """
     142    # Return a list of 'TRUNCATE x;', 'TRUNCATE y;', 'TRUNCATE z;'... style SQL statements
     143    # TODO - SQL not actually tested against ADO MSSQL yet!
     144    # TODO - autoincrement indices reset required? See other get_sql_flush() implementations
     145    sql_list = ['%s %s;' % \
     146                (sql_styler.SQL_KEYWORD('TRUNCATE'),
     147                 sql_styler.SQL_FIELD(quote_name(table))
     148                 )  for table in full_table_list]
     149
    137150OPERATOR_MAPPING = {
    138151    'exact': '= %s',
    139152    'iexact': 'LIKE %s',
  • django/db/backends/postgresql/base.py

     
    145145def get_pk_default_value():
    146146    return "DEFAULT"
    147147
     148def get_sql_flush(style, tables, sequences):
     149    """Return a list of SQL statements required to remove all data from
     150    all tables in the database (without actually removing the tables
     151    themselves) and put the database in an empty 'initial' state
     152   
     153    """
     154    # Postgres can do 'TRUNCATE x, y, z...;'. In fact, it *has to* in order to be able to
     155    # truncate tables referenced by a foreign key in any other table. The result is a
     156    # single SQL TRUNCATE statement.
     157    if tables:
     158        sql = ['%s %s;' % \
     159            (style.SQL_KEYWORD('TRUNCATE'),
     160             style.SQL_FIELD(', '.join(quote_name(table) for table in tables))
     161        )]
     162   
     163        # 'ALTER SEQUENCE sequence_name RESTART WITH 1;'... style SQL statements
     164        # to reset sequence indices
     165        for sequence_info in sequences:
     166            table_name = sequence_info['table']
     167            column_name = sequence_info['column']
     168            if column_name and len(column_name)>0:
     169                # sequence name in this case will be <table>_<column>_seq
     170                sql.append("%s %s %s %s %s %s;" % \
     171                    (style.SQL_KEYWORD('ALTER'),
     172                    style.SQL_KEYWORD('SEQUENCE'),
     173                    style.SQL_FIELD('%s_%s_seq' % (table_name, column_name)),
     174                    style.SQL_KEYWORD('RESTART'),
     175                    style.SQL_KEYWORD('WITH'),
     176                    style.SQL_FIELD('1')
     177                    )
     178                )
     179            else:
     180                # sequence name in this case will be <table>_id_seq
     181                sql.append("%s %s %s %s %s %s;" % \
     182                    (style.SQL_KEYWORD('ALTER'),
     183                     style.SQL_KEYWORD('SEQUENCE'),
     184                     style.SQL_FIELD('%s_id_seq' % table_name),
     185                     style.SQL_KEYWORD('RESTART'),
     186                     style.SQL_KEYWORD('WITH'),
     187                     style.SQL_FIELD('1')
     188                     )
     189                )
     190        return sql
     191    else:
     192        return []
     193       
    148194# Register these custom typecasts, because Django expects dates/times to be
    149195# in Python's native (standard-library) datetime/time format, whereas psycopg
    150196# use mx.DateTime by default.
  • django/db/backends/sqlite3/base.py

     
    148148def get_pk_default_value():
    149149    return "NULL"
    150150
     151def get_sql_flush(style, tables, sequences):
     152    """Return a list of SQL statements required to remove all data from
     153    all tables in the database (without actually removing the tables
     154    themselves) and put the database in an empty 'initial' state
     155   
     156    """
     157    # NB: The generated SQL below is specific to SQLite
     158    # Note: The DELETE FROM... SQL generated below works for SQLite databases
     159    # because constraints don't exist
     160    sql = ['%s %s %s;' % \
     161            (style.SQL_KEYWORD('DELETE'),
     162             style.SQL_KEYWORD('FROM'),
     163             style.SQL_FIELD(quote_name(table))
     164             ) for table in tables]
     165    # Note: No requirement for reset of auto-incremented indices (cf. other
     166    # get_sql_flush() implementations). Just return SQL at this point
     167    return sql
     168
    151169def _sqlite_date_trunc(lookup_type, dt):
    152170    try:
    153171        dt = util.typecast_timestamp(dt)
  • django/db/backends/mysql/base.py

     
    183183def get_pk_default_value():
    184184    return "DEFAULT"
    185185
     186def get_sql_flush(style, tables, sequences):
     187    """Return a list of SQL statements required to remove all data from
     188    all tables in the database (without actually removing the tables
     189    themselves) and put the database in an empty 'initial' state
     190   
     191    """
     192    # NB: The generated SQL below is specific to MySQL
     193    # 'TRUNCATE x;', 'TRUNCATE y;', 'TRUNCATE z;'... style SQL statements
     194    # to clear all tables of all data
     195    if tables:
     196        sql = ['%s %s;' % \
     197                (style.SQL_KEYWORD('TRUNCATE'),
     198                 style.SQL_FIELD(quote_name(table))
     199                )  for table in tables]
     200        # 'ALTER TABLE table AUTO_INCREMENT = 1;'... style SQL statements
     201        # to reset sequence indices
     202        sql.extend(["%s %s %s %s %s;" % \
     203            (style.SQL_KEYWORD('ALTER'),
     204             style.SQL_KEYWORD('TABLE'),
     205             style.SQL_TABLE(quote_name(sequence['table'])),
     206             style.SQL_KEYWORD('AUTO_INCREMENT'),
     207             style.SQL_FIELD('= 1'),
     208            ) for sequence in sequences])
     209        return sql
     210    else:
     211        return []
     212
    186213OPERATOR_MAPPING = {
    187214    'exact': '= %s',
    188215    'iexact': 'LIKE %s',
  • django/db/backends/oracle/base.py

     
    117117def get_pk_default_value():
    118118    return "DEFAULT"
    119119
     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    # Return a list of 'TRUNCATE x;', 'TRUNCATE y;', 'TRUNCATE z;'... style SQL statements
     126    # TODO - SQL not actually tested against Oracle yet!
     127    # TODO - autoincrement indices reset required? See other get_sql_flush() implementations
     128    sql = ['%s %s;' % \
     129            (style.SQL_KEYWORD('TRUNCATE'),
     130             style.SQL_FIELD(quote_name(table))
     131             )  for table in tables]
     132
     133
    120134OPERATOR_MAPPING = {
    121135    'exact': '= %s',
    122136    'iexact': 'LIKE %s',
  • django/db/backends/postgresql_psycopg2/base.py

     
    105105def get_pk_default_value():
    106106    return "DEFAULT"
    107107
     108def get_sql_flush(style, tables, sequences):
     109    """Return a list of SQL statements required to remove all data from
     110    all tables in the database (without actually removing the tables
     111    themselves) and put the database in an empty 'initial' state
     112    """
     113    # Postgres can do 'TRUNCATE x, y, z...;'. In fact, it *has to* in order to be able to
     114    # truncate tables referenced by a foreign key in any other table. The result is a
     115    # single SQL TRUNCATE statement
     116    if tables:
     117        sql = ['%s %s;' % \
     118                (style.SQL_KEYWORD('TRUNCATE'),
     119                 style.SQL_FIELD(', '.join(quote_name(table) for table in tables))
     120                )]
     121        # 'ALTER SEQUENCE sequence_name RESTART WITH 1;'... style SQL statements
     122        # to reset sequence indices
     123        for sequence in sequences:
     124            table_name = sequence['table']
     125            column_name = sequence['column']
     126            if column_name and len(column_name) > 0:
     127                # sequence name in this case will be <table>_<column>_seq
     128                sql.append("%s %s %s %s %s %s;" % \
     129                    (style.SQL_KEYWORD('ALTER'),
     130                     style.SQL_KEYWORD('SEQUENCE'),
     131                     style.SQL_FIELD('%s_%s_seq' % (table_name, column_name)),
     132                     style.SQL_KEYWORD('RESTART'),
     133                     style.SQL_KEYWORD('WITH'),
     134                     style.SQL_FIELD('1')
     135                     )
     136                )
     137            else:
     138                # sequence name in this case will be <table>_id_seq
     139                sql.append("%s %s %s %s %s %s;" % \
     140                    (style.SQL_KEYWORD('ALTER'),
     141                     style.SQL_KEYWORD('SEQUENCE'),
     142                     style.SQL_FIELD('%s_id_seq' % table_name),
     143                     style.SQL_KEYWORD('RESTART'),
     144                     style.SQL_KEYWORD('WITH'),
     145                     style.SQL_FIELD('1')
     146                     )
     147                )
     148        return sql
     149    else:
     150        return []
     151       
    108152OPERATOR_MAPPING = {
    109153    'exact': '= %s',
    110154    'iexact': 'ILIKE %s',
  • django/db/backends/dummy/base.py

     
    3838get_random_function_sql = complain
    3939get_fulltext_search_sql = complain
    4040get_drop_foreignkey_sql = complain
     41get_sql_flush = complain
     42
    4143OPERATOR_MAPPING = {}
  • django/conf/global_settings.py

     
    315315# The name of the database to use for testing purposes.
    316316# If None, a name of 'test_' + DATABASE_NAME will be assumed
    317317TEST_DATABASE_NAME = None
     318
     319############
     320# FIXTURES #
     321############
     322
     323# The list of directories to search for fixtures
     324FIXTURE_DIRS = ()
  • django/core/serializers/base.py

     
    141141
    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
    147147    with the many-to-many data saved with the object.
  • django/core/serializers/__init__.py

     
    4040    if not _serializers:
    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):
    4550    if not _serializers:
  • django/core/management.py

     
    6868    cursor = connection.cursor()
    6969    return get_introspection_module().get_table_list(cursor)
    7070
     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
     89
    7190# If the foreign key points to an AutoField, a PositiveIntegerField or a
    7291# PositiveSmallIntegerField, the foreign key should be an IntegerField, not the
    7392# referred field type. Otherwise, the foreign key should be the same type of
     
    330349get_sql_reset.help_doc = "Prints the DROP TABLE SQL, then the CREATE TABLE SQL, for the given app name(s)."
    331350get_sql_reset.args = APP_ARGS
    332351
    333 def get_sql_initial_data_for_model(model):
     352def get_sql_flush():
     353    "Returns a list of the SQL statements used to flush the database"
     354    from django.db import backend
     355    statements = backend.get_sql_flush(style, _get_table_list(), _get_sequence_list())
     356    return statements
     357get_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."
     358get_sql_flush.args = ''
     359
     360def get_custom_sql_for_model(model):
    334361    from django.db import models
    335362    from django.conf import settings
    336363
     
    357384
    358385    return output
    359386
    360 def get_sql_initial_data(app):
    361     "Returns a list of the initial INSERT SQL statements for the given app."
     387def get_custom_sql(app):
     388    "Returns a list of the custom table modifying SQL statements for the given app."
    362389    from django.db.models import get_models
    363390    output = []
    364391
     
    366393    app_dir = os.path.normpath(os.path.join(os.path.dirname(app.__file__), 'sql'))
    367394
    368395    for model in app_models:
    369         output.extend(get_sql_initial_data_for_model(model))
     396        output.extend(get_custom_sql_for_model(model))
    370397
    371398    return output
    372 get_sql_initial_data.help_doc = "Prints the initial INSERT SQL statements for the given app name(s)."
    373 get_sql_initial_data.args = APP_ARGS
     399get_custom_sql.help_doc = "Prints the custom table modifying SQL statements for the given app name(s)."
     400get_custom_sql.args = APP_ARGS
    374401
     402def get_sql_initial_data(apps):
     403    "Returns a list of the initial INSERT SQL statements for the given app."
     404    return style.ERROR("This action has been renamed. Try './manage.py sqlcustom %s'." % ' '.join(apps and apps or ['app1', 'app2']))
     405get_sql_initial_data.help_doc = "RENAMED: see 'sqlcustom'"
     406get_sql_initial_data.args = ''
     407
    375408def get_sql_sequence_reset(app):
    376409    "Returns a list of the SQL statements to reset PostgreSQL sequences for the given app."
    377410    from django.db import backend, models
     
    428461
    429462def get_sql_all(app):
    430463    "Returns a list of CREATE TABLE SQL, initial-data inserts, and CREATE INDEX SQL for the given module."
    431     return get_sql_create(app) + get_sql_initial_data(app) + get_sql_indexes(app)
     464    return get_sql_create(app) + get_custom_sql(app) + get_sql_indexes(app)
    432465get_sql_all.help_doc = "Prints the CREATE TABLE, initial-data and CREATE INDEX SQL statements for the given model module name(s)."
    433466get_sql_all.args = APP_ARGS
    434467
     468def _emit_post_sync_signal(created_models, verbosity, interactive):
     469    from django.db import models
     470    from django.dispatch import dispatcher
     471    # Emit the post_sync signal for every application.
     472    for app in models.get_apps():
     473        app_name = app.__name__.split('.')[-2]
     474        if verbosity >= 2:
     475            print "Running post-sync handlers for application", app_name
     476        dispatcher.send(signal=models.signals.post_syncdb, sender=app,
     477            app=app, created_models=created_models,
     478            verbosity=verbosity, interactive=interactive)
     479
    435480def syncdb(verbosity=1, interactive=True):
    436481    "Creates the database tables for all apps in INSTALLED_APPS whose tables haven't already been created."
    437482    from django.db import connection, transaction, models, get_creation_module
    438     from django.db.models import signals
    439483    from django.conf import settings
    440     from django.dispatch import dispatcher
    441484
    442485    disable_termcolors()
    443486
     
    499542
    500543    # Send the post_syncdb signal, so individual apps can do whatever they need
    501544    # to do at this point.
     545    _emit_post_sync_signal(created_models, verbosity, interactive)
     546
     547    # Install custom SQL for the app (but only if this
     548    # is a model we've just created)
    502549    for app in models.get_apps():
    503         app_name = app.__name__.split('.')[-2]
    504         if verbosity >= 2:
    505             print "Running post-sync handlers for application", app_name
    506         dispatcher.send(signal=signals.post_syncdb, sender=app,
    507             app=app, created_models=created_models,
    508             verbosity=verbosity, interactive=interactive)
    509 
    510         # Install initial data for the app (but only if this is a model we've
    511         # just created)
    512550        for model in models.get_models(app):
    513551            if model in created_models:
    514                 initial_sql = get_sql_initial_data_for_model(model)
    515                 if initial_sql:
     552                custom_sql = get_custom_sql_for_model(model)
     553                if custom_sql:
    516554                    if verbosity >= 1:
    517                         print "Installing initial data for %s.%s model" % (app_name, model._meta.object_name)
     555                        print "Installing custom SQL for %s.%s model" % (app_name, model._meta.object_name)
    518556                    try:
    519                         for sql in initial_sql:
     557                        for sql in custom_sql:
    520558                            cursor.execute(sql)
    521559                    except Exception, e:
    522                         sys.stderr.write("Failed to install initial SQL data for %s.%s model: %s" % \
     560                        sys.stderr.write("Failed to install custom SQL for %s.%s model: %s" % \
    523561                                            (app_name, model._meta.object_name, e))
    524562                        transaction.rollback_unless_managed()
    525563                    else:
     
    544582                    else:
    545583                        transaction.commit_unless_managed()
    546584
    547 syncdb.args = ''
     585    # Install the 'initialdata' fixture, using format discovery
     586    load_data(['initial_data'], verbosity=verbosity)
     587syncdb.help_doc = "Create the database tables for all apps in INSTALLED_APPS whose tables haven't already been created."
     588syncdb.args = '[--verbosity] [--interactive]'
    548589
    549590def get_admin_index(app):
    550591    "Returns admin-index template snippet (in list form) for the given app."
     
    597638    print '\n'.join(output)
    598639diffsettings.args = ""
    599640
    600 def install(app):
    601     "Executes the equivalent of 'get_sql_all' in the current database."
    602     from django.db import connection, transaction
    603 
    604     app_name = app.__name__.split('.')[-2]
    605 
    606     disable_termcolors()
    607 
    608     # First, try validating the models.
    609     _check_for_validation_errors(app)
    610 
    611     sql_list = get_sql_all(app)
    612 
    613     try:
    614         cursor = connection.cursor()
    615         for sql in sql_list:
    616             cursor.execute(sql)
    617     except Exception, e:
    618         sys.stderr.write(style.ERROR("""Error: %s couldn't be installed. Possible reasons:
    619   * The database isn't running or isn't configured correctly.
    620   * At least one of the database tables already exists.
    621   * The SQL was invalid.
    622 Hint: Look at the output of 'django-admin.py sqlall %s'. That's the SQL this command wasn't able to run.
    623 The full error: """ % (app_name, app_name)) + style.ERROR_OUTPUT(str(e)) + '\n')
    624         transaction.rollback_unless_managed()
    625         sys.exit(1)
    626     transaction.commit_unless_managed()
    627 install.help_doc = "Executes ``sqlall`` for the given app(s) in the current database."
    628 install.args = APP_ARGS
    629 
    630641def reset(app, interactive=True):
    631642    "Executes the equivalent of 'get_sql_reset' in the current database."
    632643    from django.db import connection, transaction
     
    668679    else:
    669680        print "Reset cancelled."
    670681reset.help_doc = "Executes ``sqlreset`` for the given app(s) in the current database."
    671 reset.args = APP_ARGS
     682reset.args = '[--interactive]' + APP_ARGS
    672683
     684def flush(verbosity=1, interactive=True):
     685    "Returns all tables in the database to the same state they were in immediately after syncdb."
     686    from django.conf import settings
     687    from django.db import connection, transaction, models
     688    from django.dispatch import dispatcher
     689   
     690    disable_termcolors()
     691
     692    # First, try validating the models.
     693    _check_for_validation_errors()
     694
     695    # Import the 'management' module within each installed app, to register
     696    # dispatcher events.
     697    for app_name in settings.INSTALLED_APPS:
     698        try:
     699            __import__(app_name + '.management', {}, {}, [''])
     700        except ImportError:
     701            pass
     702   
     703    sql_list = get_sql_flush()
     704
     705    if interactive:
     706        confirm = raw_input("""
     707You have requested a flush of the database.
     708This will IRREVERSIBLY DESTROY all data currently in the database,
     709and return each table to the state it was in after syncdb.
     710Are you sure you want to do this?
     711
     712Type 'yes' to continue, or 'no' to cancel: """)
     713    else:
     714        confirm = 'yes'
     715
     716    if confirm == 'yes':
     717        try:
     718            cursor = connection.cursor()
     719            for sql in sql_list:
     720                cursor.execute(sql)
     721        except Exception, e:
     722            sys.stderr.write(style.ERROR("""Error: Database %s couldn't be flushed. Possible reasons:
     723  * The database isn't running or isn't configured correctly.
     724  * At least one of the expected database tables doesn't exist.
     725  * The SQL was invalid.
     726Hint: Look at the output of 'django-admin.py sqlflush'. That's the SQL this command wasn't able to run.
     727The full error: """ % settings.DATABASE_NAME + style.ERROR_OUTPUT(str(e)) + '\n'))
     728            transaction.rollback_unless_managed()
     729            sys.exit(1)
     730        transaction.commit_unless_managed()
     731
     732        # Emit the post sync signal. This allows individual
     733        # applications to respond as if the database had been
     734        # sync'd from scratch.
     735        _emit_post_sync_signal(models.get_models(), verbosity, interactive)
     736       
     737        # Reinstall the initial_data fixture
     738        load_data(['initial_data'], verbosity=verbosity)
     739       
     740    else:
     741        print "Flush cancelled."
     742flush.help_doc = "Executes ``sqlflush`` on the current database."
     743flush.args = '[--verbosity] [--interactive]'
     744
    673745def _start_helper(app_or_project, name, directory, other_name=''):
    674746    other = {'project': 'app', 'app': 'project'}[app_or_project]
    675747    if not _is_valid_dir_name(name):
     
    751823    yield "#     * Make sure each model has one field with primary_key=True"
    752824    yield "# Feel free to rename the models, but don't rename db_table values or field names."
    753825    yield "#"
    754     yield "# Also note: You'll have to insert the output of 'django-admin.py sqlinitialdata [appname]'"
     826    yield "# Also note: You'll have to insert the output of 'django-admin.py sqlcustom [appname]'"
    755827    yield "# into your database."
    756828    yield ''
    757829    yield 'from django.db import models'
     
    12391311test.help_doc = 'Runs the test suite for the specified applications, or the entire site if no apps are specified'
    12401312test.args = '[--verbosity] ' + APP_ARGS
    12411313
     1314def load_data(fixture_labels, verbosity=1):
     1315    "Installs the provided fixture file(s) as data in the database."
     1316    from django.db.models import get_apps
     1317    from django.core import serializers
     1318    from django.db import transaction
     1319    from django.conf import settings
     1320    import sys
     1321     
     1322    # Keep a count of the installed objects and fixtures
     1323    count = [0,0]
     1324   
     1325    humanize = lambda dirname: dirname and "'%s'" % dirname or 'absolute path'
     1326
     1327    # Start transaction management. All fixtures are installed in a
     1328    # single transaction to ensure that all references are resolved.
     1329    transaction.enter_transaction_management()
     1330    transaction.managed(True)
     1331   
     1332    app_fixtures = [os.path.join(os.path.dirname(app.__file__),'fixtures') for app in get_apps()]
     1333    for fixture_label in fixture_labels:
     1334        if verbosity > 0:
     1335            print "Loading '%s' fixtures..." % fixture_label
     1336        for fixture_dir in app_fixtures + list(settings.FIXTURE_DIRS) + ['']:
     1337            if verbosity > 1:
     1338                print "Checking %s for fixtures..." % humanize(fixture_dir)
     1339            try:
     1340                fixture_name, format = fixture_label.rsplit('.', 1)
     1341                formats = [format]
     1342            except ValueError:
     1343                fixture_name = fixture_label
     1344                formats = serializers.get_serializer_formats()
     1345           
     1346            label_found = False
     1347            for format in formats:
     1348                serializer = serializers.get_serializer(format)
     1349                if verbosity > 1:
     1350                    print "Trying %s for %s fixture '%s'..." % \
     1351                        (humanize(fixture_dir), format, fixture_name)
     1352                try:
     1353                    full_path = os.path.join(fixture_dir, '.'.join([fixture_name, format]))
     1354                    fixture = open(full_path, 'r')
     1355                    if label_found:
     1356                        fixture.close()
     1357                        print style.ERROR("Multiple fixtures named '%s' in %s. Aborting." %
     1358                            (fixture_name, humanize(fixture_dir)))
     1359                        transaction.rollback()
     1360                        transaction.leave_transaction_management()
     1361                        return
     1362                    else:
     1363                        count[1] += 1
     1364                        if verbosity > 0:
     1365                            print "Installing %s fixture '%s' from %s." % \
     1366                                (format, fixture_name, humanize(fixture_dir))
     1367                        try:
     1368                            objects =  serializers.deserialize(format, fixture)
     1369                            for obj in objects:
     1370                                count[0] += 1
     1371                                obj.save()
     1372                            label_found = True
     1373                        except Exception, e:
     1374                            fixture.close()
     1375                            sys.stderr.write(
     1376                                style.ERROR("Problem installing fixture '%s': %s\n" %
     1377                                     (full_path, str(e))))
     1378                            transaction.rollback()
     1379                            transaction.leave_transaction_management()
     1380                            return
     1381                        fixture.close()
     1382                except:
     1383                    if verbosity > 1:
     1384                        print "No %s fixture '%s' in %s." % \
     1385                            (format, fixture_name, humanize(fixture_dir))
     1386    if count[0] == 0:
     1387        if verbosity > 0:
     1388            print "No fixtures found."
     1389    else:
     1390        if verbosity > 0:
     1391            print "Installed %d object(s) from %d fixture(s)" % tuple(count)
     1392    transaction.commit()
     1393    transaction.leave_transaction_management()
     1394       
     1395load_data.help_doc = 'Installs the named fixture(s) in the database'
     1396load_data.args = "[--verbosity] fixture, fixture, ..."
     1397 
     1398def dump_data(app_labels, format='json'):
     1399    "Output the current contents of the database as a fixture of the given format"
     1400    from django.db.models import get_app, get_apps, get_models
     1401    from django.core import serializers
     1402 
     1403    if len(app_labels) == 0:
     1404        app_list = get_apps()
     1405    else:
     1406        app_list = [get_app(app_label) for app_label in app_labels]
     1407 
     1408    # Check that the serialization format exists; this is a shortcut to
     1409    # avoid collating all the objects and _then_ failing.
     1410    try:
     1411        serializers.get_serializer(format)
     1412    except KeyError:
     1413        sys.stderr.write(style.ERROR("Unknown serialization format: %s\n" % format))       
     1414   
     1415    objects = []
     1416    for app in app_list:
     1417        for model in get_models(app):
     1418            objects.extend(model.objects.all())
     1419    try:
     1420        print serializers.serialize(format, objects)
     1421    except Exception, e:
     1422        sys.stderr.write(style.ERROR("Unable to serialize database: %s\n" % e))
     1423dump_data.help_doc = 'Output the contents of the database as a fixture of the given format'
     1424dump_data.args = '[--format]' + APP_ARGS
     1425
    12421426# Utilities for command-line script
    12431427
    12441428DEFAULT_ACTION_MAPPING = {
     
    12461430    'createcachetable' : createcachetable,
    12471431    'dbshell': dbshell,
    12481432    'diffsettings': diffsettings,
     1433    'dumpdata': dump_data,
     1434    'flush': flush,
    12491435    'inspectdb': inspectdb,
    1250     'install': install,
     1436    'loaddata': load_data,
    12511437    'reset': reset,
    12521438    'runfcgi': runfcgi,
    12531439    'runserver': runserver,
     
    12551441    'sql': get_sql_create,
    12561442    'sqlall': get_sql_all,
    12571443    'sqlclear': get_sql_delete,
     1444    'sqlcustom': get_custom_sql,
     1445    'sqlflush': get_sql_flush,
    12581446    'sqlindexes': get_sql_indexes,
    12591447    'sqlinitialdata': get_sql_initial_data,
    12601448    'sqlreset': get_sql_reset,
     
    12711459    'createcachetable',
    12721460    'dbshell',
    12731461    'diffsettings',
    1274     'install',
    12751462    'reset',
    12761463    'sqlindexes',
    12771464    'syncdb',
     
    13181505        help='Tells Django to NOT prompt the user for input of any kind.')
    13191506    parser.add_option('--noreload', action='store_false', dest='use_reloader', default=True,
    13201507        help='Tells Django to NOT use the auto-reloader when running the development server.')
     1508    parser.add_option('--format', default='json', dest='format',
     1509        help='Specifies the output serialization format for fixtures')   
    13211510    parser.add_option('--verbosity', action='store', dest='verbosity', default='1',
    13221511        type='choice', choices=['0', '1', '2'],
    13231512        help='Verbosity level; 0=minimal output, 1=normal output, 2=all output'),
     
    13511540        action_mapping[action](options.plain is True)
    13521541    elif action in ('validate', 'diffsettings', 'dbshell'):
    13531542        action_mapping[action]()
    1354     elif action == 'syncdb':
     1543    elif action in ('flush', 'syncdb'):
    13551544        action_mapping[action](int(options.verbosity), options.interactive)
    13561545    elif action == 'inspectdb':
    13571546        try:
     
    13651554            action_mapping[action](args[1])
    13661555        except IndexError:
    13671556            parser.print_usage_and_exit()
    1368     elif action == 'test':
     1557    elif action in ('test', 'loaddata'):
    13691558        try:
    13701559            action_mapping[action](args[1:], int(options.verbosity))
    13711560        except IndexError:
    13721561            parser.print_usage_and_exit()
     1562    elif action == 'dumpdata':
     1563        try:
     1564            action_mapping[action](args[1:], options.format)
     1565        except IndexError:
     1566            parser.print_usage_and_exit()
    13731567    elif action in ('startapp', 'startproject'):
    13741568        try:
    13751569            name = args[1]
     
    13881582        action_mapping[action](addr, port, options.use_reloader, options.admin_media_path)
    13891583    elif action == 'runfcgi':
    13901584        action_mapping[action](args[1:])
     1585    elif action == 'sqlinitialdata':
     1586        print action_mapping[action](args[1:])
     1587    elif action == 'sqlflush':
     1588        print '\n'.join(action_mapping[action]())
    13911589    else:
    13921590        from django.db import models
    13931591        validate(silent_success=True)
  • tests/modeltests/fixtures/fixtures/fixture1.json

    Property changes on: tests/modeltests/fixtures
    ___________________________________________________________________
    Name: svn:ignore
       + *.pyc
    
    
    
    Property changes on: tests/modeltests/fixtures/fixtures
    ___________________________________________________________________
    Name: svn:ignore
       + *.pyc
    
    
     
     1[
     2    {
     3        "pk": "2",
     4        "model": "fixtures.article",
     5        "fields": {
     6            "headline": "Poker has no place on ESPN",
     7            "pub_date": "2006-06-16 12:00:00"
     8        }
     9    },
     10    {
     11        "pk": "3",
     12        "model": "fixtures.article",
     13        "fields": {
     14            "headline": "Time to reform copyright",
     15            "pub_date": "2006-06-16 13:00:00"
     16        }
     17    }
     18]
     19 No newline at end of file
  • tests/modeltests/fixtures/fixtures/fixture2.json

     
     1[
     2    {
     3        "pk": "3",
     4        "model": "fixtures.article",
     5        "fields": {
     6            "headline": "Copyright is fine the way it is",
     7            "pub_date": "2006-06-16 14:00:00"
     8        }
     9    },
     10    {
     11        "pk": "4",
     12        "model": "fixtures.article",
     13        "fields": {
     14            "headline": "Django conquers world!",
     15            "pub_date": "2006-06-16 15:00:00"
     16        }
     17    }
     18]
     19 No newline at end of file
  • tests/modeltests/fixtures/fixtures/fixture2.xml

     
     1<?xml version="1.0" encoding="utf-8"?>
     2<django-objects version="1.0">
     3    <object pk="2" model="fixtures.article">
     4        <field type="CharField" name="headline">Poker on TV is great!</field>
     5        <field type="DateTimeField" name="pub_date">2006-06-16 11:00:00</field>
     6    </object>
     7    <object pk="5" model="fixtures.article">
     8        <field type="CharField" name="headline">XML identified as leading cause of cancer</field>
     9        <field type="DateTimeField" name="pub_date">2006-06-16 16:00:00</field>
     10    </object>
     11</django-objects>
     12 No newline at end of file
  • tests/modeltests/fixtures/fixtures/fixture3.xml

     
     1<?xml version="1.0" encoding="utf-8"?>
     2<django-objects version="1.0">
     3    <object pk="2" model="fixtures.article">
     4        <field type="CharField" name="headline">Poker on TV is great!</field>
     5        <field type="DateTimeField" name="pub_date">2006-06-16 11:00:00</field>
     6    </object>
     7    <object pk="5" model="fixtures.article">
     8        <field type="CharField" name="headline">XML identified as leading cause of cancer</field>
     9        <field type="DateTimeField" name="pub_date">2006-06-16 16:00:00</field>
     10    </object>
     11</django-objects>
     12 No newline at end of file
  • tests/modeltests/fixtures/models.py

     
     1"""
     239. Fixtures.
     3
     4Fixtures are a way of loading data into the database in bulk. Fixure data
     5can be stored in any serializable format (including JSON and XML). Fixtures
     6are identified by name, and are stored in either a directory named 'fixtures'
     7in the application directory, on in one of the directories named in the
     8FIXTURE_DIRS setting.
     9"""
     10
     11from django.db import models
     12
     13class Article(models.Model):
     14    headline = models.CharField(maxlength=100, default='Default headline')
     15    pub_date = models.DateTimeField()
     16
     17    def __str__(self):
     18        return self.headline
     19       
     20    class Meta:
     21        ordering = ('-pub_date', 'headline')
     22       
     23__test__ = {'API_TESTS': """
     24>>> from django.core import management
     25>>> from django.db.models import get_app
     26
     27# Syncdb introduces 1 initial data object from initial_data.json.
     28>>> Article.objects.all()
     29[<Article: Python program becomes self aware>]
     30
     31# Load fixture 1. Single JSON file, with two objects.
     32>>> management.load_data(['fixture1.json'], verbosity=0)
     33>>> Article.objects.all()
     34[<Article: Time to reform copyright>, <Article: Poker has no place on ESPN>, <Article: Python program becomes self aware>]
     35
     36# Load fixture 2. JSON file imported by default. Overwrites some existing objects
     37>>> management.load_data(['fixture2.json'], verbosity=0)
     38>>> Article.objects.all()
     39[<Article: Django conquers world!>, <Article: Copyright is fine the way it is>, <Article: Poker has no place on ESPN>, <Article: Python program becomes self aware>]
     40
     41# Load fixture 3, XML format.
     42>>> management.load_data(['fixture3.xml'], verbosity=0)
     43>>> Article.objects.all()
     44[<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!>, <Article: Python program becomes self aware>]
     45
     46# Load a fixture that doesn't exist
     47>>> management.load_data(['unknown.json'], verbosity=0)
     48
     49# object list is unaffected
     50>>> Article.objects.all()
     51[<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!>, <Article: Python program becomes self aware>]
     52
     53# Reset the database representation of this app. This will delete all data.
     54>>> management.flush(verbosity=0, interactive=False)
     55>>> Article.objects.all()
     56[<Article: Python program becomes self aware>]
     57
     58# Load fixture 1 again, using format discovery
     59>>> management.load_data(['fixture1'], verbosity=0)
     60>>> Article.objects.all()
     61[<Article: Time to reform copyright>, <Article: Poker has no place on ESPN>, <Article: Python program becomes self aware>]
     62
     63# Try to load fixture 2 using format discovery; this will fail
     64# because there are two fixture2's in the fixtures directory
     65>>> management.load_data(['fixture2'], verbosity=0) # doctest: +ELLIPSIS
     66Multiple fixtures named 'fixture2' in '.../fixtures'. Aborting.
     67
     68>>> Article.objects.all()
     69[<Article: Time to reform copyright>, <Article: Poker has no place on ESPN>, <Article: Python program becomes self aware>]
     70
     71# Dump the current contents of the database as a JSON fixture
     72>>> management.dump_data(['fixtures'], format='json')
     73[{"pk": "3", "model": "fixtures.article", "fields": {"headline": "Time to reform copyright", "pub_date": "2006-06-16 13:00:00"}}, {"pk": "2", "model": "fixtures.article", "fields": {"headline": "Poker has no place on ESPN", "pub_date": "2006-06-16 12:00:00"}}, {"pk": "1", "model": "fixtures.article", "fields": {"headline": "Python program becomes self aware", "pub_date": "2006-06-16 11:00:00"}}]
     74"""}
     75
     76from django.test import TestCase
     77
     78class SampleTestCase(TestCase):
     79    fixtures = ['fixture1.json', 'fixture2.json']
     80       
     81    def testClassFixtures(self):
     82        "Check that test case has installed 4 fixture objects"
     83        self.assertEqual(Article.objects.count(), 4)
     84        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>, <Article: Python program becomes self aware>]")
  • tests/modeltests/test_client/management.py

     
    1 from django.dispatch import dispatcher
    2 from django.db.models import signals
    3 import models as test_client_app
    4 from django.contrib.auth.models import User
    5 
    6 def setup_test(app, created_models, verbosity):
    7     # Create a user account for the login-based tests
    8     User.objects.create_user('testclient','testclient@example.com', 'password')
    9 
    10 dispatcher.connect(setup_test, sender=test_client_app, signal=signals.post_syncdb)
  • tests/modeltests/test_client/fixtures/testdata.json

    Property changes on: tests/modeltests/test_client/fixtures
    ___________________________________________________________________
    Name: svn:ignore
       + *.pyc
    
    
     
     1[
     2    {
     3        "pk": "1",
     4        "model": "auth.user",
     5        "fields": {
     6            "username": "testclient",
     7            "first_name": "Test",
     8            "last_name": "Client",
     9            "is_active": true,
     10            "is_superuser": false,
     11            "is_staff": false,
     12            "last_login": "2006-12-17 07:03:31",
     13            "groups": [],
     14            "user_permissions": [],
     15            "password": "sha1$6efc0$f93efe9fd7542f25a7be94871ea45aa95de57161",
     16            "email": "testclient@example.com",
     17            "date_joined": "2006-12-17 07:03:31"
     18        }
     19    }
     20]
     21 No newline at end of file
  • tests/modeltests/test_client/models.py

     
    1919rather than the HTML rendered to the end-user.
    2020
    2121"""
    22 from django.test.client import Client
    23 import unittest
     22from django.test import Client, TestCase
    2423
    25 class ClientTest(unittest.TestCase):
     24class ClientTest(TestCase):
     25    fixtures = ['testdata.json']
     26   
    2627    def setUp(self):
    2728        "Set up test environment"
    2829        self.client = Client()
  • tests/urls.py

     
    66
    77    # Always provide the auth system login and logout views
    88    (r'^accounts/login/$', 'django.contrib.auth.views.login', {'template_name': 'login.html'}),
    9     (r'^accounts/logout/$', 'django.contrib.auth.views.login'),
     9    (r'^accounts/logout/$', 'django.contrib.auth.views.logout'),
    1010)
Back to Top