+ {% if user.is_anonymous %}
+ {% include "admin/color_theme_toggle.html" %}
+ {% endif %}
{% endblock %}
We use this approach to teach you how to override templates. In an actual
diff --git a/docs/misc/design-philosophies.txt b/docs/misc/design-philosophies.txt
index 207685d5569..95ba3d86cd3 100644
--- a/docs/misc/design-philosophies.txt
+++ b/docs/misc/design-philosophies.txt
@@ -254,10 +254,6 @@ enough programming-esque functionality, such as branching and looping, that is
essential for making presentation-related decisions. The :ref:`Django Template
Language (DTL) ` aims to avoid advanced logic.
-The Django template system recognizes that templates are most often written by
-*designers*, not *programmers*, and therefore should not assume Python
-knowledge.
-
Safety and security
-------------------
diff --git a/docs/ref/contrib/admin/index.txt b/docs/ref/contrib/admin/index.txt
index fef582f2df4..25e98c3b949 100644
--- a/docs/ref/contrib/admin/index.txt
+++ b/docs/ref/contrib/admin/index.txt
@@ -751,8 +751,7 @@ subclass::
like::
@admin.display(ordering="-first_name")
- def colored_first_name(self):
- ...
+ def colored_first_name(self): ...
The ``ordering`` argument supports query lookups to sort by values on
related models. This example includes an "author first name" column in
@@ -3132,8 +3131,7 @@ returns a site instance.
from django.contrib import admin
- class MyAdminSite(admin.AdminSite):
- ...
+ class MyAdminSite(admin.AdminSite): ...
.. code-block:: python
:caption: ``myproject/apps.py``
@@ -3471,5 +3469,4 @@ The ``staff_member_required`` decorator
@staff_member_required
- def my_view(request):
- ...
+ def my_view(request): ...
diff --git a/docs/ref/contrib/auth.txt b/docs/ref/contrib/auth.txt
index 482eed231b4..953c31a6ae5 100644
--- a/docs/ref/contrib/auth.txt
+++ b/docs/ref/contrib/auth.txt
@@ -188,6 +188,12 @@ Methods
You may need this if authentication for your application takes place
against an existing external source such as an LDAP directory.
+ .. admonition:: Password reset restriction
+
+ Users having an unusable password will not able to request a
+ password reset email via
+ :class:`~django.contrib.auth.views.PasswordResetView`.
+
.. method:: has_usable_password()
Returns ``False`` if
diff --git a/docs/ref/contrib/gis/feeds.txt b/docs/ref/contrib/gis/feeds.txt
index 9ae9d4f03a4..3f17a685741 100644
--- a/docs/ref/contrib/gis/feeds.txt
+++ b/docs/ref/contrib/gis/feeds.txt
@@ -40,18 +40,14 @@ API Reference
item_geometry = ...
# Also a function with no arguments
- def geometry(self):
- ...
+ def geometry(self): ...
- def item_geometry(self):
- ...
+ def item_geometry(self): ...
# And as a function with a single argument
- def geometry(self, obj):
- ...
+ def geometry(self, obj): ...
- def item_geometry(self, item):
- ...
+ def item_geometry(self, item): ...
.. method:: geometry(obj)
diff --git a/docs/ref/contrib/gis/tutorial.txt b/docs/ref/contrib/gis/tutorial.txt
index d1c4cdb8e49..eb62df56a86 100644
--- a/docs/ref/contrib/gis/tutorial.txt
+++ b/docs/ref/contrib/gis/tutorial.txt
@@ -252,7 +252,7 @@ model:
This command should produce the following output:
-.. console::
+.. code-block:: sql
BEGIN;
--
@@ -686,12 +686,13 @@ __ https://spatialreference.org/ref/epsg/32140/
.. code-block:: pycon
- from django.db import connection
- # or if you're querying a non-default database:
- from django.db import connections
- connection = connections['your_gis_db_alias']
-
- City.objects.raw('SELECT id, name, %s as point from myapp_city' % (connection.ops.select % 'point'))
+ >>> from django.db import connection
+ >>> # or if you're querying a non-default database:
+ >>> from django.db import connections
+ >>> connection = connections["your_gis_db_alias"]
+ >>> City.objects.raw(
+ ... "SELECT id, name, %s as point from myapp_city" % (connection.ops.select % "point")
+ ... )
You should only use raw queries when you know exactly what you're doing.
diff --git a/docs/ref/databases.txt b/docs/ref/databases.txt
index f859f513773..92b1f40898c 100644
--- a/docs/ref/databases.txt
+++ b/docs/ref/databases.txt
@@ -164,8 +164,14 @@ password from the `password file`_, you must specify them in the
localhost:5432:NAME:USER:PASSWORD
+The PostgreSQL backend passes the content of :setting:`OPTIONS` as keyword
+arguments to the connection constructor, allowing for more advanced control
+of driver behavior. All available `parameters`_ are described in detail in the
+PostgreSQL documentation.
+
.. _connection service file: https://www.postgresql.org/docs/current/libpq-pgservice.html
.. _password file: https://www.postgresql.org/docs/current/libpq-pgpass.html
+.. _parameters: https://www.postgresql.org/docs/current/libpq-connect.html#LIBPQ-PARAMKEYWORDS
.. warning::
diff --git a/docs/ref/django-admin.txt b/docs/ref/django-admin.txt
index f533aa11dea..59c731113cb 100644
--- a/docs/ref/django-admin.txt
+++ b/docs/ref/django-admin.txt
@@ -1804,9 +1804,9 @@ allows for the following options by default:
.. django-admin-option:: --pythonpath PYTHONPATH
-Adds the given filesystem path to the Python `import search path`_. If this
-isn't provided, ``django-admin`` will use the :envvar:`PYTHONPATH` environment
-variable.
+Adds the given filesystem path to the Python :py:data:`sys.path` module
+attribute. If this isn't provided, ``django-admin`` will use the
+:envvar:`PYTHONPATH` environment variable.
This option is unnecessary in ``manage.py``, because it takes care of setting
the Python path for you.
@@ -1817,8 +1817,6 @@ Example usage:
django-admin migrate --pythonpath='/home/djangoprojects/myproject'
-.. _import search path: https://diveinto.org/python3/your-first-python-program.html#importsearchpath
-
.. django-admin-option:: --settings SETTINGS
Specifies the settings module to use. The settings module should be in Python
diff --git a/docs/ref/forms/api.txt b/docs/ref/forms/api.txt
index 7bec9b120b9..28cd452c4e8 100644
--- a/docs/ref/forms/api.txt
+++ b/docs/ref/forms/api.txt
@@ -909,7 +909,7 @@ It's possible to customize that character, or omit it entirely, using the
>>> f = ContactForm(auto_id="id_for_%s", label_suffix=" ->")
>>> print(f)
-
+
diff --git a/docs/ref/logging.txt b/docs/ref/logging.txt
index b11fb752f79..fa07422cd51 100644
--- a/docs/ref/logging.txt
+++ b/docs/ref/logging.txt
@@ -204,6 +204,39 @@ all database queries.
Support for logging transaction management queries (``BEGIN``, ``COMMIT``,
and ``ROLLBACK``) was added.
+.. _django-utils-autoreloader-logger:
+
+``django.utils.autoreload``
+~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Log messages related to automatic code reloading during the execution of the
+Django development server. This logger generates an ``INFO`` message upon
+detecting a modification in a source code file and may produce ``WARNING``
+messages during filesystem inspection and event subscription processes.
+
+.. _django-contrib-gis-logger:
+
+``django.contrib.gis``
+~~~~~~~~~~~~~~~~~~~~~~
+
+Log messages related to :doc:`contrib/gis/index` at various points: during the
+loading of external GeoSpatial libraries (GEOS, GDAL, etc.) and when reporting
+errors. Each ``ERROR`` log record includes the caught exception and relevant
+contextual data.
+
+.. _django-dispatch-logger:
+
+``django.dispatch``
+~~~~~~~~~~~~~~~~~~~
+
+This logger is used in :doc:`signals`, specifically within the
+:mod:`~django.dispatch.Signal` class, to report issues when dispatching a
+signal to a connected receiver. The ``ERROR`` log record includes the caught
+exception as ``exc_info`` and adds the following extra context:
+
+* ``receiver``: The name of the receiver.
+* ``err``: The exception that occurred when calling the receiver.
+
.. _django-security-logger:
``django.security.*``
diff --git a/docs/ref/models/constraints.txt b/docs/ref/models/constraints.txt
index efe63a8ac18..cc308cedf22 100644
--- a/docs/ref/models/constraints.txt
+++ b/docs/ref/models/constraints.txt
@@ -229,7 +229,8 @@ For example::
will allow filtering on ``room`` and ``date``, also selecting ``full_name``,
while fetching data only from the index.
-``include`` is supported only on PostgreSQL.
+Unique constraints with non-key columns are ignored for databases besides
+PostgreSQL.
Non-key columns have the same database restrictions as :attr:`Index.include`.
@@ -272,7 +273,8 @@ For example::
creates a unique constraint that only allows one row to store a ``NULL`` value
in the ``ordering`` column.
-``nulls_distinct`` is ignored for databases besides PostgreSQL 15+.
+Unique constraints with ``nulls_distinct`` are ignored for databases besides
+PostgreSQL 15+.
``violation_error_code``
------------------------
diff --git a/docs/ref/models/expressions.txt b/docs/ref/models/expressions.txt
index a5edfa550a5..63847a2328e 100644
--- a/docs/ref/models/expressions.txt
+++ b/docs/ref/models/expressions.txt
@@ -92,7 +92,7 @@ Some examples
>>> Company.objects.order_by("name__length")
# Boolean expression can be used directly in filters.
- >>> from django.db.models import Exists
+ >>> from django.db.models import Exists, OuterRef
>>> Company.objects.filter(
... Exists(Employee.objects.filter(company=OuterRef("pk"), salary__gt=10))
... )
@@ -757,9 +757,7 @@ should avoid them if possible.
your SQL with user-provided data.
You also must not quote placeholders in the SQL string. This example is
- vulnerable to SQL injection because of the quotes around ``%s``:
-
- .. code-block:: pycon
+ vulnerable to SQL injection because of the quotes around ``%s``::
RawSQL("select col from sometable where othercol = '%s'") # unsafe!
diff --git a/docs/ref/request-response.txt b/docs/ref/request-response.txt
index 3148f8ace52..49ee126acac 100644
--- a/docs/ref/request-response.txt
+++ b/docs/ref/request-response.txt
@@ -1090,13 +1090,12 @@ Custom response classes
~~~~~~~~~~~~~~~~~~~~~~~
If you find yourself needing a response class that Django doesn't provide, you
-can create it with the help of :py:class:`http.HTTPStatus`. For example:
-
-.. code-block:: pycon
+can create it with the help of :py:class:`http.HTTPStatus`. For example::
from http import HTTPStatus
from django.http import HttpResponse
+
class HttpResponseNoContent(HttpResponse):
status_code = HTTPStatus.NO_CONTENT
diff --git a/docs/ref/settings.txt b/docs/ref/settings.txt
index 28d72e356e4..a7b789c3f69 100644
--- a/docs/ref/settings.txt
+++ b/docs/ref/settings.txt
@@ -273,8 +273,8 @@ See :doc:`/topics/cache`.
Default: ``600``
-The default number of seconds to cache a page for the :ref:`cache middleware
-`.
+The default integer number of seconds to cache a page for the
+:ref:`cache middleware `.
See :doc:`/topics/cache`.
@@ -422,8 +422,7 @@ A dotted path to the view function to be used when an incoming request is
rejected by the :doc:`CSRF protection `. The function should have
this signature::
- def csrf_failure(request, reason=""):
- ...
+ def csrf_failure(request, reason=""): ...
where ``reason`` is a short message (intended for developers or logging, not
for end users) indicating the reason the request was rejected. It should return
@@ -663,8 +662,10 @@ A string representing the time zone for this database connection or ``None``.
This inner option of the :setting:`DATABASES` setting accepts the same values
as the general :setting:`TIME_ZONE` setting.
-When :setting:`USE_TZ` is ``True`` and this option is set, reading datetimes
-from the database returns aware datetimes in this time zone instead of UTC.
+When :setting:`USE_TZ` is ``True``, reading datetimes from the database
+returns aware datetimes with the timezone set to this option's value if not
+``None``, or to UTC otherwise.
+
When :setting:`USE_TZ` is ``False``, it is an error to set this option.
* If the database backend doesn't support time zones (e.g. SQLite, MySQL,
@@ -687,13 +688,18 @@ When :setting:`USE_TZ` is ``False``, it is an error to set this option.
third-party systems connect to the same database and expect to find
datetimes in local time, then you must set this option.
-* If the database backend supports time zones (e.g. PostgreSQL), the
- ``TIME_ZONE`` option is very rarely needed. It can be changed at any time;
- the database takes care of converting datetimes to the desired time zone.
+* If the database backend supports time zones (e.g., PostgreSQL), then the
+ database connection's time zone is set to this value.
- Setting the time zone of the database connection may be useful for running
- raw SQL queries involving date/time functions provided by the database, such
- as ``date_trunc``, because their results depend on the time zone.
+ Although setting the ``TIME_ZONE`` option is very rarely needed, there are
+ situations where it becomes necessary. Specifically, it's recommended to
+ match the general :setting:`TIME_ZONE` setting when dealing with raw queries
+ involving date/time functions like PostgreSQL's ``date_trunc()`` or
+ ``generate_series()``, especially when generating time-based series that
+ transition daylight savings.
+
+ This value can be changed at any time, the database will handle the
+ conversion of datetimes to the configured time zone.
However, this has a downside: receiving all datetimes in local time makes
datetime arithmetic more tricky — you must account for possible offset
@@ -1354,9 +1360,12 @@ specify a particular storage system. See :doc:`/topics/files`.
Default: ``'webmaster@localhost'``
-Default email address to use for various automated correspondence from the
-site manager(s). This doesn't include error messages sent to :setting:`ADMINS`
-and :setting:`MANAGERS`; for that, see :setting:`SERVER_EMAIL`.
+Default email address for automated correspondence from the site manager(s).
+This address is used in the ``From:`` header of outgoing emails and can take
+any format valid in the chosen email sending protocol.
+
+This doesn't affect error messages sent to :setting:`ADMINS` and
+:setting:`MANAGERS`. See :setting:`SERVER_EMAIL` for that.
.. setting:: DEFAULT_INDEX_TABLESPACE
@@ -1398,7 +1407,7 @@ This is only used if ``CommonMiddleware`` is installed (see
Default: ``'``:class:`django.core.mail.backends.smtp.EmailBackend`\ ``'``
The backend to use for sending emails. For the list of available backends see
-:doc:`/topics/email`.
+:ref:`topic-email-backends`.
.. setting:: EMAIL_FILE_PATH
@@ -2546,7 +2555,9 @@ example, to define a YAML serializer, use::
Default: ``'root@localhost'``
The email address that error messages come from, such as those sent to
-:setting:`ADMINS` and :setting:`MANAGERS`.
+:setting:`ADMINS` and :setting:`MANAGERS`. This address is used in the
+``From:`` header and can take any format valid in the chosen email sending
+protocol.
.. admonition:: Why are my emails sent from a different address?
diff --git a/docs/ref/templates/builtins.txt b/docs/ref/templates/builtins.txt
index 536f9259bc9..8a2b7141353 100644
--- a/docs/ref/templates/builtins.txt
+++ b/docs/ref/templates/builtins.txt
@@ -524,11 +524,10 @@ Use of both ``and`` and ``or`` clauses within the same tag is allowed, with
{% if athlete_list and coach_list or cheerleader_list %}
-will be interpreted like:
+will be interpreted like::
-.. code-block:: pycon
-
- if (athlete_list and coach_list) or cheerleader_list
+ if (athlete_list and coach_list) or cheerleader_list:
+ ...
Use of actual parentheses in the :ttag:`if` tag is invalid syntax. If you need
them to indicate precedence, you should use nested :ttag:`if` tags.
@@ -2441,8 +2440,8 @@ individual elements of the sequence.
Returns a slice of the list.
-Uses the same syntax as Python's list slicing. See
-https://diveinto.org/python3/native-datatypes.html#slicinglists for an
+Uses the same syntax as Python's list slicing. See the `Python documentation
+`_ for an
introduction.
Example:
diff --git a/docs/ref/utils.txt b/docs/ref/utils.txt
index acbe5d51a62..84e33d5d410 100644
--- a/docs/ref/utils.txt
+++ b/docs/ref/utils.txt
@@ -469,8 +469,7 @@ https://web.archive.org/web/20110718035220/http://diveintomark.org/archives/2004
class Person(models.Model):
@cached_property
- def friends(self):
- ...
+ def friends(self): ...
Note that as the method is now a property, in Python code it will need to
be accessed appropriately::
@@ -552,8 +551,7 @@ https://web.archive.org/web/20110718035220/http://diveintomark.org/archives/2004
# Or more succinctly:
@keep_lazy(str)
- def fancy_utility_function(s, *args, **kwargs):
- ...
+ def fancy_utility_function(s, *args, **kwargs): ...
The ``keep_lazy()`` decorator takes a number of extra arguments (``*args``)
specifying the type(s) that the original function can return. A common
@@ -578,14 +576,12 @@ https://web.archive.org/web/20110718035220/http://diveintomark.org/archives/2004
# Our previous example was:
@keep_lazy(str)
- def fancy_utility_function(s, *args, **kwargs):
- ...
+ def fancy_utility_function(s, *args, **kwargs): ...
# Which can be rewritten as:
@keep_lazy_text
- def fancy_utility_function(s, *args, **kwargs):
- ...
+ def fancy_utility_function(s, *args, **kwargs): ...
``django.utils.html``
=====================
diff --git a/docs/releases/1.0-porting-guide.txt b/docs/releases/1.0-porting-guide.txt
index f0ed8f41690..145334c2383 100644
--- a/docs/releases/1.0-porting-guide.txt
+++ b/docs/releases/1.0-porting-guide.txt
@@ -144,8 +144,7 @@ example:
Old (0.96)::
- class Parent(models.Model):
- ...
+ class Parent(models.Model): ...
class Child(models.Model):
diff --git a/docs/releases/1.10.txt b/docs/releases/1.10.txt
index b5213ee2072..d98fad2c662 100644
--- a/docs/releases/1.10.txt
+++ b/docs/releases/1.10.txt
@@ -732,8 +732,7 @@ custom lookup for it. For example::
from django.db.models.lookups import Exact
- class MyField(Field):
- ...
+ class MyField(Field): ...
class MyFieldExact(Exact):
diff --git a/docs/releases/1.2.txt b/docs/releases/1.2.txt
index de3ed69a70a..526bb1d66e2 100644
--- a/docs/releases/1.2.txt
+++ b/docs/releases/1.2.txt
@@ -455,17 +455,13 @@ database-compatible values. A custom field might look something like::
class CustomModelField(models.Field):
...
- def db_type(self):
- ...
+ def db_type(self): ...
- def get_db_prep_save(self, value):
- ...
+ def get_db_prep_save(self, value): ...
- def get_db_prep_value(self, value):
- ...
+ def get_db_prep_value(self, value): ...
- def get_db_prep_lookup(self, lookup_type, value):
- ...
+ def get_db_prep_lookup(self, lookup_type, value): ...
In 1.2, these three methods have undergone a change in prototype, and
two extra methods have been introduced::
@@ -473,23 +469,17 @@ two extra methods have been introduced::
class CustomModelField(models.Field):
...
- def db_type(self, connection):
- ...
+ def db_type(self, connection): ...
- def get_prep_value(self, value):
- ...
+ def get_prep_value(self, value): ...
- def get_prep_lookup(self, lookup_type, value):
- ...
+ def get_prep_lookup(self, lookup_type, value): ...
- def get_db_prep_save(self, value, connection):
- ...
+ def get_db_prep_save(self, value, connection): ...
- def get_db_prep_value(self, value, connection, prepared=False):
- ...
+ def get_db_prep_value(self, value, connection, prepared=False): ...
- def get_db_prep_lookup(self, lookup_type, value, connection, prepared=False):
- ...
+ def get_db_prep_lookup(self, lookup_type, value, connection, prepared=False): ...
These changes are required to support multiple databases --
``db_type`` and ``get_db_prep_*`` can no longer make any assumptions
diff --git a/docs/releases/3.2.24.txt b/docs/releases/3.2.24.txt
new file mode 100644
index 00000000000..67be0f65d18
--- /dev/null
+++ b/docs/releases/3.2.24.txt
@@ -0,0 +1,13 @@
+===========================
+Django 3.2.24 release notes
+===========================
+
+*February 6, 2024*
+
+Django 3.2.24 fixes a security issue with severity "moderate" in 3.2.23.
+
+CVE-2024-24680: Potential denial-of-service in ``intcomma`` template filter
+===========================================================================
+
+The ``intcomma`` template filter was subject to a potential denial-of-service
+attack when used with very long strings.
diff --git a/docs/releases/4.2.10.txt b/docs/releases/4.2.10.txt
new file mode 100644
index 00000000000..7cdfa698144
--- /dev/null
+++ b/docs/releases/4.2.10.txt
@@ -0,0 +1,13 @@
+===========================
+Django 4.2.10 release notes
+===========================
+
+*February 6, 2024*
+
+Django 4.2.10 fixes a security issue with severity "moderate" in 4.2.9.
+
+CVE-2024-24680: Potential denial-of-service in ``intcomma`` template filter
+===========================================================================
+
+The ``intcomma`` template filter was subject to a potential denial-of-service
+attack when used with very long strings.
diff --git a/docs/releases/4.2.9.txt b/docs/releases/4.2.9.txt
new file mode 100644
index 00000000000..9a1e7dc1985
--- /dev/null
+++ b/docs/releases/4.2.9.txt
@@ -0,0 +1,13 @@
+==========================
+Django 4.2.9 release notes
+==========================
+
+*January 2, 2024*
+
+Django 4.2.9 fixes a bug in 4.2.8.
+
+Bugfixes
+========
+
+* Fixed a regression in Django 4.2.8 where admin fields on the same line could
+ overflow the page and become non-interactive (:ticket:`35012`).
diff --git a/docs/releases/5.0.1.txt b/docs/releases/5.0.1.txt
new file mode 100644
index 00000000000..8d2bd6a1873
--- /dev/null
+++ b/docs/releases/5.0.1.txt
@@ -0,0 +1,38 @@
+==========================
+Django 5.0.1 release notes
+==========================
+
+*January 2, 2024*
+
+Django 5.0.1 fixes several bugs in 5.0.
+
+Bugfixes
+========
+
+* Reallowed, following a regression in Django 5.0, using a foreign key to a
+ model with a primary key that is not ``AutoField`` in
+ :attr:`.ModelAdmin.list_filter` (:ticket:`35020`).
+
+* Fixed a long standing bug in handling the ``RETURNING INTO`` clause that
+ caused a crash when creating a model instance with a ``GeneratedField`` which
+ ``output_field`` had backend-specific converters (:ticket:`35024`).
+
+* Fixed a regression in Django 5.0 that caused a crash of ``Model.save()`` for
+ models with both ``GeneratedField`` and ``ForeignKey`` fields
+ (:ticket:`35019`).
+
+* Fixed a bug in Django 5.0 that caused a migration crash on Oracle < 23c when
+ adding a ``GeneratedField`` with ``output_field=BooleanField``
+ (:ticket:`35018`).
+
+* Fixed a regression in Django 5.0 where admin fields on the same line could
+ overflow the page and become non-interactive (:ticket:`35012`).
+
+* Added compatibility for ``oracledb`` 2.0.0 (:ticket:`35054`).
+
+* Fixed a regression in Django 5.0 where querysets referenced incorrect field
+ names from ``FilteredRelation()`` (:ticket:`35050`).
+
+* Fixed a regression in Django 5.0 that caused a system check crash when
+ ``ModelAdmin.filter_horizontal`` or ``filter_vertical`` contained a reverse
+ many-to-many relation with ``related_name`` (:ticket:`35056`).
diff --git a/docs/releases/5.0.2.txt b/docs/releases/5.0.2.txt
new file mode 100644
index 00000000000..1da6dc02d93
--- /dev/null
+++ b/docs/releases/5.0.2.txt
@@ -0,0 +1,49 @@
+==========================
+Django 5.0.2 release notes
+==========================
+
+*February 6, 2024*
+
+Django 5.0.2 fixes a security issue with severity "moderate" and several bugs
+in 5.0.1. Also, the latest string translations from Transifex are incorporated.
+
+CVE-2024-24680: Potential denial-of-service in ``intcomma`` template filter
+===========================================================================
+
+The ``intcomma`` template filter was subject to a potential denial-of-service
+attack when used with very long strings.
+
+Bugfixes
+========
+
+* Reallowed, following a regression in Django 5.0.1, filtering against local
+ foreign keys not included in :attr:`.ModelAdmin.list_filter`
+ (:ticket:`35087`).
+
+* Fixed a regression in Django 5.0 where links in the admin had an incorrect
+ color (:ticket:`35121`).
+
+* Fixed a bug in Django 5.0 that caused a crash of ``Model.full_clean()`` on
+ models with a ``GeneratedField`` (:ticket:`35127`).
+
+* Fixed a regression in Django 5.0 that caused a crash of
+ ``FilteredRelation()`` with querysets as right-hand sides (:ticket:`35135`).
+ ``FilteredRelation()`` now raises a ``ValueError`` on querysets as right-hand
+ sides.
+
+* Fixed a regression in Django 5.0 that caused a crash of the ``dumpdata``
+ management command when a base queryset used ``prefetch_related()``
+ (:ticket:`35159`).
+
+* Fixed a regression in Django 5.0 that caused the ``request_finished`` signal to
+ sometimes not be fired when running Django through an ASGI server, resulting
+ in potential resource leaks (:ticket:`35059`).
+
+* Fixed a bug in Django 5.0 that caused a migration crash on MySQL when adding
+ a ``BinaryField``, ``TextField``, ``JSONField``, or ``GeometryField`` with a
+ ``db_default`` (:ticket:`35162`).
+
+* Fixed a bug in Django 5.0 that caused a migration crash on models with a
+ literal ``db_default`` of a complex type such as ``dict`` instance of a
+ ``JSONField``. Running ``makemigrations`` might generate no-op ``AlterField``
+ operations for fields using ``db_default`` (:ticket:`35149`).
diff --git a/docs/releases/5.0.txt b/docs/releases/5.0.txt
index 4c86337b745..a10c9d280a9 100644
--- a/docs/releases/5.0.txt
+++ b/docs/releases/5.0.txt
@@ -508,6 +508,12 @@ Django < 5.0 should be replaced with a ``UUIDField`` subclass backed by
def db_type(self, connection):
return "char(32)"
+ def get_db_prep_value(self, value, connection, prepared=False):
+ value = super().get_db_prep_value(value, connection, prepared)
+ if value is not None:
+ value = value.hex
+ return value
+
For example::
class MyModel(models.Model):
@@ -515,9 +521,7 @@ For example::
Should become::
- class Char32UUIDField(models.UUIDField):
- def db_type(self, connection):
- return "char(32)"
+ class Char32UUIDField(models.UUIDField): ...
class MyModel(models.Model):
@@ -584,6 +588,11 @@ Miscellaneous
* The minimum supported version of ``docutils`` is increased to 0.19.
+* Filtering querysets against overflowing integer values now always returns an
+ empty queryset. As a consequence, you may need to use ``ExpressionWrapper()``
+ to :ref:`explicitly wrap ` arithmetic against
+ integer fields in such cases.
+
.. _deprecated-features-5.0:
Features deprecated in 5.0
diff --git a/docs/releases/index.txt b/docs/releases/index.txt
index 63f36464a0a..ba6d7fbd460 100644
--- a/docs/releases/index.txt
+++ b/docs/releases/index.txt
@@ -25,6 +25,8 @@ versions of the documentation contain the release notes for any later releases.
.. toctree::
:maxdepth: 1
+ 5.0.2
+ 5.0.1
5.0
4.2 release
@@ -33,6 +35,8 @@ versions of the documentation contain the release notes for any later releases.
.. toctree::
:maxdepth: 1
+ 4.2.10
+ 4.2.9
4.2.8
4.2.7
4.2.6
@@ -85,6 +89,7 @@ versions of the documentation contain the release notes for any later releases.
.. toctree::
:maxdepth: 1
+ 3.2.24
3.2.23
3.2.22
3.2.21
diff --git a/docs/topics/async.txt b/docs/topics/async.txt
index 944752f3a4b..e27e3e06b6e 100644
--- a/docs/topics/async.txt
+++ b/docs/topics/async.txt
@@ -111,13 +111,11 @@ For example::
@never_cache
- def my_sync_view(request):
- ...
+ def my_sync_view(request): ...
@never_cache
- async def my_async_view(request):
- ...
+ async def my_async_view(request): ...
Queries & the ORM
-----------------
@@ -298,16 +296,14 @@ as either a direct wrapper or a decorator::
from asgiref.sync import async_to_sync
- async def get_data():
- ...
+ async def get_data(): ...
sync_get_data = async_to_sync(get_data)
@async_to_sync
- async def get_other_data():
- ...
+ async def get_other_data(): ...
The async function is run in the event loop for the current thread, if one is
present. If there is no current event loop, a new event loop is spun up
@@ -338,8 +334,7 @@ as either a direct wrapper or a decorator::
@sync_to_async
- def sync_function():
- ...
+ def sync_function(): ...
Threadlocals and contextvars values are preserved across the boundary in both
directions.
diff --git a/docs/topics/auth/default.txt b/docs/topics/auth/default.txt
index 4aee26d9cae..045710b4204 100644
--- a/docs/topics/auth/default.txt
+++ b/docs/topics/auth/default.txt
@@ -553,8 +553,7 @@ The ``login_required`` decorator
@login_required
- def my_view(request):
- ...
+ def my_view(request): ...
:func:`~django.contrib.auth.decorators.login_required` does the following:
@@ -575,8 +574,7 @@ The ``login_required`` decorator
@login_required(redirect_field_name="my_redirect_field")
- def my_view(request):
- ...
+ def my_view(request): ...
Note that if you provide a value to ``redirect_field_name``, you will most
likely need to customize your login template as well, since the template
@@ -590,8 +588,7 @@ The ``login_required`` decorator
@login_required(login_url="/accounts/login/")
- def my_view(request):
- ...
+ def my_view(request): ...
Note that if you don't specify the ``login_url`` parameter, you'll need to
ensure that the :setting:`settings.LOGIN_URL ` and your login
@@ -688,8 +685,7 @@ email in the desired domain and if not, redirects to the login page::
@user_passes_test(email_check)
- def my_view(request):
- ...
+ def my_view(request): ...
:func:`~django.contrib.auth.decorators.user_passes_test` takes a required
argument: a callable that takes a
@@ -716,8 +712,7 @@ email in the desired domain and if not, redirects to the login page::
For example::
@user_passes_test(email_check, login_url="/login/")
- def my_view(request):
- ...
+ def my_view(request): ...
.. currentmodule:: django.contrib.auth.mixins
@@ -761,8 +756,7 @@ email in the desired domain and if not, redirects to the login page::
return self.request.user.username.startswith("django")
- class MyView(TestMixin1, TestMixin2, View):
- ...
+ class MyView(TestMixin1, TestMixin2, View): ...
If ``TestMixin1`` would call ``super()`` and take that result into
account, ``TestMixin1`` wouldn't work standalone anymore.
@@ -782,8 +776,7 @@ The ``permission_required`` decorator
@permission_required("polls.add_choice")
- def my_view(request):
- ...
+ def my_view(request): ...
Just like the :meth:`~django.contrib.auth.models.User.has_perm` method,
permission names take the form ``"."``
@@ -800,8 +793,7 @@ The ``permission_required`` decorator
@permission_required("polls.add_choice", login_url="/loginpage/")
- def my_view(request):
- ...
+ def my_view(request): ...
As in the :func:`~django.contrib.auth.decorators.login_required` decorator,
``login_url`` defaults to :setting:`settings.LOGIN_URL `.
@@ -820,8 +812,7 @@ The ``permission_required`` decorator
@login_required
@permission_required("polls.add_choice", raise_exception=True)
- def my_view(request):
- ...
+ def my_view(request): ...
This also avoids a redirect loop when :class:`.LoginView`'s
``redirect_authenticated_user=True`` and the logged-in user doesn't have
@@ -1608,7 +1599,7 @@ Helper functions
Defaults to :setting:`settings.LOGIN_URL ` if not supplied.
* ``redirect_field_name``: The name of a ``GET`` field containing the
- URL to redirect to after log out. Overrides ``next`` if the given
+ URL to redirect to after login. Overrides ``next`` if the given
``GET`` parameter is passed.
.. _built-in-auth-forms:
diff --git a/docs/topics/cache.txt b/docs/topics/cache.txt
index 5935c3e27f4..d2951acfa41 100644
--- a/docs/topics/cache.txt
+++ b/docs/topics/cache.txt
@@ -608,8 +608,9 @@ entire site. You'll need to add
Then, add the following required settings to your Django settings file:
* :setting:`CACHE_MIDDLEWARE_ALIAS` -- The cache alias to use for storage.
-* :setting:`CACHE_MIDDLEWARE_SECONDS` -- The number of seconds each page should
- be cached.
+* :setting:`CACHE_MIDDLEWARE_SECONDS` -- The integer number of seconds each
+ page should be cached.
+
* :setting:`CACHE_MIDDLEWARE_KEY_PREFIX` -- If the cache is shared across
multiple sites using the same Django installation, set this to the name of
the site, or some other string that is unique to this Django instance, to
@@ -668,8 +669,7 @@ decorator that will automatically cache the view's response for you::
@cache_page(60 * 15)
- def my_view(request):
- ...
+ def my_view(request): ...
``cache_page`` takes a single argument: the cache timeout, in seconds. In the
above example, the result of the ``my_view()`` view will be cached for 15
@@ -699,8 +699,7 @@ which directs the decorator to use a specific cache (from your
want::
@cache_page(60 * 15, cache="special_cache")
- def my_view(request):
- ...
+ def my_view(request): ...
You can also override the cache prefix on a per-view basis. ``cache_page``
takes an optional keyword argument, ``key_prefix``,
@@ -708,8 +707,7 @@ which works in the same way as the :setting:`CACHE_MIDDLEWARE_KEY_PREFIX`
setting for the middleware. It can be used like this::
@cache_page(60 * 15, key_prefix="site1")
- def my_view(request):
- ...
+ def my_view(request): ...
The ``key_prefix`` and ``cache`` arguments may be specified together. The
``key_prefix`` argument and the :setting:`KEY_PREFIX `
@@ -1342,8 +1340,7 @@ To do this in Django, use the convenient
@vary_on_headers("User-Agent")
- def my_view(request):
- ...
+ def my_view(request): ...
In this case, a caching mechanism (such as Django's own cache middleware) will
cache a separate version of the page for each unique user-agent.
@@ -1357,8 +1354,7 @@ anything that was already in there.
You can pass multiple headers to ``vary_on_headers()``::
@vary_on_headers("User-Agent", "Cookie")
- def my_view(request):
- ...
+ def my_view(request): ...
This tells downstream caches to vary on *both*, which means each combination of
user-agent and cookie will get its own cache value. For example, a request with
@@ -1371,13 +1367,11 @@ Because varying on cookie is so common, there's a
are equivalent::
@vary_on_cookie
- def my_view(request):
- ...
+ def my_view(request): ...
@vary_on_headers("Cookie")
- def my_view(request):
- ...
+ def my_view(request): ...
The headers you pass to ``vary_on_headers`` are not case sensitive;
``"User-Agent"`` is the same thing as ``"user-agent"``.
@@ -1423,8 +1417,7 @@ decorator. Example::
@cache_control(private=True)
- def my_view(request):
- ...
+ def my_view(request): ...
This decorator takes care of sending out the appropriate HTTP header behind the
scenes.
@@ -1463,8 +1456,7 @@ directive::
@cache_control(max_age=3600)
- def my_view(request):
- ...
+ def my_view(request): ...
(If you *do* use the caching middleware, it already sets the ``max-age`` with
the value of the :setting:`CACHE_MIDDLEWARE_SECONDS` setting. In that case,
@@ -1494,8 +1486,7 @@ caches. Example::
@never_cache
- def myview(request):
- ...
+ def myview(request): ...
Order of ``MIDDLEWARE``
=======================
diff --git a/docs/topics/checks.txt b/docs/topics/checks.txt
index 9a91ceb4690..3e3bbe19d65 100644
--- a/docs/topics/checks.txt
+++ b/docs/topics/checks.txt
@@ -113,8 +113,7 @@ You can register "deployment checks" that are only relevant to a production
settings file like this::
@register(Tags.security, deploy=True)
- def my_check(app_configs, **kwargs):
- ...
+ def my_check(app_configs, **kwargs): ...
These checks will only be run if the :option:`check --deploy` option is used.
@@ -124,8 +123,7 @@ to ``register``.
The code below is equivalent to the code above::
- def my_check(app_configs, **kwargs):
- ...
+ def my_check(app_configs, **kwargs): ...
register(my_check, Tags.security, deploy=True)
diff --git a/docs/topics/conditional-view-processing.txt b/docs/topics/conditional-view-processing.txt
index 2447697de46..dfd36a64ae0 100644
--- a/docs/topics/conditional-view-processing.txt
+++ b/docs/topics/conditional-view-processing.txt
@@ -72,8 +72,7 @@ Suppose you have this pair of models, representing a small blog system::
from django.db import models
- class Blog(models.Model):
- ...
+ class Blog(models.Model): ...
class Entry(models.Model):
@@ -96,8 +95,7 @@ for your front page view::
@condition(last_modified_func=latest_entry)
- def front_page(request, blog_id):
- ...
+ def front_page(request, blog_id): ...
.. admonition:: Be careful with the order of decorators
@@ -131,13 +129,11 @@ We could write the earlier example, which only uses a last-modified function,
using one of these decorators::
@last_modified(latest_entry)
- def front_page(request, blog_id):
- ...
+ def front_page(request, blog_id): ...
...or::
- def front_page(request, blog_id):
- ...
+ def front_page(request, blog_id): ...
front_page = last_modified(latest_entry)(front_page)
@@ -154,8 +150,7 @@ this would lead to incorrect behavior.
# Bad code. Don't do this!
@etag(etag_func)
@last_modified(last_modified_func)
- def my_view(request):
- ...
+ def my_view(request): ...
# End of bad code.
diff --git a/docs/topics/db/fixtures.txt b/docs/topics/db/fixtures.txt
index 48c5605ff48..ac5b34dae0d 100644
--- a/docs/topics/db/fixtures.txt
+++ b/docs/topics/db/fixtures.txt
@@ -161,8 +161,7 @@ You could also write a decorator to encapsulate this logic::
@disable_for_loaddata
- def my_handler(**kwargs):
- ...
+ def my_handler(**kwargs): ...
Just be aware that this logic will disable the signals whenever fixtures are
deserialized, not just during :djadmin:`loaddata`.
diff --git a/docs/topics/db/managers.txt b/docs/topics/db/managers.txt
index 61de153898b..3a7a635e490 100644
--- a/docs/topics/db/managers.txt
+++ b/docs/topics/db/managers.txt
@@ -380,8 +380,8 @@ this base class::
class Meta:
abstract = True
-If you use this directly in a subclass, ``objects`` will be the default
-manager if you declare no managers in the base class::
+If you use this directly in a child class, ``objects`` will be the default
+manager if you declare no managers in the child class::
class ChildA(AbstractBase):
# ...
diff --git a/docs/topics/db/models.txt b/docs/topics/db/models.txt
index cc6c1f5298d..b419185bbcf 100644
--- a/docs/topics/db/models.txt
+++ b/docs/topics/db/models.txt
@@ -157,9 +157,12 @@ ones:
`, the field will be required.
:attr:`~Field.choices`
- A :term:`sequence` of 2-tuples to use as choices for this field. If this
- is given, the default form widget will be a select box instead of the
- standard text field and will limit choices to the choices given.
+ A :term:`sequence` of 2-value tuples, a :term:`mapping`, an
+ :ref:`enumeration type `, or a callable (that
+ expects no arguments and returns any of the previous formats), to use as
+ choices for this field. If this is given, the default form widget will be a
+ select box instead of the standard text field and will limit choices to the
+ choices given.
A choices list looks like this::
@@ -216,6 +219,10 @@ ones:
Further examples are available in the :ref:`model field reference
`.
+ .. versionchanged:: 5.0
+
+ Support for mappings and callables was added.
+
:attr:`~Field.default`
The default value for the field. This can be a value or a callable
object. If callable it will be called every time a new object is
diff --git a/docs/topics/db/multi-db.txt b/docs/topics/db/multi-db.txt
index 8da71df2500..be7e9953fa8 100644
--- a/docs/topics/db/multi-db.txt
+++ b/docs/topics/db/multi-db.txt
@@ -740,14 +740,14 @@ primary key of the related object is valid. If the primary key is
stored on a separate database, it's not possible to easily evaluate
the validity of a primary key.
-If you're using Postgres, Oracle, or MySQL with InnoDB, this is
+If you're using Postgres, SQLite, Oracle, or MySQL with InnoDB, this is
enforced at the database integrity level -- database level key
constraints prevent the creation of relations that can't be validated.
-However, if you're using SQLite or MySQL with MyISAM tables, there is
-no enforced referential integrity; as a result, you may be able to
-'fake' cross database foreign keys. However, this configuration is not
-officially supported by Django.
+However, if you're using MySQL with MyISAM tables, there is no enforced
+referential integrity; as a result, you may be able to 'fake' cross database
+foreign keys. However, this configuration is not officially supported by
+Django.
.. _contrib_app_multiple_databases:
diff --git a/docs/topics/db/transactions.txt b/docs/topics/db/transactions.txt
index 40d40bc7d02..e012d73af48 100644
--- a/docs/topics/db/transactions.txt
+++ b/docs/topics/db/transactions.txt
@@ -311,8 +311,7 @@ Pass a function, or any callable, to :func:`on_commit`::
from django.db import transaction
- def send_welcome_email():
- ...
+ def send_welcome_email(): ...
transaction.on_commit(send_welcome_email)
diff --git a/docs/topics/forms/modelforms.txt b/docs/topics/forms/modelforms.txt
index fbd5695c17d..3727c4f6f52 100644
--- a/docs/topics/forms/modelforms.txt
+++ b/docs/topics/forms/modelforms.txt
@@ -708,8 +708,7 @@ using the previous ``ArticleForm`` class:
.. code-block:: pycon
>>> class EnhancedArticleForm(ArticleForm):
- ... def clean_pub_date(self):
- ... ...
+ ... def clean_pub_date(self): ...
...
This creates a form that behaves identically to ``ArticleForm``, except there's
diff --git a/docs/topics/http/file-uploads.txt b/docs/topics/http/file-uploads.txt
index cb0fb5b7fbd..3a1c96846c2 100644
--- a/docs/topics/http/file-uploads.txt
+++ b/docs/topics/http/file-uploads.txt
@@ -346,7 +346,8 @@ list::
@csrf_protect
def _upload_file_view(request):
- ... # Process request
+ # Process request
+ ...
If you are using a class-based view, you will need to use
:func:`~django.views.decorators.csrf.csrf_exempt` on its
@@ -367,4 +368,5 @@ list::
@method_decorator(csrf_protect)
def post(self, request, *args, **kwargs):
- ... # Process request
+ # Process request
+ ...
diff --git a/docs/topics/migrations.txt b/docs/topics/migrations.txt
index b7cd7043c2b..74f6fddad6d 100644
--- a/docs/topics/migrations.txt
+++ b/docs/topics/migrations.txt
@@ -77,16 +77,17 @@ meaning that if a migration fails to apply you will have to manually unpick
the changes in order to try again (it's impossible to roll back to an
earlier point).
-In addition, MySQL will fully rewrite tables for almost every schema operation
-and generally takes a time proportional to the number of rows in the table to
-add or remove columns. On slower hardware this can be worse than a minute per
-million rows - adding a few columns to a table with just a few million rows
-could lock your site up for over ten minutes.
-
-Finally, MySQL has relatively small limits on name lengths for columns, tables
-and indexes, as well as a limit on the combined size of all columns an index
-covers. This means that indexes that are possible on other backends will
-fail to be created under MySQL.
+MySQL 8.0 introduced significant performance enhancements for
+`DDL operations`_, making them more efficient and reducing the need for full
+table rebuilds. However, it cannot guarantee a complete absence of locks or
+interruptions. In situations where locks are still necessary, the duration of
+these operations will be proportionate to the number of rows involved.
+
+Finally, MySQL has a relatively small limit on the combined size of all columns
+an index covers. This means that indexes that are possible on other backends
+will fail to be created under MySQL.
+
+.. _DDL operations: https://dev.mysql.com/doc/refman/en/innodb-online-ddl-operations.html
SQLite
------
diff --git a/docs/topics/settings.txt b/docs/topics/settings.txt
index dc6cd945b39..553f788e61e 100644
--- a/docs/topics/settings.txt
+++ b/docs/topics/settings.txt
@@ -44,9 +44,8 @@ by using an environment variable, :envvar:`DJANGO_SETTINGS_MODULE`.
The value of :envvar:`DJANGO_SETTINGS_MODULE` should be in Python path syntax,
e.g. ``mysite.settings``. Note that the settings module should be on the
-Python `import search path`_.
+Python :py:data:`sys.path`.
-.. _import search path: https://diveinto.org/python3/your-first-python-program.html#importsearchpath
The ``django-admin`` utility
----------------------------
diff --git a/docs/topics/signals.txt b/docs/topics/signals.txt
index a4d973ebb42..6a577dd8ad1 100644
--- a/docs/topics/signals.txt
+++ b/docs/topics/signals.txt
@@ -200,8 +200,7 @@ signals sent by some model::
@receiver(pre_save, sender=MyModel)
- def my_handler(sender, **kwargs):
- ...
+ def my_handler(sender, **kwargs): ...
The ``my_handler`` function will only be called when an instance of ``MyModel``
is saved.
diff --git a/docs/topics/testing/tools.txt b/docs/topics/testing/tools.txt
index a415097c560..ae5b7588126 100644
--- a/docs/topics/testing/tools.txt
+++ b/docs/topics/testing/tools.txt
@@ -1352,8 +1352,7 @@ For example::
class OtherDBTests(TestCase):
databases = {"other"}
- def test_other_db_query(self):
- ...
+ def test_other_db_query(self): ...
This test will only allow queries against the ``other`` database. Just like for
:attr:`SimpleTestCase.databases` and :attr:`TransactionTestCase.databases`, the
@@ -1954,22 +1953,18 @@ you might label fast or slow tests::
class SampleTestCase(TestCase):
@tag("fast")
- def test_fast(self):
- ...
+ def test_fast(self): ...
@tag("slow")
- def test_slow(self):
- ...
+ def test_slow(self): ...
@tag("slow", "core")
- def test_slow_but_core(self):
- ...
+ def test_slow_but_core(self): ...
You can also tag a test case class::
@tag("slow", "core")
- class SampleTestCase(TestCase):
- ...
+ class SampleTestCase(TestCase): ...
Subclasses inherit tags from superclasses, and methods inherit tags from their
class. Given::
@@ -1977,8 +1972,7 @@ class. Given::
@tag("foo")
class SampleTestCaseChild(SampleTestCase):
@tag("bar")
- def test(self):
- ...
+ def test(self): ...
``SampleTestCaseChild.test`` will be labeled with ``'slow'``, ``'core'``,
``'bar'``, and ``'foo'``.
@@ -2078,8 +2072,7 @@ creates.
class MyTests(TestCase):
@mock.patch(...)
@async_to_sync
- async def test_my_thing(self):
- ...
+ async def test_my_thing(self): ...
.. _topics-testing-email:
@@ -2160,8 +2153,8 @@ redirected into a ``StringIO`` instance::
class ClosepollTest(TestCase):
def test_command_output(self):
out = StringIO()
- call_command("closepoll", stdout=out)
- self.assertIn("Expected output", out.getvalue())
+ call_command("closepoll", poll_ids=[1], stdout=out)
+ self.assertIn('Successfully closed poll "1"', out.getvalue())
.. _skipping-tests:
diff --git a/package.json b/package.json
index 510d50ad5b1..02589973403 100644
--- a/package.json
+++ b/package.json
@@ -9,11 +9,11 @@
"npm": ">=1.3.0"
},
"devDependencies": {
- "eslint": "^8.49.0",
- "puppeteer": "^21.1.1",
+ "eslint": "^8.56.0",
+ "puppeteer": "^21.7.0",
"grunt": "^1.6.1",
"grunt-cli": "^1.4.3",
- "grunt-contrib-qunit": "^7.0.0",
- "qunit": "^2.19.4"
+ "grunt-contrib-qunit": "^8.0.1",
+ "qunit": "^2.20.0"
}
}
diff --git a/setup.cfg b/setup.cfg
index b93d4250c03..057319c6204 100644
--- a/setup.cfg
+++ b/setup.cfg
@@ -38,7 +38,7 @@ packages = find:
include_package_data = true
zip_safe = false
install_requires =
- asgiref >= 3.7.0
+ asgiref >= 3.7.0, < 4
sqlparse >= 0.3.1
tzdata; sys_platform == 'win32'
diff --git a/tests/admin_changelist/test_date_hierarchy.py b/tests/admin_changelist/test_date_hierarchy.py
index 03afaa33d91..94a6a8eb6c6 100644
--- a/tests/admin_changelist/test_date_hierarchy.py
+++ b/tests/admin_changelist/test_date_hierarchy.py
@@ -90,7 +90,8 @@ def test_invalid_params(self):
{"year": 2017, "month": 12, "day": 0},
)
for invalid_query in tests:
- with self.subTest(query=invalid_query), self.assertRaises(
- IncorrectLookupParameters
+ with (
+ self.subTest(query=invalid_query),
+ self.assertRaises(IncorrectLookupParameters),
):
self.assertDateParams(invalid_query, None, None)
diff --git a/tests/admin_inlines/models.py b/tests/admin_inlines/models.py
index a8d2ee02e13..eca5bae422c 100644
--- a/tests/admin_inlines/models.py
+++ b/tests/admin_inlines/models.py
@@ -1,6 +1,7 @@
"""
Testing of admin inline formsets.
"""
+
import random
from django.contrib.contenttypes.fields import GenericForeignKey
diff --git a/tests/admin_scripts/tests.py b/tests/admin_scripts/tests.py
index 7f39d7fcfc8..50e8d4a3b1e 100644
--- a/tests/admin_scripts/tests.py
+++ b/tests/admin_scripts/tests.py
@@ -3,6 +3,7 @@
advertised - especially with regards to the handling of the
DJANGO_SETTINGS_MODULE and default settings.py files.
"""
+
import os
import re
import shutil
@@ -757,7 +758,9 @@ def test_startapp_unicode_name(self):
with open(os.path.join(app_path, "apps.py"), encoding="utf8") as f:
content = f.read()
self.assertIn("class こんにちはConfig(AppConfig)", content)
- self.assertIn('name = "こんにちは"' if HAS_BLACK else "name = 'こんにちは'", content)
+ self.assertIn(
+ 'name = "こんにちは"' if HAS_BLACK else "name = 'こんにちは'", content
+ )
def test_builtin_command(self):
"""
diff --git a/tests/admin_views/custom_has_permission_admin.py b/tests/admin_views/custom_has_permission_admin.py
index 6dc2011b24d..17b6498aab8 100644
--- a/tests/admin_views/custom_has_permission_admin.py
+++ b/tests/admin_views/custom_has_permission_admin.py
@@ -1,6 +1,7 @@
"""
A custom AdminSite for AdminViewPermissionsTest.test_login_has_permission().
"""
+
from django.contrib import admin
from django.contrib.auth import get_permission_codename
from django.contrib.auth.forms import AuthenticationForm
diff --git a/tests/admin_views/customadmin.py b/tests/admin_views/customadmin.py
index e3429ec4bc4..4b30c5c30fd 100644
--- a/tests/admin_views/customadmin.py
+++ b/tests/admin_views/customadmin.py
@@ -1,6 +1,7 @@
"""
A second, custom AdminSite -- see tests.CustomAdminSiteTests.
"""
+
from django.contrib import admin
from django.contrib.auth.admin import UserAdmin
from django.contrib.auth.models import User
diff --git a/tests/aggregation_regress/tests.py b/tests/aggregation_regress/tests.py
index bfb3919b237..9199bf3eba2 100644
--- a/tests/aggregation_regress/tests.py
+++ b/tests/aggregation_regress/tests.py
@@ -1564,8 +1564,9 @@ def test_aggregate_unmanaged_model_as_tables(self):
"django.db.connection.features.allows_group_by_selected_pks_on_model",
return_value=True,
):
- with mock.patch.object(Book._meta, "managed", False), mock.patch.object(
- Author._meta, "managed", False
+ with (
+ mock.patch.object(Book._meta, "managed", False),
+ mock.patch.object(Author._meta, "managed", False),
):
_, _, grouping = qs.query.get_compiler(using="default").pre_sql_setup()
self.assertEqual(len(grouping), 2)
diff --git a/tests/asgi/tests.py b/tests/asgi/tests.py
index ced24c658e8..bbb274d9ed7 100644
--- a/tests/asgi/tests.py
+++ b/tests/asgi/tests.py
@@ -1,12 +1,15 @@
import asyncio
import sys
import threading
+import time
from pathlib import Path
+from asgiref.sync import sync_to_async
from asgiref.testing import ApplicationCommunicator
from django.contrib.staticfiles.handlers import ASGIStaticFilesHandler
from django.core.asgi import get_asgi_application
+from django.core.exceptions import RequestDataTooBig
from django.core.handlers.asgi import ASGIHandler, ASGIRequest
from django.core.signals import request_finished, request_started
from django.db import close_old_connections
@@ -20,12 +23,24 @@
)
from django.urls import path
from django.utils.http import http_date
+from django.views.decorators.csrf import csrf_exempt
from .urls import sync_waiter, test_filename
TEST_STATIC_ROOT = Path(__file__).parent / "project" / "static"
+class SignalHandler:
+ """Helper class to track threads and kwargs when signals are dispatched."""
+
+ def __init__(self):
+ super().__init__()
+ self.calls = []
+
+ def __call__(self, signal, **kwargs):
+ self.calls.append({"thread": threading.current_thread(), "kwargs": kwargs})
+
+
@override_settings(ROOT_URLCONF="asgi.urls")
class ASGITest(SimpleTestCase):
async_request_factory = AsyncRequestFactory()
@@ -196,6 +211,96 @@ async def test_post_body(self):
self.assertEqual(response_body["type"], "http.response.body")
self.assertEqual(response_body["body"], b"Echo!")
+ async def test_create_request_error(self):
+ # Track request_finished signal.
+ signal_handler = SignalHandler()
+ request_finished.connect(signal_handler)
+ self.addCleanup(request_finished.disconnect, signal_handler)
+
+ # Request class that always fails creation with RequestDataTooBig.
+ class TestASGIRequest(ASGIRequest):
+
+ def __init__(self, scope, body_file):
+ super().__init__(scope, body_file)
+ raise RequestDataTooBig()
+
+ # Handler to use the custom request class.
+ class TestASGIHandler(ASGIHandler):
+ request_class = TestASGIRequest
+
+ application = TestASGIHandler()
+ scope = self.async_request_factory._base_scope(path="/not-important/")
+ communicator = ApplicationCommunicator(application, scope)
+
+ # Initiate request.
+ await communicator.send_input({"type": "http.request"})
+ # Give response.close() time to finish.
+ await communicator.wait()
+
+ self.assertEqual(len(signal_handler.calls), 1)
+ self.assertNotEqual(
+ signal_handler.calls[0]["thread"], threading.current_thread()
+ )
+
+ async def test_cancel_post_request_with_sync_processing(self):
+ """
+ The request.body object should be available and readable in view
+ code, even if the ASGIHandler cancels processing part way through.
+ """
+ loop = asyncio.get_event_loop()
+ # Events to monitor the view processing from the parent test code.
+ view_started_event = asyncio.Event()
+ view_finished_event = asyncio.Event()
+ # Record received request body or exceptions raised in the test view
+ outcome = []
+
+ # This view will run in a new thread because it is wrapped in
+ # sync_to_async. The view consumes the POST body data after a short
+ # delay. The test will cancel the request using http.disconnect during
+ # the delay, but because this is a sync view the code runs to
+ # completion. There should be no exceptions raised inside the view
+ # code.
+ @csrf_exempt
+ @sync_to_async
+ def post_view(request):
+ try:
+ loop.call_soon_threadsafe(view_started_event.set)
+ time.sleep(0.1)
+ # Do something to read request.body after pause
+ outcome.append({"request_body": request.body})
+ return HttpResponse("ok")
+ except Exception as e:
+ outcome.append({"exception": e})
+ finally:
+ loop.call_soon_threadsafe(view_finished_event.set)
+
+ # Request class to use the view.
+ class TestASGIRequest(ASGIRequest):
+ urlconf = (path("post/", post_view),)
+
+ # Handler to use request class.
+ class TestASGIHandler(ASGIHandler):
+ request_class = TestASGIRequest
+
+ application = TestASGIHandler()
+ scope = self.async_request_factory._base_scope(
+ method="POST",
+ path="/post/",
+ )
+ communicator = ApplicationCommunicator(application, scope)
+
+ await communicator.send_input({"type": "http.request", "body": b"Body data!"})
+
+ # Wait until the view code has started, then send http.disconnect.
+ await view_started_event.wait()
+ await communicator.send_input({"type": "http.disconnect"})
+ # Wait until view code has finished.
+ await view_finished_event.wait()
+ with self.assertRaises(asyncio.TimeoutError):
+ await communicator.receive_output()
+
+ self.assertEqual(outcome, [{"request_body": b"Body data!"}])
+
async def test_untouched_request_body_gets_closed(self):
application = get_asgi_application()
scope = self.async_request_factory._base_scope(method="POST", path="/post/")
@@ -312,17 +417,12 @@ async def test_non_unicode_query_string(self):
self.assertEqual(response_body["body"], b"")
async def test_request_lifecycle_signals_dispatched_with_thread_sensitive(self):
- class SignalHandler:
- """Track threads handler is dispatched on."""
-
- threads = []
-
- def __call__(self, **kwargs):
- self.threads.append(threading.current_thread())
-
+ # Track request_started and request_finished signals.
signal_handler = SignalHandler()
request_started.connect(signal_handler)
+ self.addCleanup(request_started.disconnect, signal_handler)
request_finished.connect(signal_handler)
+ self.addCleanup(request_finished.disconnect, signal_handler)
# Perform a basic request.
application = get_asgi_application()
@@ -339,10 +439,11 @@ def __call__(self, **kwargs):
await communicator.wait()
# AsyncToSync should have executed the signals in the same thread.
- request_started_thread, request_finished_thread = signal_handler.threads
- self.assertEqual(request_started_thread, request_finished_thread)
- request_started.disconnect(signal_handler)
- request_finished.disconnect(signal_handler)
+ self.assertEqual(len(signal_handler.calls), 2)
+ request_started_call, request_finished_call = signal_handler.calls
+ self.assertEqual(
+ request_started_call["thread"], request_finished_call["thread"]
+ )
async def test_concurrent_async_uses_multiple_thread_pools(self):
sync_waiter.active_threads.clear()
@@ -378,6 +479,10 @@ async def test_concurrent_async_uses_multiple_thread_pools(self):
async def test_asyncio_cancel_error(self):
# Flag to check if the view was cancelled.
view_did_cancel = False
+ # Track request_finished signal.
+ signal_handler = SignalHandler()
+ request_finished.connect(signal_handler)
+ self.addCleanup(request_finished.disconnect, signal_handler)
# A view that will listen for the cancelled error.
async def view(request):
@@ -412,6 +517,13 @@ class TestASGIHandler(ASGIHandler):
# Give response.close() time to finish.
await communicator.wait()
self.assertIs(view_did_cancel, False)
+ # Exactly one call to request_finished handler.
+ self.assertEqual(len(signal_handler.calls), 1)
+ handler_call = signal_handler.calls.pop()
+ # It was NOT on the async thread.
+ self.assertNotEqual(handler_call["thread"], threading.current_thread())
+ # The signal sender is the handler class.
+ self.assertEqual(handler_call["kwargs"], {"sender": TestASGIHandler})
# Request cycle with a disconnect before the view can respond.
application = TestASGIHandler()
@@ -427,11 +539,22 @@ class TestASGIHandler(ASGIHandler):
await communicator.receive_output()
await communicator.wait()
self.assertIs(view_did_cancel, True)
+ # Exactly one call to request_finished handler.
+ self.assertEqual(len(signal_handler.calls), 1)
+ handler_call = signal_handler.calls.pop()
+ # It was NOT on the async thread.
+ self.assertNotEqual(handler_call["thread"], threading.current_thread())
+ # The signal sender is the handler class.
+ self.assertEqual(handler_call["kwargs"], {"sender": TestASGIHandler})
async def test_asyncio_streaming_cancel_error(self):
# Similar to test_asyncio_cancel_error(), but during a streaming
# response.
view_did_cancel = False
+ # Track request_finished signals.
+ signal_handler = SignalHandler()
+ request_finished.connect(signal_handler)
+ self.addCleanup(request_finished.disconnect, signal_handler)
async def streaming_response():
nonlocal view_did_cancel
@@ -466,6 +589,13 @@ class TestASGIHandler(ASGIHandler):
self.assertEqual(response_body["body"], b"Hello World!")
await communicator.wait()
self.assertIs(view_did_cancel, False)
+ # Exactly one call to request_finished handler.
+ self.assertEqual(len(signal_handler.calls), 1)
+ handler_call = signal_handler.calls.pop()
+ # It was NOT on the async thread.
+ self.assertNotEqual(handler_call["thread"], threading.current_thread())
+ # The signal sender is the handler class.
+ self.assertEqual(handler_call["kwargs"], {"sender": TestASGIHandler})
# Request cycle with a disconnect.
application = TestASGIHandler()
@@ -484,6 +614,13 @@ class TestASGIHandler(ASGIHandler):
await communicator.receive_output()
await communicator.wait()
self.assertIs(view_did_cancel, True)
+ # Exactly one call to request_finished handler.
+ self.assertEqual(len(signal_handler.calls), 1)
+ handler_call = signal_handler.calls.pop()
+ # It was NOT on the async thread.
+ self.assertNotEqual(handler_call["thread"], threading.current_thread())
+ # The signal sender is the handler class.
+ self.assertEqual(handler_call["kwargs"], {"sender": TestASGIHandler})
async def test_streaming(self):
scope = self.async_request_factory._base_scope(
diff --git a/tests/auth_tests/models/custom_permissions.py b/tests/auth_tests/models/custom_permissions.py
index 52d28bd2762..385de4e8461 100644
--- a/tests/auth_tests/models/custom_permissions.py
+++ b/tests/auth_tests/models/custom_permissions.py
@@ -3,6 +3,7 @@
Django permissions model. This allows us to check that the PermissionsMixin
includes everything that is needed to interact with the ModelBackend.
"""
+
from django.contrib.auth.models import AbstractBaseUser, PermissionsMixin
from django.db import models
diff --git a/tests/auth_tests/test_forms.py b/tests/auth_tests/test_forms.py
index 81c56a428ea..4583c1f344a 100644
--- a/tests/auth_tests/test_forms.py
+++ b/tests/auth_tests/test_forms.py
@@ -486,8 +486,9 @@ def signal_handler(**kwargs):
user_login_failed.disconnect(signal_handler)
def test_inactive_user_i18n(self):
- with self.settings(USE_I18N=True), translation.override(
- "pt-br", deactivate=True
+ with (
+ self.settings(USE_I18N=True),
+ translation.override("pt-br", deactivate=True),
):
# The user is inactive.
data = {
@@ -906,9 +907,9 @@ def test_bug_14242(self):
class MyUserForm(UserChangeForm):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
- self.fields[
- "groups"
- ].help_text = "These groups give users different permissions"
+ self.fields["groups"].help_text = (
+ "These groups give users different permissions"
+ )
class Meta(UserChangeForm.Meta):
fields = ("groups",)
diff --git a/tests/auth_tests/test_hashers.py b/tests/auth_tests/test_hashers.py
index e10992b25c6..cf2ebafa15c 100644
--- a/tests/auth_tests/test_hashers.py
+++ b/tests/auth_tests/test_hashers.py
@@ -327,8 +327,9 @@ def test_bcrypt_harden_runtime(self):
with mock.patch.object(hasher, "rounds", 4):
encoded = make_password("letmein", hasher="bcrypt")
- with mock.patch.object(hasher, "rounds", 6), mock.patch.object(
- hasher, "encode", side_effect=hasher.encode
+ with (
+ mock.patch.object(hasher, "rounds", 6),
+ mock.patch.object(hasher, "encode", side_effect=hasher.encode),
):
hasher.harden_runtime("wrong_password", encoded)
@@ -502,8 +503,9 @@ def test_pbkdf2_harden_runtime(self):
with mock.patch.object(hasher, "iterations", 1):
encoded = make_password("letmein")
- with mock.patch.object(hasher, "iterations", 6), mock.patch.object(
- hasher, "encode", side_effect=hasher.encode
+ with (
+ mock.patch.object(hasher, "iterations", 6),
+ mock.patch.object(hasher, "encode", side_effect=hasher.encode),
):
hasher.harden_runtime("wrong_password", encoded)
@@ -551,8 +553,9 @@ def test_check_password_calls_harden_runtime(self):
hasher = get_hasher("default")
encoded = make_password("letmein")
- with mock.patch.object(hasher, "harden_runtime"), mock.patch.object(
- hasher, "must_update", return_value=True
+ with (
+ mock.patch.object(hasher, "harden_runtime"),
+ mock.patch.object(hasher, "must_update", return_value=True),
):
# Correct password supplied, no hardening needed
check_password("letmein", encoded)
diff --git a/tests/auth_tests/test_views.py b/tests/auth_tests/test_views.py
index d15a166c987..0897046c1f5 100644
--- a/tests/auth_tests/test_views.py
+++ b/tests/auth_tests/test_views.py
@@ -818,9 +818,9 @@ def get_response(request):
# Use POST request to log in
SessionMiddleware(get_response).process_request(req)
CsrfViewMiddleware(get_response).process_view(req, LoginView.as_view(), (), {})
- req.META[
- "SERVER_NAME"
- ] = "testserver" # Required to have redirect work in login view
+ req.META["SERVER_NAME"] = (
+ "testserver" # Required to have redirect work in login view
+ )
req.META["SERVER_PORT"] = 80
resp = CsrfViewMiddleware(LoginView.as_view())(req)
csrf_cookie = resp.cookies.get(settings.CSRF_COOKIE_NAME, None)
diff --git a/tests/backends/base/test_base.py b/tests/backends/base/test_base.py
index ada2cc33c90..3a0f7f1f1cb 100644
--- a/tests/backends/base/test_base.py
+++ b/tests/backends/base/test_base.py
@@ -168,8 +168,9 @@ def test_database_queried(self):
def test_nested_wrapper_invoked(self):
outer_wrapper = self.mock_wrapper()
inner_wrapper = self.mock_wrapper()
- with connection.execute_wrapper(outer_wrapper), connection.execute_wrapper(
- inner_wrapper
+ with (
+ connection.execute_wrapper(outer_wrapper),
+ connection.execute_wrapper(inner_wrapper),
):
self.call_execute(connection)
self.assertEqual(inner_wrapper.call_count, 1)
@@ -182,8 +183,10 @@ def blocker(*args):
wrapper = self.mock_wrapper()
c = connection # This alias shortens the next line.
- with c.execute_wrapper(wrapper), c.execute_wrapper(blocker), c.execute_wrapper(
- wrapper
+ with (
+ c.execute_wrapper(wrapper),
+ c.execute_wrapper(blocker),
+ c.execute_wrapper(wrapper),
):
with c.cursor() as cursor:
cursor.execute("The database never sees this")
diff --git a/tests/backends/test_utils.py b/tests/backends/test_utils.py
index 03d4b036fdf..704498836e4 100644
--- a/tests/backends/test_utils.py
+++ b/tests/backends/test_utils.py
@@ -1,4 +1,5 @@
"""Tests for django.db.backends.utils"""
+
from decimal import Decimal, Rounded
from django.db import NotSupportedError, connection
diff --git a/tests/backends/tests.py b/tests/backends/tests.py
index 5c562682f0a..08a21d8dedb 100644
--- a/tests/backends/tests.py
+++ b/tests/backends/tests.py
@@ -1,4 +1,5 @@
"""Tests related to django.db.backends that haven't been organized."""
+
import datetime
import threading
import unittest
diff --git a/tests/bash_completion/tests.py b/tests/bash_completion/tests.py
index 856e351dfa1..324e9809375 100644
--- a/tests/bash_completion/tests.py
+++ b/tests/bash_completion/tests.py
@@ -1,6 +1,7 @@
"""
A series of tests to establish that the command-line bash completion works.
"""
+
import os
import sys
import unittest
diff --git a/tests/basic/models.py b/tests/basic/models.py
index b71b60a2135..cd896431408 100644
--- a/tests/basic/models.py
+++ b/tests/basic/models.py
@@ -3,6 +3,7 @@
This is a basic model with only two non-primary-key fields.
"""
+
import uuid
from django.db import models
diff --git a/tests/check_framework/test_security.py b/tests/check_framework/test_security.py
index b1839f48c3d..cb035a90a4b 100644
--- a/tests/check_framework/test_security.py
+++ b/tests/check_framework/test_security.py
@@ -593,8 +593,9 @@ def test_with_referrer_policy(self):
("strict-origin", "origin"),
)
for value in tests:
- with self.subTest(value=value), override_settings(
- SECURE_REFERRER_POLICY=value
+ with (
+ self.subTest(value=value),
+ override_settings(SECURE_REFERRER_POLICY=value),
):
self.assertEqual(base.check_referrer_policy(None), [])
@@ -663,8 +664,11 @@ def test_no_coop(self):
def test_with_coop(self):
tests = ["same-origin", "same-origin-allow-popups", "unsafe-none"]
for value in tests:
- with self.subTest(value=value), override_settings(
- SECURE_CROSS_ORIGIN_OPENER_POLICY=value,
+ with (
+ self.subTest(value=value),
+ override_settings(
+ SECURE_CROSS_ORIGIN_OPENER_POLICY=value,
+ ),
):
self.assertEqual(base.check_cross_origin_opener_policy(None), [])
diff --git a/tests/contenttypes_tests/test_management.py b/tests/contenttypes_tests/test_management.py
index eb472d80cef..14ff4e59521 100644
--- a/tests/contenttypes_tests/test_management.py
+++ b/tests/contenttypes_tests/test_management.py
@@ -88,9 +88,10 @@ def test_unavailable_content_type_model(self):
def test_contenttypes_removed_in_installed_apps_without_models(self):
ContentType.objects.create(app_label="empty_models", model="Fake 1")
ContentType.objects.create(app_label="no_models", model="Fake 2")
- with mock.patch(
- "builtins.input", return_value="yes"
- ), captured_stdout() as stdout:
+ with (
+ mock.patch("builtins.input", return_value="yes"),
+ captured_stdout() as stdout,
+ ):
call_command("remove_stale_contenttypes", verbosity=2)
self.assertNotIn(
"Deleting stale content type 'empty_models | Fake 1'",
@@ -106,9 +107,10 @@ def test_contenttypes_removed_in_installed_apps_without_models(self):
def test_contenttypes_removed_for_apps_not_in_installed_apps(self):
ContentType.objects.create(app_label="empty_models", model="Fake 1")
ContentType.objects.create(app_label="no_models", model="Fake 2")
- with mock.patch(
- "builtins.input", return_value="yes"
- ), captured_stdout() as stdout:
+ with (
+ mock.patch("builtins.input", return_value="yes"),
+ captured_stdout() as stdout,
+ ):
call_command(
"remove_stale_contenttypes", include_stale_apps=True, verbosity=2
)
diff --git a/tests/contenttypes_tests/test_models.py b/tests/contenttypes_tests/test_models.py
index 36c14cf56f2..1999364dd50 100644
--- a/tests/contenttypes_tests/test_models.py
+++ b/tests/contenttypes_tests/test_models.py
@@ -325,8 +325,9 @@ def test_multidb(self):
db_for_read().
"""
ContentType.objects.clear_cache()
- with self.assertNumQueries(0, using="default"), self.assertNumQueries(
- 1, using="other"
+ with (
+ self.assertNumQueries(0, using="default"),
+ self.assertNumQueries(1, using="other"),
):
ContentType.objects.get_for_model(Author)
diff --git a/tests/contenttypes_tests/test_views.py b/tests/contenttypes_tests/test_views.py
index 4d85d15065f..75f39a7babc 100644
--- a/tests/contenttypes_tests/test_views.py
+++ b/tests/contenttypes_tests/test_views.py
@@ -150,10 +150,8 @@ def test_shortcut_view_with_null_site_fk(self, get_model):
"""
The shortcut view works if a model's ForeignKey to site is None.
"""
- get_model.side_effect = (
- lambda *args, **kwargs: MockSite
- if args[0] == "sites.Site"
- else ModelWithNullFKToSite
+ get_model.side_effect = lambda *args, **kwargs: (
+ MockSite if args[0] == "sites.Site" else ModelWithNullFKToSite
)
obj = ModelWithNullFKToSite.objects.create(title="title")
@@ -172,10 +170,8 @@ def test_shortcut_view_with_site_m2m(self, get_model):
site if it's attached to the object or to the domain of the first site
found in the m2m relationship.
"""
- get_model.side_effect = (
- lambda *args, **kwargs: MockSite
- if args[0] == "sites.Site"
- else ModelWithM2MToSite
+ get_model.side_effect = lambda *args, **kwargs: (
+ MockSite if args[0] == "sites.Site" else ModelWithM2MToSite
)
# get_current_site() will lookup a Site object, so these must match the
diff --git a/tests/context_processors/tests.py b/tests/context_processors/tests.py
index 23f9d591496..ba92ff8b05e 100644
--- a/tests/context_processors/tests.py
+++ b/tests/context_processors/tests.py
@@ -1,6 +1,7 @@
"""
Tests for Django's bundled context processors.
"""
+
from django.test import SimpleTestCase, TestCase, override_settings
diff --git a/tests/custom_lookups/tests.py b/tests/custom_lookups/tests.py
index a636977b67e..22f443d100f 100644
--- a/tests/custom_lookups/tests.py
+++ b/tests/custom_lookups/tests.py
@@ -238,10 +238,11 @@ class LookupTests(TestCase):
def test_custom_name_lookup(self):
a1 = Author.objects.create(name="a1", birthdate=date(1981, 2, 16))
Author.objects.create(name="a2", birthdate=date(2012, 2, 29))
- with register_lookup(models.DateField, YearTransform), register_lookup(
- models.DateField, YearTransform, lookup_name="justtheyear"
- ), register_lookup(YearTransform, Exactly), register_lookup(
- YearTransform, Exactly, lookup_name="isactually"
+ with (
+ register_lookup(models.DateField, YearTransform),
+ register_lookup(models.DateField, YearTransform, lookup_name="justtheyear"),
+ register_lookup(YearTransform, Exactly),
+ register_lookup(YearTransform, Exactly, lookup_name="isactually"),
):
qs1 = Author.objects.filter(birthdate__testyear__exactly=1981)
qs2 = Author.objects.filter(birthdate__justtheyear__isactually=1981)
diff --git a/tests/db_functions/comparison/test_nullif.py b/tests/db_functions/comparison/test_nullif.py
index 9839e6b4c51..cdbc3d69604 100644
--- a/tests/db_functions/comparison/test_nullif.py
+++ b/tests/db_functions/comparison/test_nullif.py
@@ -23,9 +23,11 @@ def test_basic(self):
[
("smithj",),
(
- ""
- if connection.features.interprets_empty_strings_as_nulls
- else None,
+ (
+ ""
+ if connection.features.interprets_empty_strings_as_nulls
+ else None
+ ),
),
],
)
diff --git a/tests/db_functions/datetime/test_extract_trunc.py b/tests/db_functions/datetime/test_extract_trunc.py
index 29212b6e244..e576e6e4641 100644
--- a/tests/db_functions/datetime/test_extract_trunc.py
+++ b/tests/db_functions/datetime/test_extract_trunc.py
@@ -113,9 +113,11 @@ def create_model(self, start_datetime, end_datetime):
end_date=end_datetime.date() if end_datetime else None,
start_time=start_datetime.time() if start_datetime else None,
end_time=end_datetime.time() if end_datetime else None,
- duration=(end_datetime - start_datetime)
- if start_datetime and end_datetime
- else None,
+ duration=(
+ (end_datetime - start_datetime)
+ if start_datetime and end_datetime
+ else None
+ ),
)
def test_extract_year_exact_lookup(self):
diff --git a/tests/db_functions/models.py b/tests/db_functions/models.py
index d6a06511bcc..c126f1bae1a 100644
--- a/tests/db_functions/models.py
+++ b/tests/db_functions/models.py
@@ -1,6 +1,7 @@
"""
Tests for built in Function expressions.
"""
+
from django.db import models
diff --git a/tests/db_functions/text/test_md5.py b/tests/db_functions/text/test_md5.py
index fd0aec58f2d..4fd66f0397e 100644
--- a/tests/db_functions/text/test_md5.py
+++ b/tests/db_functions/text/test_md5.py
@@ -35,9 +35,11 @@ def test_basic(self):
"ca6d48f6772000141e66591aee49d56c",
"bf2c13bc1154e3d2e7df848cbc8be73d",
"d41d8cd98f00b204e9800998ecf8427e",
- "d41d8cd98f00b204e9800998ecf8427e"
- if connection.features.interprets_empty_strings_as_nulls
- else None,
+ (
+ "d41d8cd98f00b204e9800998ecf8427e"
+ if connection.features.interprets_empty_strings_as_nulls
+ else None
+ ),
],
)
diff --git a/tests/db_functions/text/test_sha1.py b/tests/db_functions/text/test_sha1.py
index 175c5727ffc..ef846d07aa5 100644
--- a/tests/db_functions/text/test_sha1.py
+++ b/tests/db_functions/text/test_sha1.py
@@ -35,9 +35,11 @@ def test_basic(self):
"0781e0745a2503e6ded05ed5bc554c421d781b0c",
"198d15ea139de04060caf95bc3e0ec5883cba881",
"da39a3ee5e6b4b0d3255bfef95601890afd80709",
- "da39a3ee5e6b4b0d3255bfef95601890afd80709"
- if connection.features.interprets_empty_strings_as_nulls
- else None,
+ (
+ "da39a3ee5e6b4b0d3255bfef95601890afd80709"
+ if connection.features.interprets_empty_strings_as_nulls
+ else None
+ ),
],
)
diff --git a/tests/db_functions/text/test_sha224.py b/tests/db_functions/text/test_sha224.py
index a6254f43e3d..60d9e378a69 100644
--- a/tests/db_functions/text/test_sha224.py
+++ b/tests/db_functions/text/test_sha224.py
@@ -37,9 +37,11 @@ def test_basic(self):
"2297904883e78183cb118fc3dc21a610d60daada7b6ebdbc85139f4d",
"eba942746e5855121d9d8f79e27dfdebed81adc85b6bf41591203080",
"d14a028c2a3a2bc9476102bb288234c415a2b01f828ea62ac5b3e42f",
- "d14a028c2a3a2bc9476102bb288234c415a2b01f828ea62ac5b3e42f"
- if connection.features.interprets_empty_strings_as_nulls
- else None,
+ (
+ "d14a028c2a3a2bc9476102bb288234c415a2b01f828ea62ac5b3e42f"
+ if connection.features.interprets_empty_strings_as_nulls
+ else None
+ ),
],
)
diff --git a/tests/db_functions/text/test_sha256.py b/tests/db_functions/text/test_sha256.py
index b399dbecd32..0cf72783e7c 100644
--- a/tests/db_functions/text/test_sha256.py
+++ b/tests/db_functions/text/test_sha256.py
@@ -35,9 +35,11 @@ def test_basic(self):
"6e4cce20cd83fc7c202f21a8b2452a68509cf24d1c272a045b5e0cfc43f0d94e",
"3ad2039e3ec0c88973ae1c0fce5a3dbafdd5a1627da0a92312c54ebfcf43988e",
"e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
- "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"
- if connection.features.interprets_empty_strings_as_nulls
- else None,
+ (
+ "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"
+ if connection.features.interprets_empty_strings_as_nulls
+ else None
+ ),
],
)
diff --git a/tests/db_functions/text/test_sha384.py b/tests/db_functions/text/test_sha384.py
index 250cadda4a0..a4d07d32952 100644
--- a/tests/db_functions/text/test_sha384.py
+++ b/tests/db_functions/text/test_sha384.py
@@ -39,10 +39,12 @@ def test_basic(self):
"26676a43c7ba378621175853b0",
"38b060a751ac96384cd9327eb1b1e36a21fdb71114be07434c0cc7bf63f6e1da274ede"
"bfe76f65fbd51ad2f14898b95b",
- "38b060a751ac96384cd9327eb1b1e36a21fdb71114be07434c0cc7bf63f6e1da274ede"
- "bfe76f65fbd51ad2f14898b95b"
- if connection.features.interprets_empty_strings_as_nulls
- else None,
+ (
+ "38b060a751ac96384cd9327eb1b1e36a21fdb71114be07434c0cc7bf63f6e1da27"
+ "4edebfe76f65fbd51ad2f14898b95b"
+ if connection.features.interprets_empty_strings_as_nulls
+ else None
+ ),
],
)
diff --git a/tests/db_functions/text/test_sha512.py b/tests/db_functions/text/test_sha512.py
index f5a7ad4ae54..b4c7a4bbd8a 100644
--- a/tests/db_functions/text/test_sha512.py
+++ b/tests/db_functions/text/test_sha512.py
@@ -39,10 +39,12 @@ def test_basic(self):
"172d88798345a3a7666faf5f35a144c60812d3234dcd35f444624f2faee16857",
"cf83e1357eefb8bdf1542850d66d8007d620e4050b5715dc83f4a921d36ce9ce"
"47d0d13c5d85f2b0ff8318d2877eec2f63b931bd47417a81a538327af927da3e",
- "cf83e1357eefb8bdf1542850d66d8007d620e4050b5715dc83f4a921d36ce9ce"
- "47d0d13c5d85f2b0ff8318d2877eec2f63b931bd47417a81a538327af927da3e"
- if connection.features.interprets_empty_strings_as_nulls
- else None,
+ (
+ "cf83e1357eefb8bdf1542850d66d8007d620e4050b5715dc83f4a921d36ce9ce"
+ "47d0d13c5d85f2b0ff8318d2877eec2f63b931bd47417a81a538327af927da3e"
+ if connection.features.interprets_empty_strings_as_nulls
+ else None
+ ),
],
)
diff --git a/tests/db_utils/tests.py b/tests/db_utils/tests.py
index a2d9cc7b5e8..4028a8acdf3 100644
--- a/tests/db_utils/tests.py
+++ b/tests/db_utils/tests.py
@@ -1,4 +1,5 @@
"""Tests for django.db.utils."""
+
import unittest
from django.core.exceptions import ImproperlyConfigured
diff --git a/tests/dbshell/test_sqlite.py b/tests/dbshell/test_sqlite.py
index faf9882ad91..e795f7619b5 100644
--- a/tests/dbshell/test_sqlite.py
+++ b/tests/dbshell/test_sqlite.py
@@ -35,8 +35,11 @@ def test_non_zero_exit_status_when_path_to_db_is_path(self):
cmd_args = self.settings_to_cmd_args_env(sqlite_with_path)[0]
msg = '"sqlite3 test.db.sqlite3" returned non-zero exit status 1.'
- with mock.patch(
- "django.db.backends.sqlite3.client.DatabaseClient.runshell",
- side_effect=subprocess.CalledProcessError(returncode=1, cmd=cmd_args),
- ), self.assertRaisesMessage(CommandError, msg):
+ with (
+ mock.patch(
+ "django.db.backends.sqlite3.client.DatabaseClient.runshell",
+ side_effect=subprocess.CalledProcessError(returncode=1, cmd=cmd_args),
+ ),
+ self.assertRaisesMessage(CommandError, msg),
+ ):
call_command("dbshell")
diff --git a/tests/distinct_on_fields/tests.py b/tests/distinct_on_fields/tests.py
index b80878bbf25..93b3f27aec0 100644
--- a/tests/distinct_on_fields/tests.py
+++ b/tests/distinct_on_fields/tests.py
@@ -76,9 +76,11 @@ def test_basic_distinct_on(self):
(StaffTag.objects.distinct("staff", "tag"), [self.st1]),
(
Tag.objects.order_by("parent__pk", "pk").distinct("parent"),
- [self.t2, self.t4, self.t1]
- if connection.features.nulls_order_largest
- else [self.t1, self.t2, self.t4],
+ (
+ [self.t2, self.t4, self.t1]
+ if connection.features.nulls_order_largest
+ else [self.t1, self.t2, self.t4]
+ ),
),
(
StaffTag.objects.select_related("staff")
diff --git a/tests/expressions/models.py b/tests/expressions/models.py
index 0a8a0a6584d..0cab2756311 100644
--- a/tests/expressions/models.py
+++ b/tests/expressions/models.py
@@ -1,6 +1,7 @@
"""
Tests for F() query expression syntax.
"""
+
import uuid
from django.db import models
diff --git a/tests/file_uploads/tests.py b/tests/file_uploads/tests.py
index 693efc4c62b..833d841361c 100644
--- a/tests/file_uploads/tests.py
+++ b/tests/file_uploads/tests.py
@@ -451,9 +451,10 @@ def test_filename_overflow(self):
def test_file_content(self):
file = tempfile.NamedTemporaryFile
- with file(suffix=".ctype_extra") as no_content_type, file(
- suffix=".ctype_extra"
- ) as simple_file:
+ with (
+ file(suffix=".ctype_extra") as no_content_type,
+ file(suffix=".ctype_extra") as simple_file,
+ ):
no_content_type.write(b"no content")
no_content_type.seek(0)
@@ -482,9 +483,10 @@ def test_file_content(self):
def test_content_type_extra(self):
"""Uploaded files may have content type parameters available."""
file = tempfile.NamedTemporaryFile
- with file(suffix=".ctype_extra") as no_content_type, file(
- suffix=".ctype_extra"
- ) as simple_file:
+ with (
+ file(suffix=".ctype_extra") as no_content_type,
+ file(suffix=".ctype_extra") as simple_file,
+ ):
no_content_type.write(b"something")
no_content_type.seek(0)
diff --git a/tests/file_uploads/uploadhandler.py b/tests/file_uploads/uploadhandler.py
index a1e1a5af053..d3c88ac305b 100644
--- a/tests/file_uploads/uploadhandler.py
+++ b/tests/file_uploads/uploadhandler.py
@@ -1,6 +1,7 @@
"""
Upload handlers to test the upload API.
"""
+
import os
from tempfile import NamedTemporaryFile
diff --git a/tests/files/tests.py b/tests/files/tests.py
index 7dc5c04668c..9d3a471cb31 100644
--- a/tests/files/tests.py
+++ b/tests/files/tests.py
@@ -144,35 +144,40 @@ def test_file_iteration_with_text(self):
self.assertEqual(list(f), ["one\n", "two\n", "three"])
def test_readable(self):
- with tempfile.TemporaryFile() as temp, File(
- temp, name="something.txt"
- ) as test_file:
+ with (
+ tempfile.TemporaryFile() as temp,
+ File(temp, name="something.txt") as test_file,
+ ):
self.assertTrue(test_file.readable())
self.assertFalse(test_file.readable())
def test_writable(self):
- with tempfile.TemporaryFile() as temp, File(
- temp, name="something.txt"
- ) as test_file:
+ with (
+ tempfile.TemporaryFile() as temp,
+ File(temp, name="something.txt") as test_file,
+ ):
self.assertTrue(test_file.writable())
self.assertFalse(test_file.writable())
- with tempfile.TemporaryFile("rb") as temp, File(
- temp, name="something.txt"
- ) as test_file:
+ with (
+ tempfile.TemporaryFile("rb") as temp,
+ File(temp, name="something.txt") as test_file,
+ ):
self.assertFalse(test_file.writable())
def test_seekable(self):
- with tempfile.TemporaryFile() as temp, File(
- temp, name="something.txt"
- ) as test_file:
+ with (
+ tempfile.TemporaryFile() as temp,
+ File(temp, name="something.txt") as test_file,
+ ):
self.assertTrue(test_file.seekable())
self.assertFalse(test_file.seekable())
def test_io_wrapper(self):
content = "vive l'été\n"
- with tempfile.TemporaryFile() as temp, File(
- temp, name="something.txt"
- ) as test_file:
+ with (
+ tempfile.TemporaryFile() as temp,
+ File(temp, name="something.txt") as test_file,
+ ):
test_file.write(content.encode())
test_file.seek(0)
wrapper = TextIOWrapper(test_file, "utf-8", newline="\n")
diff --git a/tests/filtered_relation/models.py b/tests/filtered_relation/models.py
index d34a86305fc..765d4956e2a 100644
--- a/tests/filtered_relation/models.py
+++ b/tests/filtered_relation/models.py
@@ -36,6 +36,8 @@ class Book(models.Model):
related_query_name="book",
)
editor = models.ForeignKey(Editor, models.CASCADE)
+ number_editor = models.IntegerField(default=-1)
+ editor_number = models.IntegerField(default=-2)
generic_author = GenericRelation(Author)
state = models.CharField(max_length=9, choices=STATES, default=AVAILABLE)
diff --git a/tests/filtered_relation/tests.py b/tests/filtered_relation/tests.py
index 5a21a47f369..82caba86621 100644
--- a/tests/filtered_relation/tests.py
+++ b/tests/filtered_relation/tests.py
@@ -792,6 +792,52 @@ def test_conditional_expression_with_multiple_fields(self):
).filter(my_books__isnull=True)
self.assertSequenceEqual(qs, [])
+ def test_conditional_expression_rhs_contains_relation_name(self):
+ qs = Book.objects.annotate(
+ rel=FilteredRelation(
+ "editor",
+ condition=Q(id=1 * F("number_editor")),
+ )
+ ).filter(rel__isnull=True)
+ self.assertSequenceEqual(qs, [])
+
+ def test_conditional_expression_rhs_startswith_relation_name(self):
+ qs = Book.objects.annotate(
+ rel=FilteredRelation(
+ "editor",
+ condition=Q(id=1 * F("editor_number")),
+ )
+ ).filter(rel__isnull=True)
+ self.assertSequenceEqual(qs, [])
+
+ def test_conditional_expression_lhs_startswith_relation_name(self):
+ qs = Book.objects.annotate(
+ rel=FilteredRelation(
+ "editor",
+ condition=Q(editor_number__gt=1),
+ )
+ ).filter(rel__isnull=True)
+ self.assertSequenceEqual(qs, [])
+
+ def test_conditional_expression_lhs_contains_relation_name(self):
+ qs = Book.objects.annotate(
+ rel=FilteredRelation(
+ "editor",
+ condition=Q(number_editor__gt=1),
+ )
+ ).filter(rel__isnull=True)
+ self.assertSequenceEqual(qs, [])
+
+ def test_conditional_expression_does_not_support_queryset(self):
+ msg = "Passing a QuerySet within a FilteredRelation is not supported."
+ with self.assertRaisesMessage(ValueError, msg):
+ Author.objects.annotate(
+ poem_book=FilteredRelation(
+ "book",
+ condition=Q(book__in=Book.objects.filter(title__istartswith="a")),
+ ),
+ ).filter(poem_book__isnull=False)
+
class FilteredRelationAggregationTests(TestCase):
@classmethod
diff --git a/tests/fixtures/models.py b/tests/fixtures/models.py
index 37b0066d70c..c87e170afc9 100644
--- a/tests/fixtures/models.py
+++ b/tests/fixtures/models.py
@@ -101,9 +101,15 @@ class Meta:
proxy = True
+class VisaManager(models.Manager):
+ def get_queryset(self):
+ return super().get_queryset().prefetch_related("permissions")
+
+
class Visa(models.Model):
person = models.ForeignKey(Person, models.CASCADE)
permissions = models.ManyToManyField(Permission, blank=True)
+ objects = VisaManager()
def __str__(self):
return "%s %s" % (
diff --git a/tests/fixtures/tests.py b/tests/fixtures/tests.py
index 78141b25b43..bce55bc3554 100644
--- a/tests/fixtures/tests.py
+++ b/tests/fixtures/tests.py
@@ -830,6 +830,22 @@ def test_dumpdata_proxy_with_concrete(self):
)
self.assertEqual(len(warning_list), 0)
+ def test_dumpdata_objects_with_prefetch_related(self):
+ management.call_command(
+ "loaddata", "fixture6.json", "fixture8.json", verbosity=0
+ )
+ with self.assertNumQueries(5):
+ self._dumpdata_assert(
+ ["fixtures.visa"],
+ '[{"fields": {"permissions": [["add_user", "auth", "user"]],'
+ '"person": ["Stephane Grappelli"]},'
+ '"model": "fixtures.visa", "pk": 2},'
+ '{"fields": {"permissions": [], "person": ["Prince"]},'
+ '"model": "fixtures.visa", "pk": 3}]',
+ natural_foreign_keys=True,
+ primary_keys="2,3",
+ )
+
def test_compress_format_loading(self):
# Load fixture 4 (compressed), using format specification
management.call_command("loaddata", "fixture4.json", verbosity=0)
diff --git a/tests/force_insert_update/models.py b/tests/force_insert_update/models.py
index b95b197454d..e6fdfbd827d 100644
--- a/tests/force_insert_update/models.py
+++ b/tests/force_insert_update/models.py
@@ -2,6 +2,7 @@
Tests for forcing insert and update queries (instead of Django's normal
automatic behavior).
"""
+
from django.db import models
diff --git a/tests/forms_tests/field_tests/test_decimalfield.py b/tests/forms_tests/field_tests/test_decimalfield.py
index 9d26bc88b0c..cb13e5b71b0 100644
--- a/tests/forms_tests/field_tests/test_decimalfield.py
+++ b/tests/forms_tests/field_tests/test_decimalfield.py
@@ -80,8 +80,9 @@ def test_enter_a_number_error(self):
"--0.12",
)
for value in values:
- with self.subTest(value=value), self.assertRaisesMessage(
- ValidationError, "'Enter a number.'"
+ with (
+ self.subTest(value=value),
+ self.assertRaisesMessage(ValidationError, "'Enter a number.'"),
):
f.clean(value)
diff --git a/tests/forms_tests/field_tests/test_filefield.py b/tests/forms_tests/field_tests/test_filefield.py
index 11388bdc09d..9744981471a 100644
--- a/tests/forms_tests/field_tests/test_filefield.py
+++ b/tests/forms_tests/field_tests/test_filefield.py
@@ -53,7 +53,8 @@ def test_filefield_1(self):
self.assertIsInstance(
f.clean(
SimpleUploadedFile(
- "我隻氣墊船裝滿晒鱔.txt", "मेरी मँडराने वाली नाव सर्पमीनों से भरी ह".encode()
+ "我隻氣墊船裝滿晒鱔.txt",
+ "मेरी मँडराने वाली नाव सर्पमीनों से भरी ह".encode(),
)
),
SimpleUploadedFile,
diff --git a/tests/forms_tests/tests/test_formsets.py b/tests/forms_tests/tests/test_formsets.py
index 3c260010c25..f80c1dc09e3 100644
--- a/tests/forms_tests/tests/test_formsets.py
+++ b/tests/forms_tests/tests/test_formsets.py
@@ -217,9 +217,12 @@ def mocked_func(*args, **kwargs):
[("Calexico", "100"), ("Any1", "42"), ("Any2", "101")]
)
- with mock.patch(
- "django.forms.formsets.ManagementForm.is_valid", mocked_is_valid
- ), mock.patch("django.forms.forms.BaseForm.full_clean", mocked_full_clean):
+ with (
+ mock.patch(
+ "django.forms.formsets.ManagementForm.is_valid", mocked_is_valid
+ ),
+ mock.patch("django.forms.forms.BaseForm.full_clean", mocked_full_clean),
+ ):
self.assertTrue(formset.is_valid())
self.assertEqual(is_valid_counter.call_count, 1)
self.assertEqual(full_clean_counter.call_count, 4)
diff --git a/tests/gis_tests/gdal_tests/test_raster.py b/tests/gis_tests/gdal_tests/test_raster.py
index 67108cbb329..a5f1edef0bb 100644
--- a/tests/gis_tests/gdal_tests/test_raster.py
+++ b/tests/gis_tests/gdal_tests/test_raster.py
@@ -102,8 +102,9 @@ def test_geotransform_bad_inputs(self):
]
msg = "Geotransform must consist of 6 numeric values."
for geotransform in error_geotransforms:
- with self.subTest(i=geotransform), self.assertRaisesMessage(
- ValueError, msg
+ with (
+ self.subTest(i=geotransform),
+ self.assertRaisesMessage(ValueError, msg),
):
rsmem.geotransform = geotransform
diff --git a/tests/gis_tests/geogapp/tests.py b/tests/gis_tests/geogapp/tests.py
index 85656089938..cb783b94653 100644
--- a/tests/gis_tests/geogapp/tests.py
+++ b/tests/gis_tests/geogapp/tests.py
@@ -1,6 +1,7 @@
"""
Tests for geography support in PostGIS
"""
+
import os
from django.contrib.gis.db import models
diff --git a/tests/gis_tests/test_data.py b/tests/gis_tests/test_data.py
index 0a949073208..8dd6042175e 100644
--- a/tests/gis_tests/test_data.py
+++ b/tests/gis_tests/test_data.py
@@ -2,6 +2,7 @@
This module has the mock object definitions used to hold reference geometry
for the GEOS and GDAL tests.
"""
+
import json
import os
diff --git a/tests/handlers/tests.py b/tests/handlers/tests.py
index ab3837d25e0..85f873f3da4 100644
--- a/tests/handlers/tests.py
+++ b/tests/handlers/tests.py
@@ -244,8 +244,9 @@ def test_no_response(self):
("/no_response_cbv/", "handlers.views.NoResponse.__call__"),
)
for url, view in tests:
- with self.subTest(url=url), self.assertRaisesMessage(
- ValueError, msg % view
+ with (
+ self.subTest(url=url),
+ self.assertRaisesMessage(ValueError, msg % view),
):
self.client.get(url)
diff --git a/tests/httpwrappers/tests.py b/tests/httpwrappers/tests.py
index 0a41ea5ec63..5f3f195c783 100644
--- a/tests/httpwrappers/tests.py
+++ b/tests/httpwrappers/tests.py
@@ -352,9 +352,9 @@ def test_long_line(self):
h.headers["Content-Disposition"] = 'attachment; filename="%s"' % f
# This one is triggering https://bugs.python.org/issue20747, that is Python
# will itself insert a newline in the header
- h.headers[
- "Content-Disposition"
- ] = 'attachment; filename="EdelRot_Blu\u0308te (3)-0.JPG"'
+ h.headers["Content-Disposition"] = (
+ 'attachment; filename="EdelRot_Blu\u0308te (3)-0.JPG"'
+ )
def test_newlines_in_headers(self):
# Bug #10188: Do not allow newlines in headers (CR or LF)
diff --git a/tests/humanize_tests/tests.py b/tests/humanize_tests/tests.py
index cf29f582329..a78bbadafd8 100644
--- a/tests/humanize_tests/tests.py
+++ b/tests/humanize_tests/tests.py
@@ -116,39 +116,71 @@ def test_i18n_html_ordinal(self):
def test_intcomma(self):
test_list = (
100,
+ -100,
1000,
+ -1000,
10123,
+ -10123,
10311,
+ -10311,
1000000,
+ -1000000,
1234567.25,
+ -1234567.25,
"100",
+ "-100",
"1000",
+ "-1000",
"10123",
+ "-10123",
"10311",
+ "-10311",
"1000000",
+ "-1000000",
"1234567.1234567",
+ "-1234567.1234567",
Decimal("1234567.1234567"),
+ Decimal("-1234567.1234567"),
None,
"1234567",
+ "-1234567",
"1234567.12",
+ "-1234567.12",
+ "the quick brown fox jumped over the lazy dog",
)
result_list = (
"100",
+ "-100",
"1,000",
+ "-1,000",
"10,123",
+ "-10,123",
"10,311",
+ "-10,311",
"1,000,000",
+ "-1,000,000",
"1,234,567.25",
+ "-1,234,567.25",
"100",
+ "-100",
"1,000",
+ "-1,000",
"10,123",
+ "-10,123",
"10,311",
+ "-10,311",
"1,000,000",
+ "-1,000,000",
"1,234,567.1234567",
+ "-1,234,567.1234567",
"1,234,567.1234567",
+ "-1,234,567.1234567",
None,
"1,234,567",
+ "-1,234,567",
"1,234,567.12",
+ "-1,234,567.12",
+ "the quick brown fox jumped over the lazy dog",
)
with translation.override("en"):
self.humanize_tester(test_list, result_list, "intcomma")
@@ -156,39 +188,71 @@ def test_intcomma(self):
def test_l10n_intcomma(self):
test_list = (
100,
+ -100,
1000,
+ -1000,
10123,
+ -10123,
10311,
+ -10311,
1000000,
+ -1000000,
1234567.25,
+ -1234567.25,
"100",
+ "-100",
"1000",
+ "-1000",
"10123",
+ "-10123",
"10311",
+ "-10311",
"1000000",
+ "-1000000",
"1234567.1234567",
+ "-1234567.1234567",
Decimal("1234567.1234567"),
+ -Decimal("1234567.1234567"),
None,
"1234567",
+ "-1234567",
"1234567.12",
+ "-1234567.12",
+ "the quick brown fox jumped over the lazy dog",
)
result_list = (
"100",
+ "-100",
"1,000",
+ "-1,000",
"10,123",
+ "-10,123",
"10,311",
+ "-10,311",
"1,000,000",
+ "-1,000,000",
"1,234,567.25",
+ "-1,234,567.25",
"100",
+ "-100",
"1,000",
+ "-1,000",
"10,123",
+ "-10,123",
"10,311",
+ "-10,311",
"1,000,000",
+ "-1,000,000",
"1,234,567.1234567",
+ "-1,234,567.1234567",
"1,234,567.1234567",
+ "-1,234,567.1234567",
None,
"1,234,567",
+ "-1,234,567",
"1,234,567.12",
+ "-1,234,567.12",
+ "the quick brown fox jumped over the lazy dog",
)
with self.settings(USE_THOUSAND_SEPARATOR=False):
with translation.override("en"):
diff --git a/tests/invalid_models_tests/test_models.py b/tests/invalid_models_tests/test_models.py
index dc52f58c443..f8451ea1e58 100644
--- a/tests/invalid_models_tests/test_models.py
+++ b/tests/invalid_models_tests/test_models.py
@@ -2027,15 +2027,18 @@ class Meta:
self.assertEqual(
Model.check(databases=self.databases),
- [
- Error(
- "'constraints' refers to the nonexistent field 'missing_field'.",
- obj=Model,
- id="models.E012",
- ),
- ]
- if connection.features.supports_table_check_constraints
- else [],
+ (
+ [
+ Error(
+ "'constraints' refers to the nonexistent field "
+ "'missing_field'.",
+ obj=Model,
+ id="models.E012",
+ ),
+ ]
+ if connection.features.supports_table_check_constraints
+ else []
+ ),
)
@skipUnlessDBFeature("supports_table_check_constraints")
@@ -2381,15 +2384,18 @@ class Meta:
self.assertEqual(
Model.check(databases=self.databases),
- [
- Error(
- "'constraints' refers to the nonexistent field 'missing_field'.",
- obj=Model,
- id="models.E012",
- ),
- ]
- if connection.features.supports_partial_indexes
- else [],
+ (
+ [
+ Error(
+ "'constraints' refers to the nonexistent field "
+ "'missing_field'.",
+ obj=Model,
+ id="models.E012",
+ ),
+ ]
+ if connection.features.supports_partial_indexes
+ else []
+ ),
)
def test_unique_constraint_condition_pointing_to_joined_fields(self):
@@ -2409,15 +2415,17 @@ class Meta:
self.assertEqual(
Model.check(databases=self.databases),
- [
- Error(
- "'constraints' refers to the joined field 'parent__age__lt'.",
- obj=Model,
- id="models.E041",
- )
- ]
- if connection.features.supports_partial_indexes
- else [],
+ (
+ [
+ Error(
+ "'constraints' refers to the joined field 'parent__age__lt'.",
+ obj=Model,
+ id="models.E041",
+ )
+ ]
+ if connection.features.supports_partial_indexes
+ else []
+ ),
)
def test_unique_constraint_pointing_to_reverse_o2o(self):
@@ -2436,15 +2444,17 @@ class Meta:
self.assertEqual(
Model.check(databases=self.databases),
- [
- Error(
- "'constraints' refers to the nonexistent field 'model'.",
- obj=Model,
- id="models.E012",
- ),
- ]
- if connection.features.supports_partial_indexes
- else [],
+ (
+ [
+ Error(
+ "'constraints' refers to the nonexistent field 'model'.",
+ obj=Model,
+ id="models.E012",
+ ),
+ ]
+ if connection.features.supports_partial_indexes
+ else []
+ ),
)
def test_deferrable_unique_constraint(self):
diff --git a/tests/lookup/tests.py b/tests/lookup/tests.py
index 8af459ccd7c..a198a13b62b 100644
--- a/tests/lookup/tests.py
+++ b/tests/lookup/tests.py
@@ -1522,7 +1522,6 @@ def test_lookup_in_order_by(self):
qs = Season.objects.order_by(LessThan(F("year"), 1910), F("year"))
self.assertSequenceEqual(qs, [self.s1, self.s3, self.s2])
- @skipUnlessDBFeature("supports_boolean_expr_in_select_clause")
def test_aggregate_combined_lookup(self):
expression = Cast(GreaterThan(F("year"), 1900), models.IntegerField())
qs = Season.objects.aggregate(modern=models.Sum(expression))
diff --git a/tests/m2m_and_m2o/models.py b/tests/m2m_and_m2o/models.py
index 6a5b0b29c98..4cbd1eb4e8c 100644
--- a/tests/m2m_and_m2o/models.py
+++ b/tests/m2m_and_m2o/models.py
@@ -3,6 +3,7 @@
Make sure to set ``related_name`` if you use relationships to the same table.
"""
+
from django.db import models
diff --git a/tests/m2m_intermediary/models.py b/tests/m2m_intermediary/models.py
index 1333d14b410..f82498651f6 100644
--- a/tests/m2m_intermediary/models.py
+++ b/tests/m2m_intermediary/models.py
@@ -9,6 +9,7 @@
field, which specifies the ``Reporter``'s position for the given article
(e.g. "Staff writer").
"""
+
from django.db import models
diff --git a/tests/mail/tests.py b/tests/mail/tests.py
index 848ee32e9f8..8d51c287b32 100644
--- a/tests/mail/tests.py
+++ b/tests/mail/tests.py
@@ -1394,8 +1394,9 @@ def test_wrong_admins_managers(self):
):
msg = "The %s setting must be a list of 2-tuples." % setting
for value in tests:
- with self.subTest(setting=setting, value=value), self.settings(
- **{setting: value}
+ with (
+ self.subTest(setting=setting, value=value),
+ self.settings(**{setting: value}),
):
with self.assertRaisesMessage(ValueError, msg):
mail_func("subject", "content")
diff --git a/tests/many_to_many/models.py b/tests/many_to_many/models.py
index 541928e94d6..42fc4269904 100644
--- a/tests/many_to_many/models.py
+++ b/tests/many_to_many/models.py
@@ -6,6 +6,7 @@
In this example, an ``Article`` can be published in multiple ``Publication``
objects, and a ``Publication`` has multiple ``Article`` objects.
"""
+
from django.db import models
diff --git a/tests/many_to_one/models.py b/tests/many_to_one/models.py
index cca7e798179..56e660592ab 100644
--- a/tests/many_to_one/models.py
+++ b/tests/many_to_one/models.py
@@ -3,6 +3,7 @@
To define a many-to-one relationship, use ``ForeignKey()``.
"""
+
from django.db import models
diff --git a/tests/middleware/test_security.py b/tests/middleware/test_security.py
index dd220b8f4c3..339b9181c37 100644
--- a/tests/middleware/test_security.py
+++ b/tests/middleware/test_security.py
@@ -248,8 +248,9 @@ def test_referrer_policy_on(self):
(("strict-origin", "origin"), "strict-origin,origin"),
)
for value, expected in tests:
- with self.subTest(value=value), override_settings(
- SECURE_REFERRER_POLICY=value
+ with (
+ self.subTest(value=value),
+ override_settings(SECURE_REFERRER_POLICY=value),
):
self.assertEqual(
self.process_response().headers["Referrer-Policy"],
@@ -287,8 +288,11 @@ def test_coop_on(self):
"""
tests = ["same-origin", "same-origin-allow-popups", "unsafe-none"]
for value in tests:
- with self.subTest(value=value), override_settings(
- SECURE_CROSS_ORIGIN_OPENER_POLICY=value,
+ with (
+ self.subTest(value=value),
+ override_settings(
+ SECURE_CROSS_ORIGIN_OPENER_POLICY=value,
+ ),
):
self.assertEqual(
self.process_response().headers["Cross-Origin-Opener-Policy"],
diff --git a/tests/middleware/tests.py b/tests/middleware/tests.py
index 2153a0bb5a6..4771bbe49a2 100644
--- a/tests/middleware/tests.py
+++ b/tests/middleware/tests.py
@@ -851,9 +851,9 @@ class GZipMiddlewareTest(SimpleTestCase):
def setUp(self):
self.req = self.request_factory.get("/")
self.req.META["HTTP_ACCEPT_ENCODING"] = "gzip, deflate"
- self.req.META[
- "HTTP_USER_AGENT"
- ] = "Mozilla/5.0 (Windows NT 5.1; rv:9.0.1) Gecko/20100101 Firefox/9.0.1"
+ self.req.META["HTTP_USER_AGENT"] = (
+ "Mozilla/5.0 (Windows NT 5.1; rv:9.0.1) Gecko/20100101 Firefox/9.0.1"
+ )
self.resp = HttpResponse()
self.resp.status_code = 200
self.resp.content = self.compressible_string
diff --git a/tests/migrations/test_autodetector.py b/tests/migrations/test_autodetector.py
index 85674e552ad..d0a76e10801 100644
--- a/tests/migrations/test_autodetector.py
+++ b/tests/migrations/test_autodetector.py
@@ -1310,7 +1310,7 @@ def test_add_not_null_field_with_db_default(self, mocked_ask_method):
changes, "testapp", 0, 0, name="name", preserve_default=True
)
self.assertOperationFieldAttributes(
- changes, "testapp", 0, 0, db_default=models.Value("Ada Lovelace")
+ changes, "testapp", 0, 0, db_default="Ada Lovelace"
)
@mock.patch(
@@ -1516,7 +1516,7 @@ def test_alter_field_to_not_null_with_db_default(self, mocked_ask_method):
changes, "testapp", 0, 0, name="name", preserve_default=True
)
self.assertOperationFieldAttributes(
- changes, "testapp", 0, 0, db_default=models.Value("Ada Lovelace")
+ changes, "testapp", 0, 0, db_default="Ada Lovelace"
)
@mock.patch(
diff --git a/tests/migrations/test_commands.py b/tests/migrations/test_commands.py
index a9c1cdf8938..c3830eccdbd 100644
--- a/tests/migrations/test_commands.py
+++ b/tests/migrations/test_commands.py
@@ -3102,9 +3102,11 @@ def test_optimization(self):
with open(initial_migration_file) as fp:
content = fp.read()
self.assertIn(
- '("bool", models.BooleanField'
- if HAS_BLACK
- else "('bool', models.BooleanField",
+ (
+ '("bool", models.BooleanField'
+ if HAS_BLACK
+ else "('bool', models.BooleanField"
+ ),
content,
)
self.assertEqual(
@@ -3131,9 +3133,11 @@ def test_optimization_no_verbosity(self):
with open(initial_migration_file) as fp:
content = fp.read()
self.assertIn(
- '("bool", models.BooleanField'
- if HAS_BLACK
- else "('bool', models.BooleanField",
+ (
+ '("bool", models.BooleanField'
+ if HAS_BLACK
+ else "('bool', models.BooleanField"
+ ),
content,
)
self.assertEqual(out.getvalue(), "")
diff --git a/tests/migrations/test_executor.py b/tests/migrations/test_executor.py
index 6232b44b986..571cb3e1a26 100644
--- a/tests/migrations/test_executor.py
+++ b/tests/migrations/test_executor.py
@@ -405,9 +405,9 @@ def test_custom_user(self):
migrations_apps = executor.loader.project_state(
("migrations", "0001_initial"),
).apps
- global_apps.get_app_config("migrations").models[
- "author"
- ] = migrations_apps.get_model("migrations", "author")
+ global_apps.get_app_config("migrations").models["author"] = (
+ migrations_apps.get_model("migrations", "author")
+ )
try:
migration = executor.loader.get_migration("auth", "0001_initial")
self.assertIs(executor.detect_soft_applied(None, migration)[0], True)
diff --git a/tests/migrations/test_operations.py b/tests/migrations/test_operations.py
index 670e4c0d37b..9fb8f0aa35a 100644
--- a/tests/migrations/test_operations.py
+++ b/tests/migrations/test_operations.py
@@ -1,4 +1,5 @@
import math
+from decimal import Decimal
from django.core.exceptions import FieldDoesNotExist
from django.db import IntegrityError, connection, migrations, models, transaction
@@ -1573,7 +1574,7 @@ def test_add_field_database_default(self):
self.assertEqual(len(new_state.models[app_label, "pony"].fields), 6)
field = new_state.models[app_label, "pony"].fields["height"]
self.assertEqual(field.default, models.NOT_PROVIDED)
- self.assertEqual(field.db_default, Value(4))
+ self.assertEqual(field.db_default, 4)
project_state.apps.get_model(app_label, "pony").objects.create(weight=4)
self.assertColumnNotExists(table_name, "height")
# Add field.
@@ -1624,7 +1625,7 @@ def test_add_field_database_default_special_char_escaping(self):
self.assertEqual(len(new_state.models[app_label, "pony"].fields), 6)
field = new_state.models[app_label, "pony"].fields["special_char"]
self.assertEqual(field.default, models.NOT_PROVIDED)
- self.assertEqual(field.db_default, Value(db_default))
+ self.assertEqual(field.db_default, db_default)
self.assertColumnNotExists(table_name, "special_char")
with connection.schema_editor() as editor:
operation.database_forwards(
@@ -1692,7 +1693,7 @@ def test_add_field_both_defaults(self):
self.assertEqual(len(new_state.models[app_label, "pony"].fields), 6)
field = new_state.models[app_label, "pony"].fields["height"]
self.assertEqual(field.default, 3)
- self.assertEqual(field.db_default, Value(4))
+ self.assertEqual(field.db_default, 4)
pre_pony_pk = (
project_state.apps.get_model(app_label, "pony").objects.create(weight=4).pk
)
@@ -2127,7 +2128,7 @@ def test_alter_field_add_database_default(self):
old_weight = project_state.models[app_label, "pony"].fields["weight"]
self.assertIs(old_weight.db_default, models.NOT_PROVIDED)
new_weight = new_state.models[app_label, "pony"].fields["weight"]
- self.assertEqual(new_weight.db_default, Value(4.5))
+ self.assertEqual(new_weight.db_default, 4.5)
with self.assertRaises(IntegrityError), transaction.atomic():
project_state.apps.get_model(app_label, "pony").objects.create()
# Alter field.
@@ -2169,7 +2170,7 @@ def test_alter_field_change_default_to_database_default(self):
self.assertIs(old_pink.db_default, models.NOT_PROVIDED)
new_pink = new_state.models[app_label, "pony"].fields["pink"]
self.assertIs(new_pink.default, models.NOT_PROVIDED)
- self.assertEqual(new_pink.db_default, Value(4))
+ self.assertEqual(new_pink.db_default, 4)
pony = project_state.apps.get_model(app_label, "pony").objects.create(weight=1)
self.assertEqual(pony.pink, 3)
# Alter field.
@@ -2199,7 +2200,7 @@ def test_alter_field_change_nullable_to_database_default_not_null(self):
old_green = project_state.models[app_label, "pony"].fields["green"]
self.assertIs(old_green.db_default, models.NOT_PROVIDED)
new_green = new_state.models[app_label, "pony"].fields["green"]
- self.assertEqual(new_green.db_default, Value(4))
+ self.assertEqual(new_green.db_default, 4)
old_pony = project_state.apps.get_model(app_label, "pony").objects.create(
weight=1
)
@@ -2219,6 +2220,43 @@ def test_alter_field_change_nullable_to_database_default_not_null(self):
pony = project_state.apps.get_model(app_label, "pony").objects.create(weight=1)
self.assertIsNone(pony.green)
+ def test_alter_field_change_nullable_to_decimal_database_default_not_null(self):
+ app_label = "test_alflcntdddn"
+ project_state = self.set_up_test_model(app_label)
+ operation_1 = migrations.AddField(
+ "Pony",
+ "height",
+ models.DecimalField(null=True, max_digits=5, decimal_places=2),
+ )
+ operation_2 = migrations.AlterField(
+ "Pony",
+ "height",
+ models.DecimalField(
+ max_digits=5, decimal_places=2, db_default=Decimal("12.22")
+ ),
+ )
+ table_name = f"{app_label}_pony"
+ self.assertColumnNotExists(table_name, "height")
+ # Add field.
+ new_state = project_state.clone()
+ operation_1.state_forwards(app_label, new_state)
+ with connection.schema_editor() as editor:
+ operation_1.database_forwards(app_label, editor, project_state, new_state)
+ self.assertColumnExists(table_name, "height")
+ old_pony = new_state.apps.get_model(app_label, "pony").objects.create(weight=1)
+ self.assertIsNone(old_pony.height)
+ # Alter field.
+ project_state, new_state = new_state, new_state.clone()
+ operation_2.state_forwards(app_label, new_state)
+ with connection.schema_editor() as editor:
+ operation_2.database_forwards(app_label, editor, project_state, new_state)
+ old_pony.refresh_from_db()
+ self.assertEqual(old_pony.height, Decimal("12.22"))
+ pony = new_state.apps.get_model(app_label, "pony").objects.create(weight=2)
+ if not connection.features.can_return_columns_from_insert:
+ pony.refresh_from_db()
+ self.assertEqual(pony.height, Decimal("12.22"))
+
@skipIfDBFeature("interprets_empty_strings_as_nulls")
def test_alter_field_change_blank_nullable_database_default_to_not_null(self):
app_label = "test_alflcbnddnn"
@@ -3569,15 +3607,17 @@ def test_rename_index(self):
operation.state_forwards(app_label, new_state)
# Rename index.
expected_queries = 1 if connection.features.can_rename_index else 2
- with connection.schema_editor() as editor, self.assertNumQueries(
- expected_queries
+ with (
+ connection.schema_editor() as editor,
+ self.assertNumQueries(expected_queries),
):
operation.database_forwards(app_label, editor, project_state, new_state)
self.assertIndexNameNotExists(table_name, "pony_pink_idx")
self.assertIndexNameExists(table_name, "new_pony_test_idx")
# Reversal.
- with connection.schema_editor() as editor, self.assertNumQueries(
- expected_queries
+ with (
+ connection.schema_editor() as editor,
+ self.assertNumQueries(expected_queries),
):
operation.database_backwards(app_label, editor, new_state, project_state)
self.assertIndexNameExists(table_name, "pony_pink_idx")
@@ -4343,9 +4383,10 @@ def test_add_deferred_unique_constraint(self):
)
Pony = new_state.apps.get_model(app_label, "Pony")
self.assertEqual(len(Pony._meta.constraints), 1)
- with connection.schema_editor() as editor, CaptureQueriesContext(
- connection
- ) as ctx:
+ with (
+ connection.schema_editor() as editor,
+ CaptureQueriesContext(connection) as ctx,
+ ):
operation.database_forwards(app_label, editor, project_state, new_state)
Pony.objects.create(pink=1, weight=4.0)
if connection.features.supports_deferrable_unique_constraints:
@@ -4404,9 +4445,10 @@ def test_remove_deferred_unique_constraint(self):
)
Pony = new_state.apps.get_model(app_label, "Pony")
self.assertEqual(len(Pony._meta.constraints), 0)
- with connection.schema_editor() as editor, CaptureQueriesContext(
- connection
- ) as ctx:
+ with (
+ connection.schema_editor() as editor,
+ CaptureQueriesContext(connection) as ctx,
+ ):
operation.database_forwards(app_label, editor, project_state, new_state)
# Constraint doesn't work.
Pony.objects.create(pink=1, weight=4.0)
@@ -4467,9 +4509,10 @@ def test_add_covering_unique_constraint(self):
)
Pony = new_state.apps.get_model(app_label, "Pony")
self.assertEqual(len(Pony._meta.constraints), 1)
- with connection.schema_editor() as editor, CaptureQueriesContext(
- connection
- ) as ctx:
+ with (
+ connection.schema_editor() as editor,
+ CaptureQueriesContext(connection) as ctx,
+ ):
operation.database_forwards(app_label, editor, project_state, new_state)
Pony.objects.create(pink=1, weight=4.0)
if connection.features.supports_covering_indexes:
@@ -4515,9 +4558,10 @@ def test_remove_covering_unique_constraint(self):
)
Pony = new_state.apps.get_model(app_label, "Pony")
self.assertEqual(len(Pony._meta.constraints), 0)
- with connection.schema_editor() as editor, CaptureQueriesContext(
- connection
- ) as ctx:
+ with (
+ connection.schema_editor() as editor,
+ CaptureQueriesContext(connection) as ctx,
+ ):
operation.database_forwards(app_label, editor, project_state, new_state)
# Constraint doesn't work.
Pony.objects.create(pink=1, weight=4.0)
diff --git a/tests/model_fields/models.py b/tests/model_fields/models.py
index b4b7b5bd4c5..e34f3c8947a 100644
--- a/tests/model_fields/models.py
+++ b/tests/model_fields/models.py
@@ -482,6 +482,18 @@ class UUIDGrandchild(UUIDChild):
pass
+class GeneratedModelFieldWithConverters(models.Model):
+ field = models.UUIDField()
+ field_copy = models.GeneratedField(
+ expression=F("field"),
+ output_field=models.UUIDField(),
+ db_persist=True,
+ )
+
+ class Meta:
+ required_db_features = {"supports_stored_generated_columns"}
+
+
class GeneratedModel(models.Model):
a = models.IntegerField()
b = models.IntegerField()
@@ -490,6 +502,7 @@ class GeneratedModel(models.Model):
output_field=models.IntegerField(),
db_persist=True,
)
+ fk = models.ForeignKey(Foo, on_delete=models.CASCADE, null=True, blank=True)
class Meta:
required_db_features = {"supports_stored_generated_columns"}
@@ -503,6 +516,7 @@ class GeneratedModelVirtual(models.Model):
output_field=models.IntegerField(),
db_persist=False,
)
+ fk = models.ForeignKey(Foo, on_delete=models.CASCADE, null=True, blank=True)
class Meta:
required_db_features = {"supports_virtual_generated_columns"}
diff --git a/tests/model_fields/test_generatedfield.py b/tests/model_fields/test_generatedfield.py
index 9e5d9d87c36..a636e984fdb 100644
--- a/tests/model_fields/test_generatedfield.py
+++ b/tests/model_fields/test_generatedfield.py
@@ -1,3 +1,6 @@
+import uuid
+from decimal import Decimal
+
from django.apps import apps
from django.db import IntegrityError, connection
from django.db.models import (
@@ -13,7 +16,9 @@
from django.test.utils import isolate_apps
from .models import (
+ Foo,
GeneratedModel,
+ GeneratedModelFieldWithConverters,
GeneratedModelNull,
GeneratedModelNullVirtual,
GeneratedModelOutputFieldDbCollation,
@@ -163,6 +168,14 @@ def test_unsaved_error(self):
with self.assertRaisesMessage(AttributeError, msg):
m.field
+ def test_full_clean(self):
+ m = self.base_model(a=1, b=2)
+ # full_clean() ignores GeneratedFields.
+ m.full_clean()
+ m.save()
+ m = self._refresh_if_needed(m)
+ self.assertEqual(m.field, 3)
+
def test_create(self):
m = self.base_model.objects.create(a=1, b=2)
m = self._refresh_if_needed(m)
@@ -184,6 +197,19 @@ def test_save(self):
m.refresh_from_db()
self.assertEqual(m.field, 8)
+ def test_save_model_with_foreign_key(self):
+ fk_object = Foo.objects.create(a="abc", d=Decimal("12.34"))
+ m = self.base_model(a=1, b=2, fk=fk_object)
+ m.save()
+ m = self._refresh_if_needed(m)
+ self.assertEqual(m.field, 3)
+
+ def test_generated_fields_can_be_deferred(self):
+ fk_object = Foo.objects.create(a="abc", d=Decimal("12.34"))
+ m = self.base_model.objects.create(a=1, b=2, fk=fk_object)
+ m = self.base_model.objects.defer("field").get(id=m.id)
+ self.assertEqual(m.get_deferred_fields(), {"field"})
+
def test_update(self):
m = self.base_model.objects.create(a=1, b=2)
self.base_model.objects.update(b=3)
@@ -266,6 +292,11 @@ class StoredGeneratedFieldTests(GeneratedFieldTestMixin, TestCase):
output_field_db_collation_model = GeneratedModelOutputFieldDbCollation
params_model = GeneratedModelParams
+ def test_create_field_with_db_converters(self):
+ obj = GeneratedModelFieldWithConverters.objects.create(field=uuid.uuid4())
+ obj = self._refresh_if_needed(obj)
+ self.assertEqual(obj.field, obj.field_copy)
+
@skipUnlessDBFeature("supports_virtual_generated_columns")
class VirtualGeneratedFieldTests(GeneratedFieldTestMixin, TestCase):
diff --git a/tests/model_inheritance/models.py b/tests/model_inheritance/models.py
index 47aae186e03..ffb9f28cfa6 100644
--- a/tests/model_inheritance/models.py
+++ b/tests/model_inheritance/models.py
@@ -11,6 +11,7 @@
Both styles are demonstrated here.
"""
+
from django.db import models
#
diff --git a/tests/model_inheritance/tests.py b/tests/model_inheritance/tests.py
index 5bf1ef5db54..6b005fcef0e 100644
--- a/tests/model_inheritance/tests.py
+++ b/tests/model_inheritance/tests.py
@@ -216,9 +216,11 @@ def b():
GrandChild().save()
for i, test in enumerate([a, b]):
- with self.subTest(i=i), self.assertNumQueries(4), CaptureQueriesContext(
- connection
- ) as queries:
+ with (
+ self.subTest(i=i),
+ self.assertNumQueries(4),
+ CaptureQueriesContext(connection) as queries,
+ ):
test()
for query in queries:
sql = query["sql"]
diff --git a/tests/model_inheritance_regress/tests.py b/tests/model_inheritance_regress/tests.py
index 1a9f953d6f1..ba31048ac2f 100644
--- a/tests/model_inheritance_regress/tests.py
+++ b/tests/model_inheritance_regress/tests.py
@@ -1,6 +1,7 @@
"""
Regression tests for Model inheritance behavior.
"""
+
import datetime
from operator import attrgetter
from unittest import expectedFailure
diff --git a/tests/modeladmin/test_checks.py b/tests/modeladmin/test_checks.py
index 2ed27f8a3dd..85c175d5812 100644
--- a/tests/modeladmin/test_checks.py
+++ b/tests/modeladmin/test_checks.py
@@ -322,6 +322,24 @@ class TestModelAdmin(ModelAdmin):
"admin.E020",
)
+ @isolate_apps("modeladmin")
+ def test_invalid_reverse_m2m_field_with_related_name(self):
+ class Contact(Model):
+ pass
+
+ class Customer(Model):
+ contacts = ManyToManyField("Contact", related_name="customers")
+
+ class TestModelAdmin(ModelAdmin):
+ filter_vertical = ["customers"]
+
+ self.assertIsInvalid(
+ TestModelAdmin,
+ Contact,
+ "The value of 'filter_vertical[0]' must be a many-to-many field.",
+ "admin.E020",
+ )
+
@isolate_apps("modeladmin")
def test_invalid_m2m_field_with_through(self):
class Artist(Model):
@@ -384,6 +402,24 @@ class TestModelAdmin(ModelAdmin):
"admin.E020",
)
+ @isolate_apps("modeladmin")
+ def test_invalid_reverse_m2m_field_with_related_name(self):
+ class Contact(Model):
+ pass
+
+ class Customer(Model):
+ contacts = ManyToManyField("Contact", related_name="customers")
+
+ class TestModelAdmin(ModelAdmin):
+ filter_horizontal = ["customers"]
+
+ self.assertIsInvalid(
+ TestModelAdmin,
+ Contact,
+ "The value of 'filter_horizontal[0]' must be a many-to-many field.",
+ "admin.E020",
+ )
+
@isolate_apps("modeladmin")
def test_invalid_m2m_field_with_through(self):
class Artist(Model):
diff --git a/tests/modeladmin/tests.py b/tests/modeladmin/tests.py
index f2763ecc0fb..dce91b23e41 100644
--- a/tests/modeladmin/tests.py
+++ b/tests/modeladmin/tests.py
@@ -162,6 +162,34 @@ class EmployeeProfileAdmin(ModelAdmin):
True,
)
+ @isolate_apps("modeladmin")
+ def test_lookup_allowed_for_local_fk_fields(self):
+ class Country(models.Model):
+ pass
+
+ class Place(models.Model):
+ country = models.ForeignKey(Country, models.CASCADE)
+
+ class PlaceAdmin(ModelAdmin):
+ pass
+
+ ma = PlaceAdmin(Place, self.site)
+ self.assertIs(ma.lookup_allowed("country", "1", request), True)
+
+ @isolate_apps("modeladmin")
+ def test_lookup_allowed_non_autofield_primary_key(self):
+ class Country(models.Model):
+ id = models.CharField(max_length=2, primary_key=True)
+
+ class Place(models.Model):
+ country = models.ForeignKey(Country, models.CASCADE)
+
+ class PlaceAdmin(ModelAdmin):
+ list_filter = ["country"]
+
+ ma = PlaceAdmin(Place, self.site)
+ self.assertIs(ma.lookup_allowed("country__id__exact", "DE", request), True)
+
@isolate_apps("modeladmin")
def test_lookup_allowed_foreign_primary(self):
class Country(models.Model):
diff --git a/tests/null_fk_ordering/models.py b/tests/null_fk_ordering/models.py
index adfdbf3bea4..d7b6ad82389 100644
--- a/tests/null_fk_ordering/models.py
+++ b/tests/null_fk_ordering/models.py
@@ -5,6 +5,7 @@
unexpected results
"""
+
from django.db import models
diff --git a/tests/one_to_one/models.py b/tests/one_to_one/models.py
index ca459e9edfe..20b2eeec7da 100644
--- a/tests/one_to_one/models.py
+++ b/tests/one_to_one/models.py
@@ -5,6 +5,7 @@
In this example, a ``Place`` optionally can be a ``Restaurant``.
"""
+
from django.db import models
diff --git a/tests/order_with_respect_to/base_tests.py b/tests/order_with_respect_to/base_tests.py
index ea548ca076e..5170c6d957e 100644
--- a/tests/order_with_respect_to/base_tests.py
+++ b/tests/order_with_respect_to/base_tests.py
@@ -2,6 +2,7 @@
The tests are shared with contenttypes_tests and so shouldn't import or
reference any models directly. Subclasses should inherit django.test.TestCase.
"""
+
from operator import attrgetter
@@ -117,8 +118,11 @@ def db_for_write(self, model, **hints):
return "other"
with self.settings(DATABASE_ROUTERS=[WriteToOtherRouter()]):
- with self.assertNumQueries(0, using="default"), self.assertNumQueries(
- 1,
- using="other",
+ with (
+ self.assertNumQueries(0, using="default"),
+ self.assertNumQueries(
+ 1,
+ using="other",
+ ),
):
self.q1.set_answer_order([3, 1, 2, 4])
diff --git a/tests/postgres_tests/fields.py b/tests/postgres_tests/fields.py
index 1565b5ed439..7c93d2039ed 100644
--- a/tests/postgres_tests/fields.py
+++ b/tests/postgres_tests/fields.py
@@ -2,6 +2,7 @@
Indirection layer for PostgreSQL-specific fields, so the tests don't fail when
run with a backend other than PostgreSQL.
"""
+
import enum
from django.db import models
diff --git a/tests/postgres_tests/test_constraints.py b/tests/postgres_tests/test_constraints.py
index bf478337430..e5a8e9dbe9e 100644
--- a/tests/postgres_tests/test_constraints.py
+++ b/tests/postgres_tests/test_constraints.py
@@ -300,8 +300,9 @@ def test_invalid_expressions(self):
def test_empty_expressions(self):
msg = "At least one expression is required to define an exclusion constraint."
for empty_expressions in (None, []):
- with self.subTest(empty_expressions), self.assertRaisesMessage(
- ValueError, msg
+ with (
+ self.subTest(empty_expressions),
+ self.assertRaisesMessage(ValueError, msg),
):
ExclusionConstraint(
index_type="GIST",
diff --git a/tests/postgres_tests/test_search.py b/tests/postgres_tests/test_search.py
index 6f6318899c2..472dca6c7b7 100644
--- a/tests/postgres_tests/test_search.py
+++ b/tests/postgres_tests/test_search.py
@@ -5,6 +5,7 @@
All text copyright Python (Monty) Pictures. Thanks to sacred-texts.com for the
transcript.
"""
+
from django.db.models import F, Value
from . import PostgreSQLSimpleTestCase, PostgreSQLTestCase
diff --git a/tests/prefetch_related/tests.py b/tests/prefetch_related/tests.py
index 08f87d55de1..a418beb5a52 100644
--- a/tests/prefetch_related/tests.py
+++ b/tests/prefetch_related/tests.py
@@ -1620,8 +1620,9 @@ def test_using_is_honored_custom_qs(self):
)
# Explicit using on a different db.
- with self.assertNumQueries(1, using="default"), self.assertNumQueries(
- 1, using="other"
+ with (
+ self.assertNumQueries(1, using="default"),
+ self.assertNumQueries(1, using="other"),
):
prefetch = Prefetch(
"first_time_authors", queryset=Author.objects.using("default")
diff --git a/tests/proxy_models/models.py b/tests/proxy_models/models.py
index 604136b5ac6..c0277e093fb 100644
--- a/tests/proxy_models/models.py
+++ b/tests/proxy_models/models.py
@@ -4,6 +4,7 @@
than using a new table of their own. This allows them to act as simple proxies,
providing a modified interface to the data from the base class.
"""
+
from django.db import models
# A couple of managers for testing managing overriding in proxy model cases.
diff --git a/tests/queries/models.py b/tests/queries/models.py
index 23c41e33742..9f4cf040b62 100644
--- a/tests/queries/models.py
+++ b/tests/queries/models.py
@@ -1,6 +1,7 @@
"""
Various complex queries that have been problematic in the past.
"""
+
import datetime
from django.db import models
diff --git a/tests/requests_tests/test_accept_header.py b/tests/requests_tests/test_accept_header.py
index c6eed0e4793..5afb9e99932 100644
--- a/tests/requests_tests/test_accept_header.py
+++ b/tests/requests_tests/test_accept_header.py
@@ -68,9 +68,9 @@ def test_no_headers(self):
def test_accept_headers(self):
request = HttpRequest()
- request.META[
- "HTTP_ACCEPT"
- ] = "text/html, application/xhtml+xml,application/xml ;q=0.9,*/*;q=0.8"
+ request.META["HTTP_ACCEPT"] = (
+ "text/html, application/xhtml+xml,application/xml ;q=0.9,*/*;q=0.8"
+ )
self.assertEqual(
[str(accepted_type) for accepted_type in request.accepted_types],
[
@@ -94,9 +94,9 @@ def test_request_accepts_none(self):
def test_request_accepts_some(self):
request = HttpRequest()
- request.META[
- "HTTP_ACCEPT"
- ] = "text/html,application/xhtml+xml,application/xml;q=0.9"
+ request.META["HTTP_ACCEPT"] = (
+ "text/html,application/xhtml+xml,application/xml;q=0.9"
+ )
self.assertIs(request.accepts("text/html"), True)
self.assertIs(request.accepts("application/xhtml+xml"), True)
self.assertIs(request.accepts("application/xml"), True)
diff --git a/tests/save_delete_hooks/models.py b/tests/save_delete_hooks/models.py
index e7c598aeaed..8b9826eb145 100644
--- a/tests/save_delete_hooks/models.py
+++ b/tests/save_delete_hooks/models.py
@@ -4,6 +4,7 @@
To execute arbitrary code around ``save()`` and ``delete()``, just subclass
the methods.
"""
+
from django.db import models
diff --git a/tests/schema/fields.py b/tests/schema/fields.py
index 998cb28b8cf..24a26b2c2c0 100644
--- a/tests/schema/fields.py
+++ b/tests/schema/fields.py
@@ -41,9 +41,11 @@ def __init__(
related_name=related_name,
related_query_name=related_query_name,
limit_choices_to=limit_choices_to,
- symmetrical=symmetrical
- if symmetrical is not None
- else (to == RECURSIVE_RELATIONSHIP_CONSTANT),
+ symmetrical=(
+ symmetrical
+ if symmetrical is not None
+ else (to == RECURSIVE_RELATIONSHIP_CONSTANT)
+ ),
through=through,
through_fields=through_fields,
db_constraint=db_constraint,
diff --git a/tests/schema/tests.py b/tests/schema/tests.py
index 046097366c2..465245e3e7c 100644
--- a/tests/schema/tests.py
+++ b/tests/schema/tests.py
@@ -7,6 +7,7 @@
from django.core.exceptions import FieldError
from django.core.management.color import no_style
+from django.core.serializers.json import DjangoJSONEncoder
from django.db import (
DatabaseError,
DataError,
@@ -646,9 +647,10 @@ def test_add_field(self):
# Add the new field
new_field = IntegerField(null=True)
new_field.set_attributes_from_name("age")
- with CaptureQueriesContext(
- connection
- ) as ctx, connection.schema_editor() as editor:
+ with (
+ CaptureQueriesContext(connection) as ctx,
+ connection.schema_editor() as editor,
+ ):
editor.add_field(Author, new_field)
drop_default_sql = editor.sql_alter_column_no_default % {
"column": editor.quote_name(new_field.name),
@@ -852,6 +854,27 @@ class Meta:
False,
)
+ @isolate_apps("schema")
+ @skipUnlessDBFeature("supports_virtual_generated_columns")
+ def test_add_generated_boolean_field(self):
+ class GeneratedBooleanFieldModel(Model):
+ value = IntegerField(null=True)
+ has_value = GeneratedField(
+ expression=Q(value__isnull=False),
+ output_field=BooleanField(),
+ db_persist=False,
+ )
+
+ class Meta:
+ app_label = "schema"
+
+ with connection.schema_editor() as editor:
+ editor.create_model(GeneratedBooleanFieldModel)
+ obj = GeneratedBooleanFieldModel.objects.create()
+ self.assertIs(obj.has_value, False)
+ obj = GeneratedBooleanFieldModel.objects.create(value=1)
+ self.assertIs(obj.has_value, True)
+
@isolate_apps("schema")
@skipUnlessDBFeature("supports_stored_generated_columns")
def test_add_generated_field(self):
@@ -2284,6 +2307,56 @@ class Meta:
columns = self.column_classes(Author)
self.assertEqual(columns["birth_year"][1].default, "1988")
+ @isolate_apps("schema")
+ def test_add_text_field_with_db_default(self):
+ class Author(Model):
+ description = TextField(db_default="(missing)")
+
+ class Meta:
+ app_label = "schema"
+
+ with connection.schema_editor() as editor:
+ editor.create_model(Author)
+ columns = self.column_classes(Author)
+ self.assertIn("(missing)", columns["description"][1].default)
+
+ @isolate_apps("schema")
+ def test_db_default_equivalent_sql_noop(self):
+ class Author(Model):
+ name = TextField(db_default=Value("foo"))
+
+ class Meta:
+ app_label = "schema"
+
+ with connection.schema_editor() as editor:
+ editor.create_model(Author)
+
+ new_field = TextField(db_default="foo")
+ new_field.set_attributes_from_name("name")
+ new_field.model = Author
+ with connection.schema_editor() as editor, self.assertNumQueries(0):
+ editor.alter_field(Author, Author._meta.get_field("name"), new_field)
+
+ @isolate_apps("schema")
+ def test_db_default_output_field_resolving(self):
+ class Author(Model):
+ data = JSONField(
+ encoder=DjangoJSONEncoder,
+ db_default={
+ "epoch": datetime.datetime(1970, 1, 1, tzinfo=datetime.timezone.utc)
+ },
+ )
+
+ class Meta:
+ app_label = "schema"
+
+ with connection.schema_editor() as editor:
+ editor.create_model(Author)
+
+ author = Author.objects.create()
+ author.refresh_from_db()
+ self.assertEqual(author.data, {"epoch": "1970-01-01T00:00:00Z"})
+
@skipUnlessDBFeature(
"supports_column_check_constraints", "can_introspect_check_constraints"
)
@@ -2465,9 +2538,10 @@ class Meta:
with self.assertRaises(DatabaseError):
self.column_classes(new_field.remote_field.through)
# Add the field
- with CaptureQueriesContext(
- connection
- ) as ctx, connection.schema_editor() as editor:
+ with (
+ CaptureQueriesContext(connection) as ctx,
+ connection.schema_editor() as editor,
+ ):
editor.add_field(LocalAuthorWithM2M, new_field)
# Table is not rebuilt.
self.assertEqual(
@@ -2945,9 +3019,11 @@ def test_remove_ignored_unique_constraint_not_create_fk_index(self):
)
# Redundant foreign key index is not added.
self.assertEqual(
- len(old_constraints) - 1
- if connection.features.supports_partial_indexes
- else len(old_constraints),
+ (
+ len(old_constraints) - 1
+ if connection.features.supports_partial_indexes
+ else len(old_constraints)
+ ),
len(new_constraints),
)
diff --git a/tests/serializers/models/base.py b/tests/serializers/models/base.py
index c5a4a0f5802..e4fcee366d5 100644
--- a/tests/serializers/models/base.py
+++ b/tests/serializers/models/base.py
@@ -4,6 +4,7 @@
``django.core.serializers`` provides interfaces to converting Django
``QuerySet`` objects to and from "flat" data (i.e. strings).
"""
+
from decimal import Decimal
from django.db import models
diff --git a/tests/serializers/models/data.py b/tests/serializers/models/data.py
index 3d863a3fb2b..a0e8751461a 100644
--- a/tests/serializers/models/data.py
+++ b/tests/serializers/models/data.py
@@ -4,6 +4,7 @@
NULL values, where allowed.
The basic idea is to have a model for each Django data type.
"""
+
import uuid
from django.contrib.contenttypes.fields import GenericForeignKey, GenericRelation
diff --git a/tests/serializers/models/natural.py b/tests/serializers/models/natural.py
index 1e439b34ebd..cfc57b11278 100644
--- a/tests/serializers/models/natural.py
+++ b/tests/serializers/models/natural.py
@@ -1,4 +1,5 @@
"""Models for test_natural.py"""
+
import uuid
from django.db import models
diff --git a/tests/serializers/test_data.py b/tests/serializers/test_data.py
index e1cb776d834..6361dc0c05f 100644
--- a/tests/serializers/test_data.py
+++ b/tests/serializers/test_data.py
@@ -6,6 +6,7 @@
the serializers. This includes all valid data values, plus
forward, backwards and self references.
"""
+
import datetime
import decimal
import uuid
diff --git a/tests/servers/tests.py b/tests/servers/tests.py
index ea49c11534f..05898009d53 100644
--- a/tests/servers/tests.py
+++ b/tests/servers/tests.py
@@ -1,6 +1,7 @@
"""
Tests for django.core.servers.
"""
+
import errno
import os
import socket
diff --git a/tests/sessions_tests/models.py b/tests/sessions_tests/models.py
index 6eda26f22af..93f80b347d6 100644
--- a/tests/sessions_tests/models.py
+++ b/tests/sessions_tests/models.py
@@ -3,6 +3,7 @@
real-world applications, it gives you the option of querying the database for
all active sessions for a particular account.
"""
+
from django.contrib.sessions.backends.db import SessionStore as DBStore
from django.contrib.sessions.base_session import AbstractBaseSession
from django.db import models
diff --git a/tests/signals/models.py b/tests/signals/models.py
index b758244749a..5fb9f9f772c 100644
--- a/tests/signals/models.py
+++ b/tests/signals/models.py
@@ -1,6 +1,7 @@
"""
Testing signals before/after saving and deleting.
"""
+
from django.db import models
diff --git a/tests/template_tests/test_parser.py b/tests/template_tests/test_parser.py
index e40a5611e21..eb3bb491138 100644
--- a/tests/template_tests/test_parser.py
+++ b/tests/template_tests/test_parser.py
@@ -2,6 +2,7 @@
Testing some internals of the template processing.
These are *not* examples to be copied in user code.
"""
+
from django.template import Library, TemplateSyntaxError
from django.template.base import (
FilterExpression,
diff --git a/tests/test_client/tests.py b/tests/test_client/tests.py
index 402f282588d..bfb0bd98ea9 100644
--- a/tests/test_client/tests.py
+++ b/tests/test_client/tests.py
@@ -19,6 +19,7 @@
rather than the HTML rendered to the end-user.
"""
+
import copy
import itertools
import tempfile
diff --git a/tests/test_client_regress/tests.py b/tests/test_client_regress/tests.py
index 726d1dcf8f1..bd5239ce243 100644
--- a/tests/test_client_regress/tests.py
+++ b/tests/test_client_regress/tests.py
@@ -1,6 +1,7 @@
"""
Regression tests for the Test Client, especially the customized assertions.
"""
+
import itertools
import os
diff --git a/tests/test_runner/test_discover_runner.py b/tests/test_runner/test_discover_runner.py
index 5fc35b7bf21..a845f6dd67d 100644
--- a/tests/test_runner/test_discover_runner.py
+++ b/tests/test_runner/test_discover_runner.py
@@ -658,9 +658,10 @@ def test_faulthandler_already_enabled(self, mocked_enable):
@mock.patch("faulthandler.enable")
def test_faulthandler_enabled_fileno(self, mocked_enable):
# sys.stderr that is not an actual file.
- with mock.patch(
- "faulthandler.is_enabled", return_value=False
- ), captured_stderr():
+ with (
+ mock.patch("faulthandler.is_enabled", return_value=False),
+ captured_stderr(),
+ ):
DiscoverRunner(enable_faulthandler=True)
mocked_enable.assert_called()
diff --git a/tests/test_runner/tests.py b/tests/test_runner/tests.py
index 569fd7e8621..b900ff69ea0 100644
--- a/tests/test_runner/tests.py
+++ b/tests/test_runner/tests.py
@@ -1,6 +1,7 @@
"""
Tests for django test runner
"""
+
import collections.abc
import multiprocessing
import os
@@ -992,17 +993,21 @@ def test_run_checks_raises(self):
"""
Teardown functions are run when run_checks() raises SystemCheckError.
"""
- with mock.patch(
- "django.test.runner.DiscoverRunner.setup_test_environment"
- ), mock.patch("django.test.runner.DiscoverRunner.setup_databases"), mock.patch(
- "django.test.runner.DiscoverRunner.build_suite"
- ), mock.patch(
- "django.test.runner.DiscoverRunner.run_checks", side_effect=SystemCheckError
- ), mock.patch(
- "django.test.runner.DiscoverRunner.teardown_databases"
- ) as teardown_databases, mock.patch(
- "django.test.runner.DiscoverRunner.teardown_test_environment"
- ) as teardown_test_environment:
+ with (
+ mock.patch("django.test.runner.DiscoverRunner.setup_test_environment"),
+ mock.patch("django.test.runner.DiscoverRunner.setup_databases"),
+ mock.patch("django.test.runner.DiscoverRunner.build_suite"),
+ mock.patch(
+ "django.test.runner.DiscoverRunner.run_checks",
+ side_effect=SystemCheckError,
+ ),
+ mock.patch(
+ "django.test.runner.DiscoverRunner.teardown_databases"
+ ) as teardown_databases,
+ mock.patch(
+ "django.test.runner.DiscoverRunner.teardown_test_environment"
+ ) as teardown_test_environment,
+ ):
runner = DiscoverRunner(verbosity=0, interactive=False)
with self.assertRaises(SystemCheckError):
runner.run_tests(
@@ -1016,18 +1021,22 @@ def test_run_checks_raises_and_teardown_raises(self):
SystemCheckError is surfaced when run_checks() raises SystemCheckError
and teardown databases() raises ValueError.
"""
- with mock.patch(
- "django.test.runner.DiscoverRunner.setup_test_environment"
- ), mock.patch("django.test.runner.DiscoverRunner.setup_databases"), mock.patch(
- "django.test.runner.DiscoverRunner.build_suite"
- ), mock.patch(
- "django.test.runner.DiscoverRunner.run_checks", side_effect=SystemCheckError
- ), mock.patch(
- "django.test.runner.DiscoverRunner.teardown_databases",
- side_effect=ValueError,
- ) as teardown_databases, mock.patch(
- "django.test.runner.DiscoverRunner.teardown_test_environment"
- ) as teardown_test_environment:
+ with (
+ mock.patch("django.test.runner.DiscoverRunner.setup_test_environment"),
+ mock.patch("django.test.runner.DiscoverRunner.setup_databases"),
+ mock.patch("django.test.runner.DiscoverRunner.build_suite"),
+ mock.patch(
+ "django.test.runner.DiscoverRunner.run_checks",
+ side_effect=SystemCheckError,
+ ),
+ mock.patch(
+ "django.test.runner.DiscoverRunner.teardown_databases",
+ side_effect=ValueError,
+ ) as teardown_databases,
+ mock.patch(
+ "django.test.runner.DiscoverRunner.teardown_test_environment"
+ ) as teardown_test_environment,
+ ):
runner = DiscoverRunner(verbosity=0, interactive=False)
with self.assertRaises(SystemCheckError):
runner.run_tests(
@@ -1041,18 +1050,19 @@ def test_run_checks_passes_and_teardown_raises(self):
Exceptions on teardown are surfaced if no exceptions happen during
run_checks().
"""
- with mock.patch(
- "django.test.runner.DiscoverRunner.setup_test_environment"
- ), mock.patch("django.test.runner.DiscoverRunner.setup_databases"), mock.patch(
- "django.test.runner.DiscoverRunner.build_suite"
- ), mock.patch(
- "django.test.runner.DiscoverRunner.run_checks"
- ), mock.patch(
- "django.test.runner.DiscoverRunner.teardown_databases",
- side_effect=ValueError,
- ) as teardown_databases, mock.patch(
- "django.test.runner.DiscoverRunner.teardown_test_environment"
- ) as teardown_test_environment:
+ with (
+ mock.patch("django.test.runner.DiscoverRunner.setup_test_environment"),
+ mock.patch("django.test.runner.DiscoverRunner.setup_databases"),
+ mock.patch("django.test.runner.DiscoverRunner.build_suite"),
+ mock.patch("django.test.runner.DiscoverRunner.run_checks"),
+ mock.patch(
+ "django.test.runner.DiscoverRunner.teardown_databases",
+ side_effect=ValueError,
+ ) as teardown_databases,
+ mock.patch(
+ "django.test.runner.DiscoverRunner.teardown_test_environment"
+ ) as teardown_test_environment,
+ ):
runner = DiscoverRunner(verbosity=0, interactive=False)
with self.assertRaises(ValueError):
# Suppress the output when running TestDjangoTestCase.
diff --git a/tests/transactions/models.py b/tests/transactions/models.py
index 9506bace351..3d4c7d7e449 100644
--- a/tests/transactions/models.py
+++ b/tests/transactions/models.py
@@ -6,6 +6,7 @@
commit-on-success behavior. Alternatively, you can manage the transaction
manually.
"""
+
from django.db import models
diff --git a/tests/urlpatterns_reverse/tests.py b/tests/urlpatterns_reverse/tests.py
index 89dfd0deba4..8384f55b3c5 100644
--- a/tests/urlpatterns_reverse/tests.py
+++ b/tests/urlpatterns_reverse/tests.py
@@ -1,6 +1,7 @@
"""
Unit tests for reverse URL lookups.
"""
+
import pickle
import sys
import threading
diff --git a/tests/urls.py b/tests/urls.py
index 7d3a3a790ae..67c4d4091b4 100644
--- a/tests/urls.py
+++ b/tests/urls.py
@@ -3,5 +3,4 @@
This helps the tests remain isolated.
"""
-
urlpatterns = []
diff --git a/tests/utils_tests/test_datastructures.py b/tests/utils_tests/test_datastructures.py
index 07229f25ed9..d5d90b68fc0 100644
--- a/tests/utils_tests/test_datastructures.py
+++ b/tests/utils_tests/test_datastructures.py
@@ -1,6 +1,7 @@
"""
Tests for stuff in django.utils.datastructures.
"""
+
import collections.abc
import copy
import pickle
diff --git a/tests/utils_tests/test_jslex.py b/tests/utils_tests/test_jslex.py
index ee13eb4d64e..59551930c6f 100644
--- a/tests/utils_tests/test_jslex.py
+++ b/tests/utils_tests/test_jslex.py
@@ -1,4 +1,5 @@
"""Tests for jslex."""
+
# originally from https://bitbucket.org/ned/jslex
from django.test import SimpleTestCase
diff --git a/tests/view_tests/tests/test_debug.py b/tests/view_tests/tests/test_debug.py
index 65f9db89bfe..e1d7d296545 100644
--- a/tests/view_tests/tests/test_debug.py
+++ b/tests/view_tests/tests/test_debug.py
@@ -332,14 +332,19 @@ def test_template_loader_postmortem(self):
with tempfile.NamedTemporaryFile(prefix=template_name) as tmpfile:
tempdir = os.path.dirname(tmpfile.name)
template_path = os.path.join(tempdir, template_name)
- with override_settings(
- TEMPLATES=[
- {
- "BACKEND": "django.template.backends.django.DjangoTemplates",
- "DIRS": [tempdir],
- }
- ]
- ), self.assertLogs("django.request", "ERROR"):
+ with (
+ override_settings(
+ TEMPLATES=[
+ {
+ "BACKEND": (
+ "django.template.backends.django.DjangoTemplates"
+ ),
+ "DIRS": [tempdir],
+ }
+ ]
+ ),
+ self.assertLogs("django.request", "ERROR"),
+ ):
response = self.client.get(
reverse(
"raises_template_does_not_exist", kwargs={"path": template_name}