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
6 changes: 3 additions & 3 deletions django/conf/locale/fr_CA/formats.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,12 @@
# The *_FORMAT strings use the Django date format syntax,
# see https://docs.djangoproject.com/en/dev/ref/templates/builtins/#date
DATE_FORMAT = "j F Y" # 31 janvier 2024
TIME_FORMAT = "H\xa0h\xa0i" # 13 h 40
DATETIME_FORMAT = "j F Y, H\xa0h\xa0i" # 31 janvier 2024, 13 h 40
TIME_FORMAT = "H\xa0\\h\xa0i" # 13 h 40
DATETIME_FORMAT = "j F Y, H\xa0\\h\xa0i" # 31 janvier 2024, 13 h 40
YEAR_MONTH_FORMAT = "F Y"
MONTH_DAY_FORMAT = "j F"
SHORT_DATE_FORMAT = "Y-m-d"
SHORT_DATETIME_FORMAT = "Y-m-d H\xa0h\xa0i"
SHORT_DATETIME_FORMAT = "Y-m-d H\xa0\\h\xa0i"
FIRST_DAY_OF_WEEK = 0 # Dimanche

# The *_INPUT_FORMATS strings use the Python strftime format syntax,
Expand Down
11 changes: 11 additions & 0 deletions django/db/backends/base/operations.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
from django.db import NotSupportedError, transaction
from django.db.models.expressions import Col
from django.utils import timezone
from django.utils.duration import duration_microseconds
from django.utils.encoding import force_str


Expand Down Expand Up @@ -564,6 +565,16 @@ def adapt_datetimefield_value(self, value):
return None
return str(value)

def adapt_durationfield_value(self, value):
"""
Transform a timedelta value into an object compatible with what is
expected by the backend driver for duration columns (by default,
an integer of microseconds).
"""
if value is None:
return None
return duration_microseconds(value)

