Opened 5 weeks ago
Closed 11 days ago
#35920 closed Bug (fixed)
Migrate command runs system checks regardless of the value of requires_system_checks
Reported by: | Jacob Walls | Owned by: | Jacob Walls |
---|---|---|---|
Component: | Core (Management commands) | Version: | 5.1 |
Severity: | Normal | Keywords: | skip-checks |
Cc: | Simon Charette, Hasan Ramezani | Triage Stage: | Ready for checkin |
Has patch: | yes | Needs documentation: | no |
Needs tests: | no | Patch needs improvement: | no |
Easy pickings: | no | UI/UX: | no |
Description (last modified by )
Without --skip-checks=False
, the migrate
command runs system checks regardless of the value of requires_system_checks
, which is []
by default (itself a little misleading) but ultimately something I would like to be able to override at the project level. The fact that []
is not respected and not overridable seems like a bug, and AFAIK it's an asymmetry with other commands. (EDIT: turns out, runserver
behaves in a similar way, with a comment from 17 years ago, predating migrations, explaining there is a separate mechanism from the one that became requires_system_checks
that runs the system checks. I think we can improve the story here.)
- I wrote a system check following the documented example inside Model.check().
- I did this as part of a feature adding a new column to my model, and I queried this column in my check. I made the necessary migration.
- During
manage.py migrate
my check ran before my column was added, raisingProgrammingError
. - I verified
--skip-checks
works like a charm, but I don't want to impose cryptic failures on my fellow developers who expect plainmigrate
to work. - I attempted to override the
migrate
command in my project, like this:
/app/management/commands/migrate.py
from django.core.checks.registry import registry from django.core.management.commands.migrate import Command as MigrateCommand class Command(MigrateCommand): # Silence model checks that may depend on new columns. requires_system_checks = list(registry.tags_available() - {"models"})
- Result: no change:
ProgrammingError
- Then, I added this patch to Django. (
--skip-checks
still works; here, I have to avoid it being added again):-
django/core/management/commands/migrate.py
diff --git a/django/core/management/commands/migrate.py b/django/core/management/commands/migrate.py index fa420ee6e3..4000d76f3b 100644
a b class Command(BaseCommand): 19 19 help = ( 20 20 "Updates database schema. Manages both apps with migrations and those without." 21 21 ) 22 requires_system_checks = []22 requires_system_checks = "__all__" 23 23 24 24 def add_arguments(self, parser): 25 parser.add_argument(26 "--skip-checks",27 action="store_true",28 help="Skip system checks.",29 )30 25 parser.add_argument( 31 26 "app_label", 32 27 nargs="?", … … class Command(BaseCommand): 99 94 def handle(self, *args, **options): 100 95 database = options["database"] 101 96 if not options["skip_checks"]: 102 self.check( databases=[database])97 self.check(tags=self.requires_system_checks, databases=[database]) 103 98 104 99 self.verbosity = options["verbosity"] 105 100 self.interactive = options["interactive"]
-
- Result: check skipped as expected via my project-level override of the migrate command.
I suppose there's another question here about whether Tags.models
checks should run as part of the migrate command, or whether the change I made at the project level should be pulled into core, given that this is a reasonably realistic scenario for development? I can take that to the forum later. But at the moment, I'm just hoping to get the override working. Happy to PR this if welcome :-)
Change History (9)
comment:1 by , 5 weeks ago
Description: | modified (diff) |
---|
comment:2 by , 5 weeks ago
Cc: | added |
---|---|
Triage Stage: | Unreviewed → Accepted |
comment:3 by , 4 weeks ago
Has patch: | set |
---|
Update on this side point:
I suppose there's another question here about whether Tags.models checks should run as part of the migrate command, or whether the change I made at the project level should be pulled into core, given that this is a reasonably realistic scenario for development? I can take that to the forum later.
On second thought, that's a bad idea. Initially I missed the caveat that you need to guard your system check under if kwargs["database"]:
if you're going to make queries. Guarding like that was sufficient to prevent the failures.
comment:4 by , 4 weeks ago
Patch needs improvement: | set |
---|
I agree that it makes sense to adjust the migrate
and runserver
commands to allow for requires_system_checks
to be adjusted but the I believe the proposed solution fails to account for the fact that BaseCommand.execute
(which is .handle
's caller) already calls check
when requires_system_checks
is not empty.
What we want here is a single call to self.check
with the proper arguments being provided (databases
and display_num_errors
respectively). In the case of display_num_errors=Tue
it's pretty trivial as runserver.Command.check
can be overridden to call super().check()
but in the case of migrate.Command
it's a bit more tricky.
Maybe we could introduce a get_check_kwargs(**options) -> dict: Kwargs
method that simplifies all that?
-
django/core/management/base.py
diff --git a/django/core/management/base.py b/django/core/management/base.py index 6232b42bd4..ba38ae1748 100644
a b def execute(self, *args, **options): 450 450 self.stderr = OutputWrapper(options["stderr"]) 451 451 452 452 if self.requires_system_checks and not options["skip_checks"]: 453 if self.requires_system_checks == ALL_CHECKS: 454 self.check() 455 else: 456 self.check(tags=self.requires_system_checks) 453 check_kwargs = self.get_check_kwargs(options) 454 self.check(**check_kwargs) 457 455 if self.requires_migrations_checks: 458 456 self.check_migrations() 459 457 output = self.handle(*args, **options) … … def execute(self, *args, **options): 468 466 self.stdout.write(output) 469 467 return output 470 468 469 def get_check_kwargs(self, options): 470 if self.requires_system_checks == ALL_CHECKS: 471 return {} 472 return {"tags": self.requires_system_checks} 473 471 474 def check( 472 475 self, 473 476 app_configs=None,
comment:6 by , 2 weeks ago
Needs documentation: | set |
---|
comment:7 by , 2 weeks ago
Needs documentation: | unset |
---|
comment:8 by , 11 days ago
Triage Stage: | Accepted → Ready for checkin |
---|
It looks like previously
requires_system_checks
was a boolean.It was updated to be able to specify specific checks: c60524c658f197f645b638f9bcc553103bfe2630 (#31546)
Prior to that commit there was no customizing what type of checks are run and it was all or nothing (unless overwritten by the command)
Previously 0b83c8cc4db95812f1e15ca19d78614e94cf38dd (#31055) implemented database specific checks and so marked
requires_system_checks
toFalse
to run make sure only the database specific checks were run for migrateSo I think you're right that we should do an update here to make this work well together.