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: 1 addition & 5 deletions django/contrib/gis/forms/fields.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
from django import forms
from django.contrib.gis.gdal import GDALException
from django.contrib.gis.geos import GEOSException, GEOSGeometry
from django.core.exceptions import ValidationError
from django.utils.translation import gettext_lazy as _
Expand Down Expand Up @@ -41,10 +40,7 @@ def to_python(self, value):

if not isinstance(value, GEOSGeometry):
if hasattr(self.widget, "deserialize"):
try:
value = self.widget.deserialize(value)
except GDALException:
value = None
value = self.widget.deserialize(value)
else:
try:
value = GEOSGeometry(value)
Expand Down
3 changes: 2 additions & 1 deletion django/contrib/gis/forms/widgets.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import logging

from django.contrib.gis import gdal
from django.contrib.gis.gdal import GDALException
from django.contrib.gis.geometry import json_regex
from django.contrib.gis.geos import GEOSException, GEOSGeometry
from django.forms.widgets import Widget
Expand Down Expand Up @@ -36,7 +37,7 @@ def serialize(self, value):
def deserialize(self, value):
try:
return GEOSGeometry(value)
except (GEOSException, ValueError, TypeError) as err:
except (GEOSException, GDALException, ValueError, TypeError) as err:
logger.error("Error creating geometry from value '%s' (%s)", value, err)
return None

Expand Down
5 changes: 3 additions & 2 deletions django/db/models/query.py
Original file line number Diff line number Diff line change
Expand Up @@ -1158,7 +1158,7 @@ async def alatest(self, *fields):

def first(self):
"""Return the first object of a query or None if no match is found."""
if self.ordered:
if self.ordered or not self.query.default_ordering:
queryset = self
else:
self._check_ordering_first_last_queryset_aggregation(method="first")
Expand All @@ -1171,7 +1171,7 @@ async def afirst(self):

def last(self):
"""Return the last object of a query or None if no match is found."""
if self.ordered:
if self.ordered or not self.query.default_ordering:
queryset = self.reverse()
else:
self._check_ordering_first_last_queryset_aggregation(method="last")
Expand Down Expand Up @@ -1679,6 +1679,7 @@ def _combinator_query(self, combinator, *other_qs, all=False):
clone = self._chain()
# Clear limits and ordering so they can be reapplied
clone.query.clear_ordering(force=True)
clone.query.default_ordering = True
clone.query.clear_limits()
clone.query.combined_queries = (self.query, *(qs.query for qs in other_qs))
clone.query.combinator = combinator
Expand Down
8 changes: 8 additions & 0 deletions tests/get_earliest_or_latest/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,14 @@ class Comment(models.Model):
likes_count = models.PositiveIntegerField()


class OrderedArticle(models.Model):
headline = models.CharField(max_length=100)
pub_date = models.DateField()

class Meta:
ordering = ["headline"]


# Ticket #23555 - model with an intentionally broken QuerySet.__iter__ method.


Expand Down
30 changes: 29 additions & 1 deletion tests/get_earliest_or_latest/tests.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
from datetime import datetime
from unittest.mock import patch

from django.db.models import Avg
from django.test import TestCase

from .models import Article, Comment, IndexErrorArticle, Person
from .models import Article, Comment, IndexErrorArticle, OrderedArticle, Person


class EarliestOrLatestTests(TestCase):
Expand Down Expand Up @@ -265,3 +266,30 @@ def test_first_last_unordered_qs_aggregation_error(self):
qs.first()
with self.assertRaisesMessage(TypeError, msg % "last"):
qs.last()

def test_first_last_empty_order_by_has_no_pk_ordering(self):
Article.objects.create(
headline="Article 1",
pub_date=datetime(2006, 9, 10),
expire_date=datetime(2056, 9, 11),
)

qs = Article.objects.order_by()
with patch.object(type(qs), "order_by") as mock_order_by:
qs.first()
mock_order_by.assert_not_called()
qs.last()
mock_order_by.assert_not_called()

def test_first_last_empty_order_by_clears_default_ordering(self):
OrderedArticle.objects.create(
headline="Article 1",
pub_date=datetime(2006, 9, 10),
)

qs = OrderedArticle.objects.order_by()
with patch.object(type(qs), "order_by") as mock_order_by:
qs.first()
mock_order_by.assert_not_called()
qs.last()
mock_order_by.assert_not_called()
13 changes: 13 additions & 0 deletions tests/gis_tests/test_geoforms.py
Original file line number Diff line number Diff line change
Expand Up @@ -494,6 +494,19 @@ def test_get_context_attrs(self):
context = widget.get_context("geometry", None, None)
self.assertEqual(context["widget"]["attrs"]["geom_name"], "Geometry")

def test_invalid_values(self):
bad_inputs = [
"POINT(5)",
"MULTI POLYGON(((0 0, 0 1, 1 1, 1 0, 0 0)))",
"BLAH(0 0, 1 1)",
'{"type": "FeatureCollection", "features": ['
'{"geometry": {"type": "Point", "coordinates": [508375, 148905]}, '
'"type": "Feature"}]}',
]
for input in bad_inputs:
with self.subTest(input=input):
self.assertIsNone(BaseGeometryWidget().deserialize(input))

def test_subwidgets(self):
widget = forms.BaseGeometryWidget()
self.assertEqual(
Expand Down
9 changes: 9 additions & 0 deletions tests/queries/test_qs_combinators.py
Original file line number Diff line number Diff line change
Expand Up @@ -418,6 +418,15 @@ def test_union_with_first(self):
qs2 = base_qs.filter(name="a2")
self.assertEqual(qs1.union(qs2).first(), a1)

@skipUnlessDBFeature("supports_slicing_ordering_in_compound")
def test_union_applies_default_ordering_afterward(self):
c = Tag.objects.create(name="C")
Tag.objects.create(name="B")
a = Tag.objects.create(name="A")
qs1 = Tag.objects.filter(name__in=["A", "B"])[:1]
qs2 = Tag.objects.filter(name__in=["C"])[:1]
self.assertSequenceEqual(qs1.union(qs2), [a, c])

def test_union_multiple_models_with_values_list_and_order(self):
reserved_name = ReservedName.objects.create(name="rn1", order=0)
qs1 = Celebrity.objects.all()
Expand Down