def adapt_timefield_value(self, value):
"""
Transform a time value to an object compatible with what is expected
Expand Down
3 changes: 3 additions & 0 deletions django/db/backends/oracle/operations.py
Original file line number Diff line number Diff line change
Expand Up @@ -608,6 +608,9 @@ def adapt_datetimefield_value(self, value):

return Oracle_datetime.from_datetime(value)

def adapt_durationfield_value(self, value):
return value

def adapt_timefield_value(self, value):
if value is None:
return None
Expand Down
3 changes: 3 additions & 0 deletions django/db/backends/postgresql/operations.py
Original file line number Diff line number Diff line change
Expand Up @@ -330,6 +330,9 @@ def adapt_datefield_value(self, value):
def adapt_datetimefield_value(self, value):
return value

def adapt_durationfield_value(self, value):
return value

def adapt_timefield_value(self, value):
return value

Expand Down
5 changes: 4 additions & 1 deletion django/db/models/deletion.py
Original file line number Diff line number Diff line change
Expand Up @@ -309,7 +309,10 @@ def collect(
protected_objects = defaultdict(list)
for related in get_candidate_relations_to_delete(model._meta):
# Preserve parent reverse relationships if keep_parents=True.
if keep_parents and related.model in model._meta.all_parents:
if (
keep_parents
and related.model._meta.concrete_model in model._meta.all_parents
):
continue
field = related.field
on_delete = field.remote_field.on_delete
Expand Down
8 changes: 2 additions & 6 deletions django/db/models/fields/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@
parse_duration,
parse_time,
)
from django.utils.duration import duration_microseconds, duration_string
from django.utils.duration import duration_string
from django.utils.functional import Promise, cached_property
from django.utils.ipv6 import MAX_IPV6_ADDRESS_LENGTH, clean_ipv6_address
from django.utils.text import capfirst
Expand Down Expand Up @@ -1890,11 +1890,7 @@ def to_python(self, value):
)

def get_db_prep_value(self, value, connection, prepared=False):
if connection.features.has_native_duration_field:
return value
if value is None:
return None
return duration_microseconds(value)
return connection.ops.adapt_durationfield_value(value)

def get_db_converters(self, connection):
converters = []
Expand Down
22 changes: 11 additions & 11 deletions docs/ref/contrib/postgres/search.txt
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ single column in the database. For example:
.. code-block:: pycon

>>> Entry.objects.filter(body_text__search="Cheese")
[<Entry: Cheese on Toast recipes>, <Entry: Pizza Recipes>]
<QuerySet [<Entry: Cheese on Toast recipes>, <Entry: Pizza Recipes>]>

This creates a ``to_tsvector`` in the database from the ``body_text`` field
and a ``plainto_tsquery`` from the search term ``'Cheese'``, both using the
Expand All @@ -52,7 +52,7 @@ To query against both fields, use a ``SearchVector``:
>>> Entry.objects.annotate(
... search=SearchVector("body_text", "blog__tagline"),
... ).filter(search="Cheese")
[<Entry: Cheese on Toast recipes>, <Entry: Pizza Recipes>]
<QuerySet [<Entry: Cheese on Toast recipes>, <Entry: Pizza Recipes>]>

The arguments to ``SearchVector`` can be any
:class:`~django.db.models.Expression` or the name of a field. Multiple
Expand All @@ -67,7 +67,7 @@ For example:
>>> Entry.objects.annotate(
... search=SearchVector("body_text") + SearchVector("blog__tagline"),
... ).filter(search="Cheese")
[<Entry: Cheese on Toast recipes>, <Entry: Pizza Recipes>]
<QuerySet [<Entry: Cheese on Toast recipes>, <Entry: Pizza Recipes>]>

See :ref:`postgresql-fts-search-configuration` and
:ref:`postgresql-fts-weighting-queries` for an explanation of the ``config``
Expand Down Expand Up @@ -142,7 +142,7 @@ order by relevancy:
>>> vector = SearchVector("body_text")
>>> query = SearchQuery("cheese")
>>> Entry.objects.annotate(rank=SearchRank(vector, query)).order_by("-rank")
[<Entry: Cheese on Toast recipes>, <Entry: Pizza recipes>]
<QuerySet [<Entry: Cheese on Toast recipes>, <Entry: Pizza recipes>]>

See :ref:`postgresql-fts-weighting-queries` for an explanation of the
``weights`` parameter.
Expand Down Expand Up @@ -240,7 +240,7 @@ different language parsers and dictionaries as defined by the database:
>>> Entry.objects.annotate(
... search=SearchVector("body_text", config="french"),
... ).filter(search=SearchQuery("œuf", config="french"))
[<Entry: Pain perdu>]
<QuerySet [<Entry: Pain perdu>]>

The value of ``config`` could also be stored in another column:

Expand All @@ -250,7 +250,7 @@ The value of ``config`` could also be stored in another column:
>>> Entry.objects.annotate(
... search=SearchVector("body_text", config=F("blog__language")),
... ).filter(search=SearchQuery("œuf", config=F("blog__language")))
[<Entry: Pain perdu>]
<QuerySet [<Entry: Pain perdu>]>

.. _postgresql-fts-weighting-queries:

Expand Down Expand Up @@ -364,7 +364,7 @@ if it were an annotated ``SearchVector``:

>>> Entry.objects.update(search_vector=SearchVector("body_text"))
>>> Entry.objects.filter(search_vector="cheese")
[<Entry: Cheese on Toast recipes>, <Entry: Pizza recipes>]
<QuerySet [<Entry: Cheese on Toast recipes>, <Entry: Pizza recipes>]>

.. _PostgreSQL documentation: https://www.postgresql.org/docs/current/textsearch-features.html#TEXTSEARCH-UPDATE-TRIGGERS

Expand Down Expand Up @@ -403,7 +403,7 @@ Usage example:
... ).filter(
... similarity__gt=0.3
... ).order_by("-similarity")
[<Author: Katy Stevens>, <Author: Stephen Keats>]
<QuerySet [<Author: Katy Stevens>, <Author: Stephen Keats>]>

``TrigramWordSimilarity``
-------------------------
Expand All @@ -426,7 +426,7 @@ Usage example:
... ).filter(
... similarity__gt=0.3
... ).order_by("-similarity")
[<Author: Katy Stevens>]
<QuerySet [<Author: Katy Stevens>]>

``TrigramStrictWordSimilarity``
-------------------------------
Expand Down Expand Up @@ -459,7 +459,7 @@ Usage example:
... ).filter(
... distance__lte=0.7
... ).order_by("distance")
[<Author: Katy Stevens>, <Author: Stephen Keats>]
<QuerySet [<Author: Katy Stevens>, <Author: Stephen Keats>]>

``TrigramWordDistance``
-----------------------
Expand All @@ -482,7 +482,7 @@ Usage example:
... ).filter(
... distance__lte=0.7
... ).order_by("distance")
[<Author: Katy Stevens>]
<QuerySet [<Author: Katy Stevens>]>

``TrigramStrictWordDistance``
-----------------------------
Expand Down
4 changes: 3 additions & 1 deletion docs/releases/6.1.txt
Original file line number Diff line number Diff line change
Expand Up @@ -243,7 +243,9 @@ Database backend API
This section describes changes that may be needed in third-party database
backends.

* ...
* The ``DatabaseOperations.adapt_durationfield_value()`` hook is added. If the
database has native support for ``DurationField``, override this method to
simply return the value.

Miscellaneous
-------------
Expand Down
7 changes: 6 additions & 1 deletion tests/delete/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,11 @@ class RChild(R):
pass


class RProxy(R):
class Meta:
proxy = True


class RChildChild(RChild):
pass

Expand Down Expand Up @@ -179,7 +184,7 @@ class RelToBase(models.Model):


class Origin(models.Model):
pass
r_proxy = models.ForeignKey("RProxy", models.CASCADE, null=True)


class Referrer(models.Model):
Expand Down
9 changes: 9 additions & 0 deletions tests/delete/tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
RChild,
RChildChild,
Referrer,
RProxy,
S,
T,
User,
Expand Down Expand Up @@ -675,6 +676,14 @@ def receiver(instance, **kwargs):
)
signal.disconnect(receiver, sender=Referrer)

def test_keep_parents_does_not_delete_proxy_related(self):
r_child = RChild.objects.create()
r_proxy = RProxy.objects.get(pk=r_child.pk)
Origin.objects.create(r_proxy=r_proxy)
self.assertEqual(Origin.objects.count(), 1)
r_child.delete(keep_parents=True)
self.assertEqual(Origin.objects.count(), 1)


class FastDeleteTests(TestCase):
def test_fast_delete_all(self):
Expand Down
21 changes: 21 additions & 0 deletions tests/i18n/tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -1158,6 +1158,27 @@ def test_l10n_enabled(self):
),
)

def test_uncommon_locale_formats(self):
testcases = {
# French Canadian locale uses 'h' as time format seperator.
("fr-ca", time_format, (self.t, "TIME_FORMAT")): "10\xa0h\xa015",
(
"fr-ca",
date_format,
(self.dt, "DATETIME_FORMAT"),
): "31 décembre 2009, 20\xa0h\xa050",
(
"fr-ca",
date_format,
(self.dt, "SHORT_DATETIME_FORMAT"),
): "2009-12-31 20\xa0h\xa050",
}
for testcase, expected in testcases.items():
locale, format_function, format_args = testcase
with self.subTest(locale=locale, expected=expected):
with translation.override(locale, deactivate=True):
self.assertEqual(expected, format_function(*format_args))

def test_sub_locales(self):
"""
Check if sublocales fall back to the main locale
Expand Down
Loading