wemake-services / django-test-migrations

Test django schema and data migrations, including migrations' order and best practices.

Home Page:https://pypi.org/project/django-test-migrations/

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

django.db.utils.OperationalError: (1050, "Table 'bar' already exists")

ulyssejdv opened this issue · comments

Hi :)

First of all, thank you very much for the package I think it will be very helpfull for the Django community.

Unfortunately i encounter some issues :( I'm sure I'm not using it the right way ...

There is an example of test I wrote.

@pytest.mark.django_db()
def test_should_copy_cads_validations_to_validations_demonstrations(migrator):

    old_state = migrator.apply_initial_migration(
        ("validations", "0004_auto_20230623_1110")
    )

    # .. create some models here ..

    new_state = migrator.apply_tested_migration(
        ("validations", "0005_auto_20230713_1115")
    )
    # After the initial migration is done, we can use the model state:
    Bar = new_state.apps.get_model("foo", "Bar")

   # .. some assert here ..

When I execute this test I have the folloing error :

django.db.utils.OperationalError: (1050, "Table 'foo_bar' already exists")

When i stop on a breakpoint in debug mode in my IDE, there are table already created before apply_initial_migration was called (the error source I think) but I don't understand why tables are already present. I execute this test in total isolation, make this there is no table in the DB.

If you have an idea, I will really appreciate :)

I use a MySQL 5.7, python 3.9, Django 3.2

hi @ulyssejdv,

can you say what line exactly inside apply_initial_migration is causing the error?
Generally, in apply_initial_migration we are dropping all models tables, before running migrations, so please double check foo.Bar is really a Django model (not a DB view).
Do you use the --migrations flag when running pytest?
It will be really useful if you could create a PR or paste the simplest code that is causing the issue you have spotted, so we can debug it, help you, and potentially fix the problem in the project ;)

There is the full trace

  Applying repairs.0022_auto_20230213_0904... OK
  Applying repairs.0023_repairpartnumberevolution_order... OK
  Applying sessions.0001_initial... OK
  Applying validations.0001_initial... OK
  Applying validations.0002_alter_demonstration_key_arguments... OK
  Applying validations.0003_alter_validatedccl_demonstration... OK
  Applying validations.0004_validatedmodificationexplication... OK
  Applying validations.0005_auto_20230713_1115... OK
Creating test database for alias 'default' ('test_ecad')...
FAILED [100%]
tests\apps\validations\migrations\test_migration_0005.py:5 (test_should_copy_cads_validations_to_validations_demonstrations)
self = <django.db.backends.utils.CursorWrapper object at 0x000001D72EBB34F0>
sql = 'CREATE TABLE `cads_page` (`id` integer AUTO_INCREMENT NOT NULL PRIMARY KEY, `full_path` varchar(255) NOT NULL, `updated_at` datetime(6) NOT NULL, `doc_id` char(32) NOT NULL, `updated_by_id` integer NULL)'
params = None
ignored_wrapper_args = (False, {'connection': <django.db.backends.mysql.base.DatabaseWrapper object at 0x000001D72BE6B880>, 'cursor': <django.db.backends.utils.CursorWrapper object at 0x000001D72EBB34F0>})

    def _execute(self, sql, params, *ignored_wrapper_args):
        self.db.validate_no_broken_transaction()
        with self.db.wrap_database_errors:
            if params is None:
                # params default might be backend specific.
>               return self.cursor.execute(sql)

C:\SafApp\virtualenvs\ecad\lib\site-packages\django\db\backends\utils.py:82: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _

self = <django.db.backends.mysql.base.CursorWrapper object at 0x000001D72EC26EE0>
query = 'CREATE TABLE `cads_page` (`id` integer AUTO_INCREMENT NOT NULL PRIMARY KEY, `full_path` varchar(255) NOT NULL, `updated_at` datetime(6) NOT NULL, `doc_id` char(32) NOT NULL, `updated_by_id` integer NULL)'
args = None

    def execute(self, query, args=None):
        try:
            # args is None means no string interpolation
>           return self.cursor.execute(query, args)

