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

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .github/workflows/benchmark.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ jobs:
Run_benchmarks:
if: contains(github.event.pull_request.labels.*.name, 'benchmark')
runs-on: ubuntu-latest
timeout-minutes: 60
steps:
- name: Checkout Benchmark Repo
uses: actions/checkout@v5
Expand Down
1 change: 1 addition & 0 deletions .github/workflows/check_commit_messages.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ jobs:
check-commit-prefix:
if: startsWith(github.event.pull_request.base.ref, 'stable/')
runs-on: ubuntu-latest
timeout-minutes: 60
steps:
- uses: actions/checkout@v5
with:
Expand Down
3 changes: 3 additions & 0 deletions .github/workflows/docs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ jobs:
docs:
runs-on: ubuntu-24.04
name: spelling
timeout-minutes: 60
steps:
- name: Checkout
uses: actions/checkout@v5
Expand All @@ -43,6 +44,7 @@ jobs:
blacken-docs:
runs-on: ubuntu-latest
name: blacken-docs
timeout-minutes: 60
steps:
- name: Checkout
uses: actions/checkout@v5
Expand All @@ -66,6 +68,7 @@ jobs:
lint-docs:
runs-on: ubuntu-latest
name: lint-docs
timeout-minutes: 60
steps:
- name: Checkout
uses: actions/checkout@v5
Expand Down
1 change: 1 addition & 0 deletions .github/workflows/labels.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ jobs:
if: github.repository == 'django/django'
name: "Flag if no Trac ticket is found in the title"
runs-on: ubuntu-latest
timeout-minutes: 60
steps:
- uses: actions/checkout@v5
with:
Expand Down
4 changes: 4 additions & 0 deletions .github/workflows/linters.yml
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ jobs:
flake8:
name: flake8
runs-on: ubuntu-latest
timeout-minutes: 60
steps:
- name: Checkout
uses: actions/checkout@v5
Expand All @@ -40,6 +41,7 @@ jobs:

isort:
runs-on: ubuntu-latest
timeout-minutes: 60
steps:
- name: Checkout
uses: actions/checkout@v5
Expand All @@ -59,6 +61,7 @@ jobs:

black:
runs-on: ubuntu-latest
timeout-minutes: 60
steps:
- name: Checkout
uses: actions/checkout@v5
Expand All @@ -69,6 +72,7 @@ jobs:

zizmor:
runs-on: ubuntu-latest
timeout-minutes: 60
steps:
- name: Checkout
uses: actions/checkout@v5
Expand Down
1 change: 1 addition & 0 deletions .github/workflows/new_contributor_pr.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ jobs:
if: github.repository == 'django/django'
name: Hello new contributor
runs-on: ubuntu-latest
timeout-minutes: 60
steps:
# Pin to v1: https://github.com/actions/first-interaction/issues/369
- uses: actions/first-interaction@v1
Expand Down
1 change: 1 addition & 0 deletions .github/workflows/postgis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ jobs:
--health-interval 10s
--health-timeout 5s
--health-retries 5
timeout-minutes: 60
steps:
- name: Checkout
uses: actions/checkout@v5
Expand Down
2 changes: 2 additions & 0 deletions .github/workflows/python_matrix.yml
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ jobs:
runs-on: ubuntu-latest
outputs:
python_versions_output: ${{ steps.set-matrix.outputs.python_versions }}
timeout-minutes: 60
steps:
- name: Checkout code
uses: actions/checkout@v5
Expand All @@ -36,6 +37,7 @@ jobs:
strategy:
matrix:
python-version: ${{ fromJson(needs.define-matrix.outputs.python_versions_output) }}
timeout-minutes: 60
steps:
- name: Checkout
uses: actions/checkout@v5
Expand Down
6 changes: 6 additions & 0 deletions .github/workflows/schedule_tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ jobs:
- '3.14'
- '3.15-dev'
name: Windows, SQLite, Python ${{ matrix.python-version }}
timeout-minutes: 60
continue-on-error: true
steps:
- name: Checkout
Expand All @@ -42,6 +43,7 @@ jobs:
pyc-only:
runs-on: ubuntu-latest
name: Byte-compiled Django with no source files (only .pyc files)
timeout-minutes: 60
steps:
- name: Checkout
uses: actions/checkout@v5
Expand Down Expand Up @@ -70,6 +72,7 @@ jobs:
javascript-tests:
runs-on: ubuntu-latest
name: JavaScript tests
timeout-minutes: 60
steps:
- name: Checkout
uses: actions/checkout@v5
Expand All @@ -87,6 +90,7 @@ jobs:
selenium-sqlite:
runs-on: ubuntu-latest
name: Selenium tests, SQLite
timeout-minutes: 60
steps:
- name: Checkout
uses: actions/checkout@v5
Expand All @@ -111,6 +115,7 @@ jobs:
selenium-postgresql:
runs-on: ubuntu-latest
name: Selenium tests, PostgreSQL
timeout-minutes: 60
services:
postgres:
image: postgres:15-alpine
Expand Down Expand Up @@ -156,6 +161,7 @@ jobs:
server_side_bindings: [0, 1]
runs-on: ubuntu-latest
name: PostgreSQL Versions
timeout-minutes: 60
env:
SERVER_SIDE_BINDING: ${{ matrix.server_side_bindings }}
services:
Expand Down
1 change: 1 addition & 0 deletions .github/workflows/schedules.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ jobs:
runs-on: ubuntu-latest
environment: schedules
name: Trigger Full Build
timeout-minutes: 60
# Only trigger on the main Django repository
if: (github.event_name == 'schedule' && github.repository == 'django/django') || (github.event_name != 'schedule')
strategy:
Expand Down
1 change: 1 addition & 0 deletions .github/workflows/screenshots.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ jobs:
if: contains(join(github.event.pull_request.labels.*.name, '|'), 'screenshots')
runs-on: ubuntu-latest
name: Screenshots
timeout-minutes: 60
steps:
- name: Checkout
uses: actions/checkout@v5
Expand Down
2 changes: 2 additions & 0 deletions .github/workflows/selenium.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ jobs:
if: contains(github.event.pull_request.labels.*.name, 'selenium')
runs-on: ubuntu-latest
name: SQLite
timeout-minutes: 60
steps:
- name: Checkout
uses: actions/checkout@v5
Expand All @@ -43,6 +44,7 @@ jobs:
if: contains(github.event.pull_request.labels.*.name, 'selenium')
runs-on: ubuntu-latest
name: PostgreSQL
timeout-minutes: 60
services:
postgres:
image: postgres:15-alpine
Expand Down
2 changes: 2 additions & 0 deletions .github/workflows/tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ jobs:
python-version:
- '3.14'
name: Windows, SQLite, Python ${{ matrix.python-version }}
timeout-minutes: 60
steps:
- name: Checkout
uses: actions/checkout@v5
Expand All @@ -45,6 +46,7 @@ jobs:
javascript-tests:
runs-on: ubuntu-latest
name: JavaScript tests
timeout-minutes: 60
steps:
- name: Checkout
uses: actions/checkout@v5
Expand Down
12 changes: 12 additions & 0 deletions docs/ref/models/expressions.txt
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,12 @@ determine the result's field type, such as complex expressions that mix field
types. For example, adding a ``DecimalField()`` and a ``FloatField()`` requires
an output field, like ``output_field=FloatField()``.

