| | 1 | I have a situation where I want to modify each database table as it is created. The solution I created should work for PostgreSQL or any backend. In this particular case, my project is using a MySQL database, and requires transactions- that is, InnoDB storage. Alas, my hosting service defaults all tables to MyISAM, and I can't change that. |
| | 2 | |
| | 3 | The usual method is to create a custom sql file "app_model.sql" that alters the table. That gets tedious when one has dozens, or hundreds of models. |
| | 4 | |
| | 5 | Here's an 80% solution. Add code like the below to your app's {{{ __init__.py }}}: |
| | 6 | |
| | 7 | |
| | 8 | {{{ |
| | 9 | # use a consistant MySQL storage type for all apps, all models |
| | 10 | |
| | 11 | from django.dispatch import dispatcher |
| | 12 | from django.db.models import signals |
| | 13 | |
| | 14 | default_storage = 'InnoDB' |
| | 15 | storage = { 'app_model_has_different_storage':'MyISAM'} |
| | 16 | skip = set(('app_model_should_go_unchanged',)) |
| | 17 | |
| | 18 | def modify_MySQL_storage(sender, app, created_models, verbosity, interactive): |
| | 19 | from django.db import connection |
| | 20 | cursor = connection.cursor() |
| | 21 | |
| | 22 | for model in created_models: |
| | 23 | db_table=model._meta.db_table |
| | 24 | if db_table not in skip: |
| | 25 | skip.add(db_table) |
| | 26 | engine = storage.get(model._meta.db_table,default_storage) |
| | 27 | stmt = 'ALTER TABLE %s ENGINE=%s' % (db_table,engine) |
| | 28 | if verbosity > 1: print ' ',stmt |
| | 29 | cursor.execute(stmt) |
| | 30 | |
| | 31 | # Modify SQL storage after any app installs |
| | 32 | dispatcher.connect(modify_MySQL_storage,signal=signals.post_syncdb) |
| | 33 | }}} |
| | 34 | |
| | 35 | I call this an 80% solution because it won't touch the intermediary M2M tables. For that, we need a signal that fires on the creation of each database table, or lists all tables that were created, which doesn't exist. |
| | 36 | |
| | 37 | If most tables are InnoDB but you try setting one or a few to MyISAM using {{{storage}}}, MySQL may complain about foreign keys... rather than making that work, I found it simpler to add the table to {{{skip}}} and write out the alter statement the old way, with the raw sql in {{{app/sql/app_model.sql}}}. |
| | 38 | |
| | 39 | The code could be refined to only alter models in a particular app. |