C:\SafApp\virtualenvs\ecad\lib\site-packages\django\db\backends\mysql\base.py:73: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _

self = <MySQLdb.cursors.Cursor object at 0x000001D72EC26FD0>
query = b'CREATE TABLE `cads_page` (`id` integer AUTO_INCREMENT NOT NULL PRIMARY KEY, `full_path` varchar(255) NOT NULL, `updated_at` datetime(6) NOT NULL, `doc_id` char(32) NOT NULL, `updated_by_id` integer NULL)'
args = None

    def execute(self, query, args=None):
        """Execute a query.
    
        query -- string, query to execute on server
        args -- optional sequence or mapping, parameters to use with query.
    
        Note: If args is a sequence, then %s must be used as the
        parameter placeholder in the query. If a mapping is used,
        %(key)s must be used as the placeholder.
    
        Returns integer represents rows affected, if any
        """
        while self.nextset():
            pass
        db = self._get_db()
    
        if isinstance(query, str):
            query = query.encode(db.encoding)
    
        if args is not None:
            if isinstance(args, dict):
                nargs = {}
                for key, item in args.items():
                    if isinstance(key, str):
                        key = key.encode(db.encoding)
                    nargs[key] = db.literal(item)
                args = nargs
            else:
                args = tuple(map(db.literal, args))
            try:
                query = query % args
            except TypeError as m:
                raise ProgrammingError(str(m))
    
        assert isinstance(query, (bytes, bytearray))
>       res = self._query(query)

C:\SafApp\virtualenvs\ecad\lib\site-packages\MySQLdb\cursors.py:206: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _

self = <MySQLdb.cursors.Cursor object at 0x000001D72EC26FD0>
q = b'CREATE TABLE `cads_page` (`id` integer AUTO_INCREMENT NOT NULL PRIMARY KEY, `full_path` varchar(255) NOT NULL, `updated_at` datetime(6) NOT NULL, `doc_id` char(32) NOT NULL, `updated_by_id` integer NULL)'

    def _query(self, q):
        db = self._get_db()
        self._result = None
>       db.query(q)

C:\SafApp\virtualenvs\ecad\lib\site-packages\MySQLdb\cursors.py:319: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _

self = <_mysql.connection open to '127.0.0.1' at 000001D72B6DD8F0>
query = b'CREATE TABLE `cads_page` (`id` integer AUTO_INCREMENT NOT NULL PRIMARY KEY, `full_path` varchar(255) NOT NULL, `updated_at` datetime(6) NOT NULL, `doc_id` char(32) NOT NULL, `updated_by_id` integer NULL)'

    def query(self, query):
        # Since _mysql releases GIL while querying, we need immutable buffer.
        if isinstance(query, bytearray):
            query = bytes(query)
>       _mysql.connection.query(self, query)
E       MySQLdb._exceptions.OperationalError: (1050, "Table 'cads_page' already exists")

C:\SafApp\virtualenvs\ecad\lib\site-packages\MySQLdb\connections.py:259: OperationalError

The above exception was the direct cause of the following exception:

    @pytest.mark.django_db(transaction=True)
    def test_should_copy_cads_validations_to_validations_demonstrations():
        """Ensures that the initial migration works."""
    
        migrator = Migrator(database="default")
    
>       old_state = migrator.apply_initial_migration(
            ("validations", "0004_validatedmodificationexplication"),
        )

test_migration_0005.py:12: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
C:\SafApp\virtualenvs\ecad\lib\site-packages\django_test_migrations\migrator.py:61: in apply_initial_migration
    return self._migrate(targets, plan=plan)
C:\SafApp\virtualenvs\ecad\lib\site-packages\django_test_migrations\migrator.py:84: in _migrate
    return self._executor.migrate(migration_targets, plan=plan)
C:\SafApp\virtualenvs\ecad\lib\site-packages\django\db\migrations\executor.py:117: in migrate
    state = self._migrate_all_forwards(state, plan, full_plan, fake=fake, fake_initial=fake_initial)
