#31286: Database specific fields checks should be databases aware.
-------------------------------------+-------------------------------------
     Reporter:  Hongtao Ma           |                    Owner:  Hongtao
                                     |  Ma
         Type:  Bug                  |                   Status:  closed
    Component:  Database layer       |                  Version:  dev
  (models, ORM)                      |
     Severity:  Normal               |               Resolution:  fixed
     Keywords:                       |             Triage Stage:  Ready for
                                     |  checkin
    Has patch:  1                    |      Needs documentation:  0
  Needs tests:  1                    |  Patch needs improvement:  0
Easy pickings:  1                    |                    UI/UX:  0
-------------------------------------+-------------------------------------
Comment (by Tim Graham):

 I think there's a misunderstanding with your statement: "Database checks
 ​were never ran by default even prior to this patch." The only database
 check (designated by `@register(Tags.database)`) invokes
 
[https://github.com/django/django/blob/74a9c2711cd81708ae754c4d92432511efa96149/django/core/checks/database.py#L6-L14
 DatabaseValidation.check()]. This invokes, for example, MySQL's
 
[https://github.com/django/django/blob/74a9c2711cd81708ae754c4d92432511efa96149/django/db/backends/mysql/validation.py#L7-L36check
 on strict mode]. This check introspects the database and fails if it
 doesn't exist, so I understand that the test runner may need to skip these
 checks.

 Separately, the `DatabaseValidation` class also has a
 
[https://github.com/django/django/blob/74a9c2711cd81708ae754c4d92432511efa96149/django/db/backends/base/validation.py#L13-L32
 check_field() hook] which calls `DatabaseValidation.check_field_type()`
 (if implemented by the backend, e.g.
 
[https://github.com/django/django/blob/74a9c2711cd81708ae754c4d92432511efa96149/django/db/backends/mysql/validation.py#L38-L77
 MySQL's]). This method does static analysis (checking field attributes)
 and doesn't require the presence of a database. The
 
[https://github.com/django/django/blob/74a9c2711cd81708ae754c4d92432511efa96149/django/db/models/fields/__init__.py#L266-L274
 calling chain] is `Field.check()` ->
 `Field._check_backend_specific_checks()`.  These checks were run by
 `makemigrations` (and even by `runserver` which I'll get in to below)
 until 271fdab8b78af558238df51c64b4d1c8dd0792bb, which stopped iterating
 through all the databases (`django.db.connections`) and instead uses the
 list of `databases` aliases passed from (typically) the management
 command's `--database` argument (e.g. for
 
[https://github.com/django/django/blob/74a9c2711cd81708ae754c4d92432511efa96149/django/core/management/commands/migrate.py#L94
 migrate]).

 I believe it's useful to have field-specific checks run for all databases
 at the `makemigrations` stage rather than deferring them until `migrate`,
 at which point a potentially invalid migration has already been created.
 Besides `Field._check_backend_specific_checks()`, the other checks that
 rely on `databases` are therefore are currently deferred are:
 -
 
[https://github.com/django/django/blob/74a9c2711cd81708ae754c4d92432511efa96149/django/db/models/fields/__init__.py#L390-L424
 Field._check_db_default()]
 -
 
[https://github.com/django/django/blob/74a9c2711cd81708ae754c4d92432511efa96149/django/db/models/fields/__init__.py#L438-L458
 Field._check_db_comment()]
 -
 
[https://github.com/django/django/blob/74a9c2711cd81708ae754c4d92432511efa96149/django/db/models/base.py#L2345C9-L2422
 Model._check_long_column_names()]
 -
 
[https://github.com/django/django/blob/74a9c2711cd81708ae754c4d92432511efa96149/django/db/models/base.py#L2159-L2168
 Model._check_indexes()]
 -
 
[https://github.com/django/django/blob/74a9c2711cd81708ae754c4d92432511efa96149/django/db/models/base.py#L2440-L2449
 Model._check_constraints()]
 -
 
[https://github.com/django/django/blob/74a9c2711cd81708ae754c4d92432511efa96149/django/db/models/base.py#L1837-L1857
 Model._check_db_table_comment()]

 My proposal is to make `makemigrations` run these checks for all
 connections:
 {{{#!diff
 diff --git a/django/core/management/commands/makemigrations.py
 b/django/core/management/commands/makemigrations.py
 index 7f711ed7ae..5860c462c2 100644
 --- a/django/core/management/commands/makemigrations.py
 +++ b/django/core/management/commands/makemigrations.py
 @@ -102,6 +102,10 @@ class Command(BaseCommand):
      def log(self, msg):
          self.log_output.write(msg)

 +    def get_check_kwargs(self, options):
 +        kwargs = super().get_check_kwargs(options)
 +        return {**kwargs, "databases": connections}
 +
      @no_translations
      def handle(self, *app_labels, **options):
          self.written_files = []
 }}}

 While thinking through this, I noticed another surprising, perhaps
 inadvertent regression / behavior change. Currently, in a multi-db setup
 (I'll use the aliases 'default' and 'other' as an example), unless I
 missed a place that passes all db aliases to `check()`, the only way to
 get the pre-Django 3.1 behavior for `Model._check_long_column_names()`
 (which consults all database aliases to find the maximum allowed column
 name) would be to invoke: `check --database=default --database=other`.

 Essentially, #31055 started treating the bulleted list of checks above as
 "database checks" which was overly aggressive. They are really static
 checks that don't require the presence of a database. These checks used to
 be a lot more visible since they were even invoked by `runserver`
 (right?). So arguably `runserver` (and even `check`) could get the same
 `get_check_kwargs()` treatment proposed above, and then we may want to go
 back to
 
[https://github.com/django/django/commit/0b83c8cc4db95812f1e15ca19d78614e94cf38dd
 #diff-
 f10ce154e1f5bcdba92b54ebf8cb57357b404a3f03ab51ca85a031358f7372ecL66-L69
 excluding database tagged checks by default].
-- 
Ticket URL: <https://code.djangoproject.com/ticket/31286#comment:10>
Django <https://code.djangoproject.com/>
The Web framework for perfectionists with deadlines.

-- 
You received this message because you are subscribed to the Google Groups 
"Django updates" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to django-updates+unsubscr...@googlegroups.com.
To view this discussion visit 
https://groups.google.com/d/msgid/django-updates/010701995f953cf2-64d8d0d4-2083-42c2-9cda-020910b22958-000000%40eu-central-1.amazonses.com.

Reply via email to