There is a common issue for beginners that they run migrations and nothing happens because they haven’t done the opt-in dance for migrations. Reddit - The heart of the internet for a recent example.
I get that migrations was opt-in back in the day, but isn’t it time we make it opt-out now?
For folks who are confused as to what this is about there’s a user “kankyo” making multiple replies on that thread proclaiming that migrations are “opt-in” due to the following behaviour (which I think needs correcting as it’s confusing):
For an app that has no migrations/ folder/module, the makemigrations & showmigrations commands can behave differently depending on whether the app name is passed in.
Here in this sample Django project I have a new app called no_migrations_folder missing migrations/. I create a new model “Foo” and run makemigrations:
Could someone explain exactly what they mean by migrations being opt-out or opt-in? I don’t think I’ve ever run across this problem in years of using Django so I’m not clear what it is I’m (unusually) doing right, or what changing the current behaviour would mean.
For further context, I believe kankyo and the OP here are either the same person or colleagues/friends, because both plug their project iommi at every opportunity on this forum and reddit. (Which doesn’t invalidate this suggestion in any way.)
shangxiao described it well I think. Django will only run migrations logic over an app if it has a migrations folder. That’s the opt in part. manage.py makemigrations app_name will create that folder, and then continue as normal.
either the same person
Yep, it’s the same person. Unfortunately I was too slow in grabbing my normal nick on reddit back in the day.
With contemporary Django usage migrations are opt-out via the managed=False flag.
Historically though Django never used to have migrations and you’d run database updates with a syncdb command – these were still controlled by the managed flag btw.
I believe the opt-in behaviour being described was a way to transition from syncdb to migrate… any Django apps missing a migrations/ module were considered to be “unmigrated”.
I think though now since syncdb is only usable via migrate --run-syncdb … and all mention of it has been completely removed from the documentation (from what I can tell)… it’s probably time to consider whether it’s worth removing this behaviour to make it more consistent. Someone who runs a legacy codebase from pre-migrations days may want to chime in with ways that this will break their systems.
Hmm, ok, no that’s not what I’m talking about. I’m only talking about the need for the migrations folder to be present for migrations to pick up models.
If you create an app by just mkdir project, create some models in models.py and add the app to INSTALLED_APPS, then running makemigrations will find nothing. You have to also create the empty migrations folder in the app.
This is what I mean by it being opt-in. You either opt in by making that folder manually, or (indirectly) by creating the app via startapp.
I forgot to mention: If we change this, there might need to be a new mechanism to opt out for those projects that for various reasons haven’t made the switch to migrations. I’m not sure about that detail.
I’m curious to know why this is described as “opt-in” when the standard application structure created by manage.py startapp includes the migrations/ folder and the documentation always points you to using startapp for a new application. In order to get into this situation you have to either go against the grain and manually create your app directory (in which case you might also mess up other things that Django can’t necessarily help you with) or use startapp but delete the migrations/ folder afterward.
I am with James here, I wouldn’t want to add to much code to Django here – especially no complex opt in / opt out logic. If you are using startapp everything seems to work as intended. If you are not using startapp you are supposed to know what Django requires as part of an application (same goes for setting up a project without startproject).
FWIW noone questions that this is a stumbling block for some people, but if startapp doesn’t have this problem then this feels a bit like giving people a linux CD to install on their PC just to have them go and download all packages manually and then complaining that the installer itself is missing.
Similar to Florian’s comment, it’s not that I’m saying people don’t stumble on this. I’m just saying that in order to stumble on this you have to be going against what the official tutorial and documentation teach you to do, and once someone’s doing that we’re talking about a different balancing act.
I think it might be worth adding something to the system check framework that warns if you have an app listed in INSTALLED_APPS which doesn’t have a migrations/ directory, but that’s about the most I’d want to do for this situation.
Good points. I have another alternative change that should be pretty small:
If you run makemigrations and there are apps in INSTALLED_APPS that don’t have migrations folder but has models, print a message like “App x has models, but no migrations. Run makemigrations x to start using migrations for this app”.
This should cleanly solve the problem and not interfere with users who don’t use migrations (since they won’t run makemigrations) and it will catch the people who stumble on this just when they stumble.
One thought is that we could combine that with the logic of the MIGRATION_MODULES setting.
Currently for None values:
When you supply None as a value for an app, Django will consider the app as an app without migrations regardless of an existing migrations submodule.
A system check that warned unless you’d set a None value in MIGRATION_MODULES would probably cover almost all cases[1]. (i.e. I’ve had to opt out of the warning for this app.)
I’m kind of ±0 on whether it’s worth it given the points James and Florian already made: folks have to go hunting for this problem.
Let’s put it in new-features and see whether people vote for it
folks have to go hunting for this problem
I agree from my perspective… that’s why I was initially confused… however a couple of people in that Reddit thread reported confusion. Perhaps the message isn’t getting across that startapp is how you make a new app or something, or maybe people just really like doing things the manual way.
It’s also worth mentioning that my recent usage of AI to create Django apps have not been consistent with creating a migrations directory and __init__.py.
Having something happen when you run makemigrations would, to me, be one less paper-cut to experience.
I’m in favour of making some kind of change here. We have explicit opt-outs via Meta.managed=False and MIGRATION_MODULES, let’s avoid this extra one based on whether a migrations directory exists.
It may be noted that since Git doesn’t track directories, one could get different behaviour in a repository depending on whether you had a fresh clone or one that had a migrations directory that had been previously emptied.
I frequently run into the “opt-in” behaviour. I use my simple project template for all kinds of mini projects: blog posts, testing regressions, and so on. It generates a project with a single app, but no models.py file.
If a project requires models, I make the models.py file for the already-created app, run ./manage.py makemigrations, scratch my head at the “No changes detected” message, then finally remember I need to run ./manage.py makemigrations example for the first run.
But even though I use a template, it doesn’t really make sense for it to include a models.py and empty migrations directory, and then I run into this oddity.
So, let’s say we put a warning up, how would that look? Maybe:
> ./manage.py makemigrations
WARNING: The app `foo` has models, but has not enabled migrations. Run `manage.py makemigrations foo` to enable migrations for this app.
No changes detected
Preferably the warning is in yellow or something.
I think this would 99% solve the issue, and I’m guessing it’s minimal code, and has no backwards compatibility concerns.
To be clear, this is basically what I meant by saying it should be in the system check framework. Having it show up in manage.py check and in the implicit invocation of check by other commands (including migrate) would make people aware of the issue with minimal changes to Django itself.
I don’t think that’s a good place though. Think about old projects that don’t use Django migrations. They would get this warning when starting the runserver for example. That would be rather annoying, and hit users that should not be affected. While putting it inside the makemigrations command itself has none of those drawbacks.