Refs #26351 -- Added check hook to support database-related checks

Thanks Tim Graham and Shai Berger for the reviews.
This commit is contained in:
Claude Paroz 2016-03-19 12:15:09 +01:00
parent 5ac7c8f7ab
commit 0d3c616fbb
9 changed files with 85 additions and 9 deletions

View File

@ -10,6 +10,7 @@ from .registry import Tags, register, run_checks, tag_exists
# Import these to force registration of checks
import django.core.checks.caches # NOQA isort:skip
import django.core.checks.compatibility.django_1_8_0 # NOQA isort:skip
import django.core.checks.database # NOQA isort:skip
import django.core.checks.model_checks # NOQA isort:skip
import django.core.checks.security.base # NOQA isort:skip
import django.core.checks.security.csrf # NOQA isort:skip

View File

@ -0,0 +1,11 @@
from django.db import connections
from . import Tags, register
@register(Tags.database)
def check_database_backends(*args, **kwargs):
issues = []
for conn in connections.all():
issues.extend(conn.validation.check(**kwargs))
return issues

View File

@ -13,6 +13,7 @@ class Tags(object):
admin = 'admin'
caches = 'caches'
compatibility = 'compatibility'
database = 'database'
models = 'models'
security = 'security'
signals = 'signals'
@ -70,6 +71,11 @@ class CheckRegistry(object):
if tags is not None:
checks = [check for check in checks
if hasattr(check, 'tags') and set(check.tags) & set(tags)]
else:
# By default, 'database'-tagged checks are not run as they do more
# than mere static code analysis.
checks = [check for check in checks
if not hasattr(check, 'tags') or Tags.database not in check.tags]
for check in checks:
new_errors = check(app_configs=app_configs)

View File

@ -368,6 +368,9 @@ class BaseCommand(object):
translation.activate(saved_locale)
return output
def _run_checks(self, **kwargs):
return checks.run_checks(**kwargs)
def check(self, app_configs=None, tags=None, display_num_errors=False,
include_deployment_checks=False, fail_level=checks.ERROR):
"""
@ -376,7 +379,7 @@ class BaseCommand(object):
If there are only light messages (like warnings), they are printed to
stderr and no exception is raised.
"""
all_issues = checks.run_checks(
all_issues = self._run_checks(
app_configs=app_configs,
tags=tags,
include_deployment_checks=include_deployment_checks,

View File

@ -6,6 +6,7 @@ from collections import OrderedDict
from importlib import import_module
from django.apps import apps
from django.core.checks import Tags, run_checks
from django.core.management.base import BaseCommand, CommandError
from django.core.management.sql import (
emit_post_migrate_signal, emit_pre_migrate_signal,
@ -56,6 +57,11 @@ class Command(BaseCommand):
help='Creates tables for apps without migrations.',
)
def _run_checks(self, **kwargs):
issues = run_checks(tags=[Tags.database])
issues.extend(super(Command, self).check(**kwargs))
return issues
def handle(self, *args, **options):
self.verbosity = options['verbosity']

View File

@ -1,9 +1,12 @@
class BaseDatabaseValidation(object):
"""
This class encapsulates all backend-specific model validation.
This class encapsulates all backend-specific validation.
"""
def __init__(self, connection):
self.connection = connection
def check(self, **kwargs):
return []
def check_field(self, field, **kwargs):
return []

View File

@ -84,6 +84,14 @@ Django's system checks are organized using the following tags:
* ``templates``: Checks template related configuration.
* ``caches``: Checks cache related configuration.
* ``urls``: Checks URL configuration.
* ``database``: Checks database-related configuration issues. Database checks
are not run by default because they do more than static code analysis as
regular checks do. They are only run by the :djadmin:`migrate` command or if
you specify the ``database`` tag when calling the :djadmin:`check` command.
.. versionadded:: 1.10
The ``database`` tag was added.
Some checks may be registered with multiple tags.

View File

@ -120,17 +120,22 @@ The code below is equivalent to the code above::
.. _field-checking:
Field, model, and manager checks
--------------------------------
Field, model, manager, and database checks
------------------------------------------
In some cases, you won't need to register your check function -- you can
piggyback on an existing registration.
Fields, models, and model managers all implement a ``check()`` method that is
already registered with the check framework. If you want to add extra checks,
you can extend the implementation on the base class, perform any extra
checks you need, and append any messages to those generated by the base class.
It's recommended that you delegate each check to separate methods.
Fields, models, model managers, and database backends all implement a
``check()`` method that is already registered with the check framework. If you
want to add extra checks, you can extend the implementation on the base class,
perform any extra checks you need, and append any messages to those generated
by the base class. It's recommended that you delegate each check to separate
methods.
.. versionchanged:: 1.10
Database backend checks were added.
Consider an example where you are implementing a custom field named
``RangedIntegerField``. This field adds ``min`` and ``max`` arguments to the

View File

@ -0,0 +1,33 @@
import unittest
from django.core.checks import Tags, run_checks
from django.core.checks.registry import CheckRegistry
from django.db import connection
from django.test import TestCase, mock
class DatabaseCheckTests(TestCase):
@property
def func(self):
from django.core.checks.database import check_database_backends
return check_database_backends
def test_database_checks_not_run_by_default(self):
"""
`database` checks are only run when their tag is specified.
"""
def f1(**kwargs):
return [5]
registry = CheckRegistry()
registry.register(Tags.database)(f1)
errors = registry.run_checks()
self.assertEqual(errors, [])
errors2 = registry.run_checks(tags=[Tags.database])
self.assertEqual(errors2, [5])
def test_database_checks_called(self):
with mock.patch('django.db.backends.base.validation.BaseDatabaseValidation.check') as mocked_check:
run_checks(tags=[Tags.database])
self.assertTrue(mocked_check.called)