Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 10 additions & 5 deletions django/utils/autoreload.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand All @@ -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

Expand Down Expand Up @@ -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
Expand All @@ -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()
Expand Down
43 changes: 18 additions & 25 deletions docs/topics/db/sql.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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`_
Expand Down Expand Up @@ -34,6 +36,19 @@ __ `executing custom SQL directly`_
Please read more about :ref:`SQL injection protection
<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 <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
Expand Down Expand Up @@ -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 <func-expressions>`.

__ https://www.postgresql.org/docs/current/functions-datetime.html

Passing parameters into ``raw()``
---------------------------------

Expand Down
12 changes: 12 additions & 0 deletions tests/utils_tests/test_autoreload.py
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
Loading