``output_field`` also allows using custom fields that perform type conversions
outside a specific model field context. For example, if you frequently need to
perform date arithmetic with ``timedelta``, you can create a custom field that
handles the conversion, ensuring consistent results across databases. See
:doc:`/howto/custom-model-fields`.

Some examples
=============

Expand Down Expand Up @@ -575,6 +581,12 @@ available on other expressions. ``ExpressionWrapper`` is necessary when using
arithmetic on ``F()`` expressions with different types as described in
:ref:`using-f-with-annotations`.

.. admonition:: Database casting not performed

``ExpressionWrapper`` only sets the output field for the ORM and does not
perform any database-level casting. To ensure a specific type is returned
from the database, use :class:`~django.db.models.functions.Cast` instead.

``JSONNull()`` expression
-------------------------

Expand Down
33 changes: 33 additions & 0 deletions docs/ref/models/fields.txt
Original file line number Diff line number Diff line change
Expand Up @@ -831,6 +831,39 @@ exclusive. Any combination of these options will result in an error.
instance, removing its time component. This is true for both storage and
comparison.

.. warning::

On PostgreSQL and MySQL, arithmetic operations on a ``DateField`` with a
:class:`~datetime.timedelta` return a ``datetime`` instead of a ``date``.
This occurs because Python's ``timedelta`` is converted to SQL
``INTERVAL``, and the SQL operation ``date +/- interval`` returns a
``timestamp`` on these databases.

To ensure a ``date`` result, use one of the following approaches. Either
explicitly cast the result to a date::

import datetime
from django.db.models import DateField, F
from django.db.models.functions import Cast

qs = MyModel.objects.annotate(
previous_day=Cast(
F("date_field") - datetime.timedelta(days=1),
output_field=DateField(),
)
)

Or on PostgreSQL only, use integer arithmetic to represent days::

from django.db.models import DateField, ExpressionWrapper, F

qs = MyModel.objects.annotate(
previous_day=ExpressionWrapper(
F("date_field") - 1, # Subtract 1 day as integer
output_field=DateField(),
)
)

``DateTimeField``
-----------------

Expand Down
22 changes: 22 additions & 0 deletions eslint-recommended.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,27 @@
/* https://github.com/eslint/eslint/blob/v9.36.0/packages/js/src/configs/eslint-recommended.js */

/*
Copyright OpenJS Foundation and other contributors, <www.openjsf.org>

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/

/**
* @fileoverview Configuration applied when a user configuration extends from
* eslint:recommended.
Expand Down
12 changes: 12 additions & 0 deletions globals.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,17 @@
/* https://github.com/sindresorhus/globals/blob/v16.4.0/globals.json */

/*
MIT License

Copyright (c) Sindre Sorhus <sindresorhus@gmail.com> (https://sindresorhus.com)

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/

"use strict";


Expand Down
2 changes: 2 additions & 0 deletions tests/file_storage/tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -671,9 +671,11 @@ def test_save_overwrite_behavior_temp_file(self):
content_1 = b"content one"
content_2 = b"second content"
f_1 = TemporaryUploadedFile("tmp1", "text/plain", 11, "utf8")
self.addCleanup(f_1.close)
f_1.write(content_1)
f_1.seek(0)
f_2 = TemporaryUploadedFile("tmp2", "text/plain", 14, "utf8")
self.addCleanup(f_2.close)
f_2.write(content_2)
f_2.seek(0)
stored_name_1 = self.storage.save(name, f_1)
Expand Down