checks.txt 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282
  1. ======================
  2. System check framework
  3. ======================
  4. .. module:: django.core.checks
  5. The system check framework is a set of static checks for validating Django
  6. projects. It detects common problems and provides hints for how to fix them.
  7. The framework is extensible so you can easily add your own checks.
  8. Checks can be triggered explicitly via the :djadmin:`check` command. Checks are
  9. triggered implicitly before most commands, including :djadmin:`runserver` and
  10. :djadmin:`migrate`. For performance reasons, checks are not run as part of the
  11. WSGI stack that is used in deployment. If you need to run system checks on your
  12. deployment server, trigger them explicitly using :djadmin:`check`.
  13. Serious errors will prevent Django commands (such as :djadmin:`runserver`) from
  14. running at all. Minor problems are reported to the console. If you have inspected
  15. the cause of a warning and are happy to ignore it, you can hide specific warnings
  16. using the :setting:`SILENCED_SYSTEM_CHECKS` setting in your project settings file.
  17. A full list of all checks that can be raised by Django can be found in the
  18. :doc:`System check reference </ref/checks>`.
  19. Writing your own checks
  20. =======================
  21. The framework is flexible and allows you to write functions that perform
  22. any other kind of check you may require. The following is an example stub
  23. check function::
  24. from django.core.checks import Error, register
  25. @register()
  26. def example_check(app_configs, **kwargs):
  27. errors = []
  28. # ... your check logic here
  29. if check_failed:
  30. errors.append(
  31. Error(
  32. "an error",
  33. hint="A hint.",
  34. obj=checked_object,
  35. id="myapp.E001",
  36. )
  37. )
  38. return errors
  39. The check function *must* accept an ``app_configs`` argument; this argument is
  40. the list of applications that should be inspected. If ``None``, the check must
  41. be run on *all* installed apps in the project.
  42. The check will receive a ``databases`` keyword argument. This is a list of
  43. database aliases whose connections may be used to inspect database level
  44. configuration. If ``databases`` is ``None``, the check must not use any
  45. database connections.
  46. The ``**kwargs`` argument is required for future expansion.
  47. Messages
  48. --------
  49. The function must return a list of messages. If no problems are found as a result
  50. of the check, the check function must return an empty list.
  51. The warnings and errors raised by the check method must be instances of
  52. :class:`~django.core.checks.CheckMessage`. An instance of
  53. :class:`~django.core.checks.CheckMessage` encapsulates a single reportable
  54. error or warning. It also provides context and hints applicable to the
  55. message, and a unique identifier that is used for filtering purposes.
  56. The concept is very similar to messages from the :doc:`message framework
  57. </ref/contrib/messages>` or the :doc:`logging framework </topics/logging>`.
  58. Messages are tagged with a ``level`` indicating the severity of the message.
  59. There are also shortcuts to make creating messages with common levels easier.
  60. When using these classes you can omit the ``level`` argument because it is
  61. implied by the class name.
  62. * :class:`Debug`
  63. * :class:`Info`
  64. * :class:`Warning`
  65. * :class:`Error`
  66. * :class:`Critical`
  67. .. _registering-labeling-checks:
  68. Registering and labeling checks
  69. -------------------------------
  70. Lastly, your check function must be registered explicitly with system check
  71. registry. Checks should be registered in a file that's loaded when your
  72. application is loaded; for example, in the :meth:`AppConfig.ready()
  73. <django.apps.AppConfig.ready>` method.
  74. .. function:: register(*tags)(function)
  75. You can pass as many tags to ``register`` as you want in order to label your
  76. check. Tagging checks is useful since it allows you to run only a certain
  77. group of checks. For example, to register a compatibility check, you would
  78. make the following call::
  79. from django.core.checks import register, Tags
  80. @register(Tags.compatibility)
  81. def my_check(app_configs, **kwargs):
  82. # ... perform compatibility checks and collect errors
  83. return errors
  84. You can register "deployment checks" that are only relevant to a production
  85. settings file like this::
  86. @register(Tags.security, deploy=True)
  87. def my_check(app_configs, **kwargs): ...
  88. These checks will only be run if the :option:`check --deploy` option is used.
  89. You can also use ``register`` as a function rather than a decorator by
  90. passing a callable object (usually a function) as the first argument
  91. to ``register``.
  92. The code below is equivalent to the code above::
  93. def my_check(app_configs, **kwargs): ...
  94. register(my_check, Tags.security, deploy=True)
  95. .. _field-checking:
  96. Field, model, manager, template engine, and database checks
  97. -----------------------------------------------------------
  98. In some cases, you won't need to register your check function -- you can
  99. piggyback on an existing registration.
  100. Fields, models, model managers, template engines, and database backends all
  101. implement a ``check()`` method that is already registered with the check
  102. framework. If you want to add extra checks, you can extend the implementation
  103. on the base class, perform any extra checks you need, and append any messages
  104. to those generated by the base class. It's recommended that you delegate each
  105. check to separate methods.
  106. Consider an example where you are implementing a custom field named
  107. ``RangedIntegerField``. This field adds ``min`` and ``max`` arguments to the
  108. constructor of ``IntegerField``. You may want to add a check to ensure that users
  109. provide a min value that is less than or equal to the max value. The following
  110. code snippet shows how you can implement this check::
  111. from django.core import checks
  112. from django.db import models
  113. class RangedIntegerField(models.IntegerField):
  114. def __init__(self, min=None, max=None, **kwargs):
  115. super().__init__(**kwargs)
  116. self.min = min
  117. self.max = max
  118. def check(self, **kwargs):
  119. # Call the superclass
  120. errors = super().check(**kwargs)
  121. # Do some custom checks and add messages to `errors`:
  122. errors.extend(self._check_min_max_values(**kwargs))
  123. # Return all errors and warnings
  124. return errors
  125. def _check_min_max_values(self, **kwargs):
  126. if self.min is not None and self.max is not None and self.min > self.max:
  127. return [
  128. checks.Error(
  129. "min greater than max.",
  130. hint="Decrease min or increase max.",
  131. obj=self,
  132. id="myapp.E001",
  133. )
  134. ]
  135. # When no error, return an empty list
  136. return []
  137. If you wanted to add checks to a model manager, you would take the same
  138. approach on your subclass of :class:`~django.db.models.Manager`.
  139. If you want to add a check to a model class, the approach is *almost* the same:
  140. the only difference is that the check is a classmethod, not an instance method::
  141. class MyModel(models.Model):
  142. @classmethod
  143. def check(cls, **kwargs):
  144. errors = super().check(**kwargs)
  145. # ... your own checks ...
  146. return errors
  147. .. versionchanged:: 5.1
  148. In older versions, template engines didn't implement a ``check()`` method.
  149. Writing tests
  150. -------------
  151. Messages are comparable. That allows you to easily write tests::
  152. from django.core.checks import Error
  153. errors = checked_object.check()
  154. expected_errors = [
  155. Error(
  156. "an error",
  157. hint="A hint.",
  158. obj=checked_object,
  159. id="myapp.E001",
  160. )
  161. ]
  162. self.assertEqual(errors, expected_errors)
  163. Writing integration tests
  164. ~~~~~~~~~~~~~~~~~~~~~~~~~
  165. Given the need to register certain checks when the application loads, it can be
  166. useful to test their integration within the system checks framework. This can
  167. be accomplished by using the :func:`~django.core.management.call_command`
  168. function.
  169. For example, this test demonstrates that the :setting:`SITE_ID` setting must be
  170. an integer, a built-in :ref:`check from the sites framework
  171. <sites-system-checks>`::
  172. from django.core.management import call_command
  173. from django.core.management.base import SystemCheckError
  174. from django.test import SimpleTestCase, modify_settings, override_settings
  175. class SystemCheckIntegrationTest(SimpleTestCase):
  176. @override_settings(SITE_ID="non_integer")
  177. @modify_settings(INSTALLED_APPS={"prepend": "django.contrib.sites"})
  178. def test_non_integer_site_id(self):
  179. message = "(sites.E101) The SITE_ID setting must be an integer."
  180. with self.assertRaisesMessage(SystemCheckError, message):
  181. call_command("check")
  182. Consider the following check which issues a warning on deployment if a custom
  183. setting named ``ENABLE_ANALYTICS`` is not set to ``True``::
  184. from django.conf import settings
  185. from django.core.checks import Warning, register
  186. @register("myapp", deploy=True)
  187. def check_enable_analytics_is_true_on_deploy(app_configs, **kwargs):
  188. errors = []
  189. if getattr(settings, "ENABLE_ANALYTICS", None) is not True:
  190. errors.append(
  191. Warning(
  192. "The ENABLE_ANALYTICS setting should be set to True in deployment.",
  193. id="myapp.W001",
  194. )
  195. )
  196. return errors
  197. Given that this check will not raise a ``SystemCheckError``, the presence of
  198. the warning message in the ``stderr`` output can be asserted like so::
  199. from io import StringIO
  200. from django.core.management import call_command
  201. from django.test import SimpleTestCase, override_settings
  202. class EnableAnalyticsDeploymentCheckTest(SimpleTestCase):
  203. @override_settings(ENABLE_ANALYTICS=None)
  204. def test_when_set_to_none(self):
  205. stderr = StringIO()
  206. call_command("check", "-t", "myapp", "--deploy", stderr=stderr)
  207. message = (
  208. "(myapp.W001) The ENABLE_ANALYTICS setting should be set "
  209. "to True in deployment."
  210. )
  211. self.assertIn(message, stderr.getvalue())