diff --git a/django/utils/autoreload.py b/django/utils/autoreload.py index 99812979d73c..13019f7214b7 100644 --- a/django/utils/autoreload.py +++ b/django/utils/autoreload.py @@ -33,6 +33,8 @@ # file paths to allow watching them in the future. _error_files = [] _exception = None +# Exception raised while loading the URLConf. +_url_module_exception = None try: import termios @@ -62,7 +64,7 @@ def wrapper(*args, **kwargs): global _exception try: fn(*args, **kwargs) - except Exception: + except Exception as e: _exception = sys.exc_info() et, ev, tb = _exception @@ -75,8 +77,10 @@ def wrapper(*args, **kwargs): if filename not in _error_files: _error_files.append(filename) + if _url_module_exception is not None: + raise e from _url_module_exception - raise + raise e return wrapper @@ -339,6 +343,7 @@ def wait_for_apps_ready(self, app_reg, django_main_thread): return False def run(self, django_main_thread): + global _url_module_exception logger.debug("Waiting for apps ready_event.") self.wait_for_apps_ready(apps, django_main_thread) from django.urls import get_resolver @@ -347,10 +352,10 @@ def run(self, django_main_thread): # reloader starts by accessing the urlconf_module property. try: get_resolver().urlconf_module - except Exception: + except Exception as e: # Loading the urlconf can result in errors during development. - # If this occurs then swallow the error and continue. - pass + # If this occurs then store the error and continue. + _url_module_exception = e logger.debug("Apps ready_event triggered. Sending autoreload_started signal.") autoreload_started.send(sender=self) self.run_loop() diff --git a/docs/topics/db/sql.txt b/docs/topics/db/sql.txt index bd34df8bb73a..da26f433472b 100644 --- a/docs/topics/db/sql.txt +++ b/docs/topics/db/sql.txt @@ -4,9 +4,11 @@ Performing raw SQL queries .. currentmodule:: django.db.models -Django gives you two ways of performing raw SQL queries: you can use -:meth:`Manager.raw` to `perform raw queries and return model instances`__, or -you can avoid the model layer entirely and `execute custom SQL directly`__. +Django gives you three ways of performing raw SQL queries: you can embed raw +SQL fragments into ORM queries using +:class:`~django.db.models.expressions.RawSQL` (see :ref:`raw-sql-fragments`), +use :meth:`Manager.raw` to `perform raw queries and return model instances`__, +or avoid the model layer entirely and `execute custom SQL directly`__. __ `performing raw queries`_ __ `executing custom SQL directly`_ @@ -34,6 +36,19 @@ __ `executing custom SQL directly`_ Please read more about :ref:`SQL injection protection `. +.. _raw-sql-fragments: + +Raw SQL fragments +================= + +In some cases, you may need to embed raw SQL fragments directly into ORM +queries — for example, in :meth:`~django.db.models.query.QuerySet.annotate` or +:meth:`~django.db.models.query.QuerySet.filter` calls. Use :ref:`Func() +expressions ` for calling database functions across +backends, or +:class:`~django.db.models.expressions.RawSQL` for arbitrary parameterized SQL +fragments. + .. _executing-raw-queries: Performing raw queries @@ -195,28 +210,6 @@ must always be included in a raw query. A :class:`~django.core.exceptions.FieldDoesNotExist` exception will be raised if you forget to include the primary key. -Adding annotations ------------------- - -You can also execute queries containing fields that aren't defined on the -model. For example, we could use `PostgreSQL's age() function`__ to get a list -of people with their ages calculated by the database: - -.. code-block:: pycon - - >>> people = Person.objects.raw("SELECT *, age(birth_date) AS age FROM myapp_person") - >>> for p in people: - ... print("%s is %s." % (p.first_name, p.age)) - ... - John is 37. - Jane is 42. - ... - -You can often avoid using raw SQL to compute annotations by instead using a -:ref:`Func() expression `. - -__ https://www.postgresql.org/docs/current/functions-datetime.html - Passing parameters into ``raw()`` --------------------------------- diff --git a/tests/utils_tests/test_autoreload.py b/tests/utils_tests/test_autoreload.py index c9e6443c6fbf..2033728da809 100644 --- a/tests/utils_tests/test_autoreload.py +++ b/tests/utils_tests/test_autoreload.py @@ -434,6 +434,18 @@ def test_mutates_error_files(self): autoreload._exception = None self.assertEqual(mocked_error_files.append.call_count, 1) + def test_urlconf_exception_is_used_as_cause(self): + urlconf_exc = ValueError("Error") + fake_method = mock.MagicMock(side_effect=RuntimeError()) + wrapped = autoreload.check_errors(fake_method) + with mock.patch.object(autoreload, "_url_module_exception", urlconf_exc): + try: + with self.assertRaises(RuntimeError) as cm: + wrapped() + finally: + autoreload._exception = None + self.assertIs(cm.exception.__cause__, urlconf_exc) + class TestRaiseLastException(SimpleTestCase): @mock.patch("django.utils.autoreload._exception", None)