summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorClaude Paroz <claude@2xlibre.net>2016-03-19 12:15:09 +0100
committerClaude Paroz <claude@2xlibre.net>2016-04-08 20:28:00 +0200
commit0d3c616fbb2f49fa7ff6809e5a6777275352b35b (patch)
treee444996e32456a0ab64f65ee76e1d0c110f25ff5
parent5ac7c8f7ab2b2e1fec50abb14539a2eb520d1995 (diff)
Refs #26351 -- Added check hook to support database-related checks
Thanks Tim Graham and Shai Berger for the reviews.
-rw-r--r--django/core/checks/__init__.py1
-rw-r--r--django/core/checks/database.py11
-rw-r--r--django/core/checks/registry.py6
-rw-r--r--django/core/management/base.py5
-rw-r--r--django/core/management/commands/migrate.py6
-rw-r--r--django/db/backends/base/validation.py5
-rw-r--r--docs/ref/checks.txt8
-rw-r--r--docs/topics/checks.txt19
-rw-r--r--tests/check_framework/test_database.py33
9 files changed, 85 insertions, 9 deletions
diff --git a/django/core/checks/__init__.py b/django/core/checks/__init__.py
index cd21744327..657d0419f0 100644
--- a/django/core/checks/__init__.py
+++ b/django/core/checks/__init__.py
@@ -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
diff --git a/django/core/checks/database.py b/django/core/checks/database.py
new file mode 100644
index 0000000000..5778844fe9
--- /dev/null
+++ b/django/core/checks/database.py
@@ -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
diff --git a/django/core/checks/registry.py b/django/core/checks/registry.py
index 3e3d45fff8..292e0cb73b 100644
--- a/django/core/checks/registry.py
+++ b/django/core/checks/registry.py
@@ -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)
diff --git a/django/core/management/base.py b/django/core/management/base.py
index 1601d6d055..0a9037c1d1 100644
--- a/django/core/management/base.py
+++ b/django/core/management/base.py
@@ -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,
diff --git a/django/core/management/commands/migrate.py b/django/core/management/commands/migrate.py
index 84dbc59c65..72538f4586 100644
--- a/django/core/management/commands/migrate.py
+++ b/django/core/management/commands/migrate.py
@@ -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']
diff --git a/django/db/backends/base/validation.py b/django/db/backends/base/validation.py
index 888908c1aa..23761b4161 100644
--- a/django/db/backends/base/validation.py
+++ b/django/db/backends/base/validation.py
@@ -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 []
diff --git a/docs/ref/checks.txt b/docs/ref/checks.txt
index c82bd2bc98..a332df2071 100644
--- a/docs/ref/checks.txt
+++ b/docs/ref/checks.txt
@@ -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.
diff --git a/docs/topics/checks.txt b/docs/topics/checks.txt
index a019caa7c6..355e85714e 100644
--- a/docs/topics/checks.txt
+++ b/docs/topics/checks.txt
@@ -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
diff --git a/tests/check_framework/test_database.py b/tests/check_framework/test_database.py
new file mode 100644
index 0000000000..73edd6234c
--- /dev/null
+++ b/tests/check_framework/test_database.py
@@ -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)