Opened 12 months ago
Closed 11 months 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 migratemy check ran before my column was added, raisingProgrammingError. - I verified
--skip-checksworks like a charm, but I don't want to impose cryptic failures on my fellow developers who expect plainmigrateto work. - I attempted to override the
migratecommand 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-checksstill 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 , 12 months ago
| Description: | modified (diff) |
|---|
comment:2 by , 12 months ago
| Cc: | added |
|---|---|
| Triage Stage: | Unreviewed → Accepted |
comment:3 by , 11 months 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 , 11 months 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 , 11 months ago
| Needs documentation: | set |
|---|
comment:7 by , 11 months ago
| Needs documentation: | unset |
|---|
comment:8 by , 11 months ago
| Triage Stage: | Accepted → Ready for checkin |
|---|
It looks like previously
requires_system_checkswas 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_checkstoFalseto 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.