Django 1.8:为现有模式创build初始迁移
我开始了一个使用迁移系统的django 1.8项目。
不知何故,事情变得混乱了,所以我从数据库中删除了迁移文件夹和表格,现在我试图重build它们,但没有成功。
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:
- Erase all
migrations
folders. Done! - Delete everything from the
django_migrations
table. Done! - Run
python manage.py makemigrations --empty <app>
for every app. Done! - Run
python manage.py migrate --fake
. Done! (although it works only if I run it after everymakemigrations
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?
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):
- Empty the
django_migrations
table:delete from django_migrations;
- For every app, delete its
migrations
folder:rm -rf <app>/migrations/
- Reset the migrations for the "built-in" apps:
python manage.py migrate --fake
- For each app run:
python manage.py makemigrations <app>
. Take care of dependencies (models with ForeignKey's should run after their parent model). - 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.
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.
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).
-
For every app, get rid of its migrations folder:
mv <app>/migrations/ <app>/migrationsOLD/
-
For each that app run:
python manage.py makemigrations <app>
. -
Customize each new migration:
-
if you have a complex app, or more apps and related models between them, to avoid
CircularDependencyError
orValueError: Unhandled pending operations for models
:prepare second empty migration in
<app>
0002_initial2.py
(put there dependency toapp_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 frommigrationsOLD
and copy the code to new initial migration if needed. -
(optional for
--fake-initial
) Addinitial=True
to all new Migration classes (0002 too if was added). - Add
replaces
attribute in new Migration class. (like own custom asquashmigrations
). Put there all old migrations from<app>
-
-
Verify everything with
makemigrations
.assert "No changes detected"
-
Check if
migrate -l
show [x] everywhereassert 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 replaces
to 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.
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
If you still get this error when upgrading to Django 1.8 make sure to do makemigrations and migrate individually for third party apps that your apps are dependent on.