77

I started a django 1.8 project, which uses the migrations system.
Somehow along the way things got messy, so I erased the migrations folders and table from the DB, and now I'm trying to reconstruct them, with no success.

I have three apps (3 models.py files), and the models reflect the tables EXACTLY!

The best approach that I've found so far was:

  1. Erase all migrations folders. Done!
  2. Delete everything from the django_migrations table. Done!
  3. Run python manage.py makemigrations --empty <app> for every app. Done!
  4. Run python manage.py migrate --fake. Done! (although it works only if I run it after every makemigrations command.

Now I add a new field, run the makemigrations command, and I receive the following error:
django.db.utils.OperationalError: (1054, "Unknown column 'accounts_plan.max_item_size' in 'field list'")

I've been burning HOURS on this thing. How the h**l can I initialize the migrations so I can continue working without migration interruptions every time?

Why is it so complicated? Why isn't there a simple one-liner: initiate_migrations_from_schema?

EDIT:
Now things get even nastier. I truncated the django_migrations table and deleted all the migrations folder.
Now I try to run python manage.py migrate --fake-initial (something I found in the DEV docs), just so it sets up all of Django's 'internal' apps (auth, session, etc) and I'm getting:
(1054, "Unknown column 'name' in 'django_content_type'").
Now, this "column" is not a real column. It's a @property defined in Django's contenttypes app. WHAT IS GOING ON HERE? Why is it identifying the name property as a real column?

user1102018
  • 4,369
  • 6
  • 26
  • 33
  • Did you delete the actual table? Or did you just empty it? – rnevius Apr 27 '15 at 05:56
  • I just emptied it: `delete from django_migrations` – user1102018 Apr 27 '15 at 06:02
  • You likely need to [drop all of the tables](http://stackoverflow.com/questions/3414247/django-drop-all-tables-from-database), not just empty them – rnevius Apr 27 '15 at 06:06
  • I have data in these tables and I don't want to drop them. I know I can back them up, drop, create and restore the data. But I have 10 tables and I don't want to do that. I just want to initiate the migrations. – user1102018 Apr 27 '15 at 06:10

4 Answers4

174

Finally got it to work, although I don't know why and I hope it will work in the future.
After doing numerous trials and going through Django's dev site (link).
Here are the steps (for whoever runs into this problem):

  1. Empty the django_migrations table: delete from django_migrations;
  2. For every app, delete its migrations folder: rm -rf <app>/migrations/
  3. Reset the migrations for the "built-in" apps: python manage.py migrate --fake
  4. For each app run: python manage.py makemigrations <app>. Take care of dependencies (models with ForeignKey's should run after their parent model).
  5. Finally: python manage.py migrate --fake-initial

After that I ran the last command without the --fake-initial flag, just to make sure.

Now everything works and I can use the migrations system normally.

I'm sure I'm not the only one who encounters this issue. It must be documented better and even simplified.

Update for Django 1.9 users:
I had this scenario again with a Django 1.9.4, and step 5 failed.
All I had to do is replace --fake-initial with --fake to make it work.

user1102018
  • 4,369
  • 6
  • 26
  • 33
  • I'm getting `RuntimeError: Error creating new content types. Please make sure contenttypes is migrated before trying to migrate apps individually.` at the final step for Django 1.8.3 though. Do you know why? – ericn Sep 21 '15 at 03:45
  • 1
    @eric try running `python manage.py migrate contenttypes` before the final step. – azalea Oct 14 '15 at 19:33
  • 9
    should be documented under "How to reset migrations". maybe even a reset_migrations management command – Adam Spence May 04 '16 at 22:04
  • Thanks for this, I couldn't find how to do this elsewhere... This really should be documented. – E... Aug 22 '16 at 13:49
  • Had to delete from django_migrations; and then run python manage.py migrate. and it worked. – Stryker Nov 23 '16 at 15:24
  • `find . -path *migrations* -name "*.py" -not -path "*__init__*" -delete` will delete all migration files if you run this from your root directory. Run `find . -path *migrations* -name "*.py" -not -path "*__init__*"` before just to be safe – Skam Jan 30 '17 at 04:11
  • I had success with this (thank you) on the project based on pydanny cookiecutter django. However there is some trick with the generated myproject/contrib/sites (I don't understand it much). Without migrations/ in myproject/contrib/sites I was unable to run step 3 (migrate fake). So I recommend: step 2: rename migrations->migOLD, step 3b: revert migOLD->migrations where step 3 fails and run 3 again, step 3c: repeat step 1 (delete django-migrations content again), step 3d: repeat step 2 (where you have applied step 3b). . . . resetmigrations would be great ! – mirek Jul 31 '18 at 12:32
  • Additionaly I'm not sure if it is good idea to "reset migrations" for foreign applications (like django itself: auth,..). What will happen together with their upgraded version? If we want to do it for our own applications, then we could temporary remove them from INSTALLED_APPS during the step 3 (migrate fake). Maybe there are some imports from own applications in urls.py and step 3 will fail. (So I have temporary removed everything from urls.py. However it is neccessary to have at least one url defined.). So we can "reset our migrations" and leave the django+3rd party without any change. – mirek Jul 31 '18 at 13:48
  • Of course there are problems with other db instances later. This means imported sqldata/fixtures too (you have to recreate them from the final state later). I do it so: 1) git checkout , 2) ./manage.py migrate #we have the last state, 3) git checkout , 4) delete from django_migrations; 4) ./manage.py migrate --fake – mirek Jul 31 '18 at 14:54
4

django ..., 1.8, 1.9, ...

What you want to achieve is squashing existing migrations and use replacement for them.

How to do it right without using any command when releasing (a case without impact on database and coworkers).

  1. For every app, get rid of its migrations folder: mv <app>/migrations/ <app>/migrationsOLD/

  2. For each that app run: python manage.py makemigrations <app>.

  3. Customize each new migration:

    • if you have a complex app, or more apps and related models between them, to avoid CircularDependencyError or ValueError: Unhandled pending operations for models:

      prepare second empty migration in <app> 0002_initial2.py (put there dependency to app_other::0001_initial.py and <app>::0001_initial.py as well - all ForeignKey, M2M related to models created in 0001 migration step in other apps)

      All must be in order - sometimes it will require more migrations to prepare. Take care of dependencies attribute here in each Migration.

    • take care of initial values - verify yourself all RunPython actions from migrationsOLD and copy the code to new initial migration if needed.

    • (optional for --fake-initial) Add initial=True to all new Migration classes (0002 too if was added).

    • Add replaces attribute in new Migration class. (like own custom a squashmigrations). Put there all old migrations from <app>
  4. Verify everything with makemigrations.

    assert "No changes detected"

  5. Check if migrate -l show [x] everywhere

    assert similar:

    [X] 0001_initial

    [X] 0002_initial2 (102 squashed migrations)

Example:

For old:

0001_initial.py
0002_auto.py
...
0103_auto.py

prepare:

0001_initial.py
0002_initial2.py  (optional but sometimes required to satisfy dependency)

and add to replacesto last one (0002 here, can be 0001):

replaces = [(b'<app>', '0002_auto.py'), ..., (b'<app>', '0103_auto.py')]

0001_initial.py should be named the same way as old one.

0002_initial2.py is new one, but it's a replacement for old migrations so Django will treat it as loaded.

Sławomir Lenart
  • 7,543
  • 4
  • 45
  • 61
2

I've run into this scenario but I've never had to drop the database to solve it. Typically I delete the migrations folder from the app's, and remove the migration entries from the database.

I would try to run make migrations one app at a time. If any of the app's rely upon other tables obviously add them last.

Also I usually just run, python manage.py makemigrations then just python manage.py migrate Even with the initial migration it should work fine with Django 1.7 and 1.8.

Chris Hawkes
  • 11,923
  • 6
  • 58
  • 68
0

If you are using routers, might be a problem there. Check method allow_migrate if it is executed in a right way in routers.py. Try to set return value always to be True, and check whether it resolves problem,

def allow_migrate(self, db, app_label, model_name=None, **hints):
    return True
AmirM
  • 1,089
  • 1
  • 12
  • 26