michiya / django-pyodbc-azure

Django backend for Microsoft SQL Server and Azure SQL Database using pyodbc
https://pypi.python.org/pypi/django-pyodbc-azure
BSD 3-Clause "New" or "Revised" License
321 stars 140 forks source link

Django thinks migrations are not applied #54

Open unformatt opened 8 years ago

unformatt commented 8 years ago

Not sure if this is related to django-pyodbc-azure (but I think it is because I tested with MySQL also). So first I run migrations:

>>> python manage.py migrate                                                                                                                                               
Operations to perform:
  Apply all migrations: admin, contenttypes, auth, sessions
Running migrations:
  Rendering model states... DONE
  Applying contenttypes.0001_initial... OK
  Applying auth.0001_initial... OK
  Applying admin.0001_initial... OK
  Applying admin.0002_logentry_remove_auto_add... OK
  Applying contenttypes.0002_remove_content_type_name... OK
  Applying auth.0002_alter_permission_name_max_length... OK
  Applying auth.0003_alter_user_email_max_length... OK
  Applying auth.0004_alter_user_username_opts... OK
  Applying auth.0005_alter_user_last_login_null... OK
  Applying auth.0006_require_contenttypes_0002... OK
  Applying auth.0007_alter_validators_add_error_messages... OK
  Applying sessions.0001_initial... OK

Then try to run django, it warns me about migrations:

>>> ./manage.py runserver                                                                                                                                                  
Performing system checks...

System check identified no issues (0 silenced).

You have unapplied migrations; your app may not work properly until they are applied.
Run 'python manage.py migrate' to apply them.

So try to run migrations again (which fails because I already did):

python manage.py migrate                                                                                                                                                (master)
Operations to perform:
  Apply all migrations: admin, contenttypes, auth, sessions
Running migrations:
  Rendering model states... DONE
  Applying contenttypes.0001_initial...Traceback (most recent call last):
  File "manage.py", line 10, in <module>
    execute_from_command_line(sys.argv)
  File "env/lib/python2.7/site-packages/django/core/management/__init__.py", line 353, in execute_from_command_line
    utility.execute()
  File "env/lib/python2.7/site-packages/django/core/management/__init__.py", line 345, in execute
    self.fetch_command(subcommand).run_from_argv(self.argv)
  File "env/lib/python2.7/site-packages/django/core/management/base.py", line 348, in run_from_argv
    self.execute(*args, **cmd_options)
  File "env/lib/python2.7/site-packages/django/core/management/base.py", line 399, in execute
    output = self.handle(*args, **options)
  File "env/lib/python2.7/site-packages/django/core/management/commands/migrate.py", line 200, in handle
    executor.migrate(targets, plan, fake=fake, fake_initial=fake_initial)
  File "env/lib/python2.7/site-packages/django/db/migrations/executor.py", line 92, in migrate
    self._migrate_all_forwards(plan, full_plan, fake=fake, fake_initial=fake_initial)
  File "env/lib/python2.7/site-packages/django/db/migrations/executor.py", line 121, in _migrate_all_forwards
    state = self.apply_migration(state, migration, fake=fake, fake_initial=fake_initial)
  File "env/lib/python2.7/site-packages/django/db/migrations/executor.py", line 198, in apply_migration
    state = migration.apply(state, schema_editor)
  File "env/lib/python2.7/site-packages/django/db/migrations/migration.py", line 123, in apply
    operation.database_forwards(self.app_label, schema_editor, old_state, project_state)
  File "env/lib/python2.7/site-packages/django/db/migrations/operations/models.py", line 59, in database_forwards
    schema_editor.create_model(model)
  File "env/lib/python2.7/site-packages/sql_server/pyodbc/schema.py", line 489, in create_model
    self.execute(sql, params or None)
  File "env/lib/python2.7/site-packages/sql_server/pyodbc/schema.py", line 538, in execute
    cursor.execute(sql, params)
  File "env/lib/python2.7/site-packages/django/db/backends/utils.py", line 79, in execute
    return super(CursorDebugWrapper, self).execute(sql, params)
  File "env/lib/python2.7/site-packages/django/db/backends/utils.py", line 64, in execute
    return self.cursor.execute(sql, params)
  File "env/lib/python2.7/site-packages/django/db/utils.py", line 95, in __exit__
    six.reraise(dj_exc_type, dj_exc_value, traceback)
  File "env/lib/python2.7/site-packages/django/db/backends/utils.py", line 62, in execute
    return self.cursor.execute(sql)
  File "env/lib/python2.7/site-packages/sql_server/pyodbc/base.py", line 537, in execute
    return self.cursor.execute(sql, params)
django.db.utils.ProgrammingError: ('42S01', "[42S01] [FreeTDS][SQL Server]There is already an object named 'django_content_type' in the database. (2714) (SQLExecDirectW)")
unformatt commented 8 years ago

I've found that migrations names are getting truncated in django_migrations tables:

http://i.imgur.com/hnSeuXy.png

shaib commented 8 years ago

It looks like all your names get cut in half, which I'm pretty sure neither Django nor django-pyodbc-azure would do on purpose. Could there be something funny with your character encodings, which would make some component along the way use the number of characters as length in bytes (but only for the "name" column)? Perhaps a weird encoding in the file system (because that's where the migration names are taken from)?

It would probably help if you said what operating system your client is on (Mac? Linux? Cygwin?)

unformatt commented 8 years ago

I'm on OSX. Having dealt with MS SQL <--> python character encoding nightmares before, I agree it probably lies within there.

unformatt commented 8 years ago

Sorry this doesn't pertain exactly to django-pyodbc-azure but I can't find any answers. I've snooped into django/db/migrations/recorder.py and when it saves the name of the migration, it's set to u'0001_initial', when I load the same record and print it, it's '0\x00\U0009005f\x00ions\x00\uffff@\U00012068'.

I can't figure out where to set the character encoding. I've tried in django settings and odbc.ini.

I've tested this manually and the cause is definitely unicode

from django.db.migrations.recorder import MigrationRecorder
m = MigrationRecorder.Migration.objects.get(id=1)
m.name = 'the_correct_name'  # this saves correctly
m.name = u'the_correct_name'  # this doesn't save correctly
m.save()
unformatt commented 8 years ago

My MS SQL DB shows DEFAULT_CHARACTER_SET_NAME as "iso_1". I added 'driver_charset': 'iso-8859-1' to database OPTIONS and this has seemed to fix the issue.

@michiya - what DB charset do you use where you aren't required to have driver_charset defined?

michiya commented 8 years ago

Hi,

I always use UTF-8 encoding when I work with FreeTDS by setting client charset to UTF-8 in my freetds.conf. http://www.freetds.org/userguide/freetdsconf.htm

Hope this helps.