C:\SafApp\virtualenvs\ecad\lib\site-packages\django\db\migrations\executor.py:147: in _migrate_all_forwards
    state = self.apply_migration(state, migration, fake=fake, fake_initial=fake_initial)
C:\SafApp\virtualenvs\ecad\lib\site-packages\django\db\migrations\executor.py:227: in apply_migration
    state = migration.apply(state, schema_editor)
C:\SafApp\virtualenvs\ecad\lib\site-packages\django\db\migrations\migration.py:126: in apply
    operation.database_forwards(self.app_label, schema_editor, old_state, project_state)
C:\SafApp\virtualenvs\ecad\lib\site-packages\django\db\migrations\operations\models.py:92: in database_forwards
    schema_editor.create_model(model)
C:\SafApp\virtualenvs\ecad\lib\site-packages\django\db\backends\base\schema.py:345: in create_model
    self.execute(sql, params or None)
C:\SafApp\virtualenvs\ecad\lib\site-packages\django\db\backends\base\schema.py:145: in execute
    cursor.execute(sql, params)
C:\SafApp\virtualenvs\ecad\lib\site-packages\django\db\backends\utils.py:66: in execute
    return self._execute_with_wrappers(sql, params, many=False, executor=self._execute)
C:\SafApp\virtualenvs\ecad\lib\site-packages\django\db\backends\utils.py:75: in _execute_with_wrappers
    return executor(sql, params, many, context)
C:\SafApp\virtualenvs\ecad\lib\site-packages\django\db\backends\utils.py:84: in _execute
    return self.cursor.execute(sql, params)
C:\SafApp\virtualenvs\ecad\lib\site-packages\django\db\utils.py:90: in __exit__
    raise dj_exc_value.with_traceback(traceback) from exc_value
C:\SafApp\virtualenvs\ecad\lib\site-packages\django\db\backends\utils.py:82: in _execute
    return self.cursor.execute(sql)
C:\SafApp\virtualenvs\ecad\lib\site-packages\django\db\backends\mysql\base.py:73: in execute
    return self.cursor.execute(query, args)
C:\SafApp\virtualenvs\ecad\lib\site-packages\MySQLdb\cursors.py:206: in execute
    res = self._query(query)
C:\SafApp\virtualenvs\ecad\lib\site-packages\MySQLdb\cursors.py:319: in _query
    db.query(q)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _

self = <_mysql.connection open to '127.0.0.1' at 000001D72B6DD8F0>
query = b'CREATE TABLE `cads_page` (`id` integer AUTO_INCREMENT NOT NULL PRIMARY KEY, `full_path` varchar(255) NOT NULL, `updated_at` datetime(6) NOT NULL, `doc_id` char(32) NOT NULL, `updated_by_id` integer NULL)'

    def query(self, query):
        # Since _mysql releases GIL while querying, we need immutable buffer.
        if isinstance(query, bytearray):
            query = bytes(query)
>       _mysql.connection.query(self, query)
E       django.db.utils.OperationalError: (1050, "Table 'cads_page' already exists")

C:\SafApp\virtualenvs\ecad\lib\site-packages\MySQLdb\connections.py:259: OperationalError

I tried to switch to sqlite to test if the error only concerned MySQL but I have the same issue ^^

In django_test_migrations.sql when we try to get all table, there is only one missing ... the cads_page table ^^

tables = connection.introspection.django_table_names(
        only_existing=True,
        include_views=False,
)

I will search why !

Final update (I think) :

I don't know why but django core api ignored one of my model...
I have the followed project structure :

apps/
    - cads/
        - models/
            - shared.py ( with some models)
            - pages.py ( with class Page(models.Model): )

I move Page from pages.py to shared.py and it works....

Thank you very much for your support :) this a realy helpfull django package <3

I close the issue but if want more details or keep me as tester PM me !

@ulyssejdv thank you!

It seems like your Page model from apps/cads/models/pages.py was not imported in a proper place (at Django, you need to put models into the models package - either models.py or models/__init__.py) or import them in the app ready hook method.

Reference: https://docs.djangoproject.com/en/5.0/topics/db/models/#organizing-models-in-a-package