Skip to content

Commit

Permalink
Merge remote-tracking branch 'upstream/main'
Browse files Browse the repository at this point in the history
  • Loading branch information
dan-mm committed Dec 27, 2023
2 parents 5820296 + 751d732 commit dd9cc39
Show file tree
Hide file tree
Showing 8 changed files with 197 additions and 4 deletions.
2 changes: 1 addition & 1 deletion django/contrib/admin/checks.py
Original file line number Diff line number Diff line change
Expand Up @@ -532,7 +532,7 @@ def _check_filter_item(self, obj, field_name, label):
field=field_name, option=label, obj=obj, id="admin.E019"
)
else:
if not field.many_to_many:
if not field.many_to_many or isinstance(field, models.ManyToManyRel):
return must_be(
"a many-to-many field", option=label, obj=obj, id="admin.E020"
)
Expand Down
4 changes: 3 additions & 1 deletion django/contrib/gis/gdal/geometries.py
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,9 @@ def __init__(self, geom_input, srs=None):
self.srs = srs

# Setting the class depending upon the OGR Geometry Type
self.__class__ = GEO_CLASSES[self.geom_type.num]
if (geo_class := GEO_CLASSES.get(self.geom_type.num)) is None:
raise TypeError(f"Unsupported geometry type: {self.geom_type}")
self.__class__ = geo_class

# Pickle routines
def __getstate__(self):
Expand Down
48 changes: 48 additions & 0 deletions django/contrib/gis/gdal/geomtype.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,57 @@ class OGRGeomType:
5: "MultiLineString",
6: "MultiPolygon",
7: "GeometryCollection",
8: "CircularString",
9: "CompoundCurve",
10: "CurvePolygon",
11: "MultiCurve",
12: "MultiSurface",
15: "PolyhedralSurface",
16: "TIN",
17: "Triangle",
100: "None",
101: "LinearRing",
102: "PointZ",
1008: "CircularStringZ",
1009: "CompoundCurveZ",
1010: "CurvePolygonZ",
1011: "MultiCurveZ",
1012: "MultiSurfaceZ",
1013: "CurveZ",
1014: "SurfaceZ",
1015: "PolyhedralSurfaceZ",
1016: "TINZ",
1017: "TriangleZ",
2001: "PointM",
2002: "LineStringM",
2003: "PolygonM",
2004: "MultiPointM",
2005: "MultiLineStringM",
2006: "MultiPolygonM",
2007: "GeometryCollectionM",
2008: "CircularStringM",
2009: "CompoundCurveM",
2010: "CurvePolygonM",
2011: "MultiCurveM",
2012: "MultiSurfaceM",
2015: "PolyhedralSurfaceM",
2016: "TINM",
2017: "TriangleM",
3001: "PointZM",
3002: "LineStringZM",
3003: "PolygonZM",
3004: "MultiPointZM",
3005: "MultiLineStringZM",
3006: "MultiPolygonZM",
3007: "GeometryCollectionZM",
3008: "CircularStringZM",
3009: "CompoundCurveZM",
3010: "CurvePolygonZM",
3011: "MultiCurveZM",
3012: "MultiSurfaceZM",
3015: "PolyhedralSurfaceZM",
3016: "TINZM",
3017: "TriangleZM",
1 + wkb25bit: "Point25D",
2 + wkb25bit: "LineString25D",
3 + wkb25bit: "Polygon25D",
Expand Down
4 changes: 3 additions & 1 deletion django/contrib/gis/geometry.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,9 @@
r"^(SRID=(?P<srid>\-?[0-9]+);)?"
r"(?P<wkt>"
r"(?P<type>POINT|LINESTRING|LINEARRING|POLYGON|MULTIPOINT|"
r"MULTILINESTRING|MULTIPOLYGON|GEOMETRYCOLLECTION)"
r"MULTILINESTRING|MULTIPOLYGON|GEOMETRYCOLLECTION|CIRCULARSTRING|COMPOUNDCURVE|"
r"CURVEPOLYGON|MULTICURVE|MULTISURFACE|CURVE|SURFACE|POLYHEDRALSURFACE|TIN|"
r"TRIANGLE)"
r"[ACEGIMLONPSRUTYZ0-9,.+() -]+)$",
re.I,
)
Expand Down
4 changes: 4 additions & 0 deletions docs/releases/5.0.1.txt
Original file line number Diff line number Diff line change
Expand Up @@ -32,3 +32,7 @@ Bugfixes

* 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`).
22 changes: 22 additions & 0 deletions tests/cache/tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -1766,6 +1766,28 @@ def test_has_key_race_handling(self):
self.assertIs(cache.has_key("key"), False)
mocked_open.assert_called_once()

def test_touch(self):
"""Override to manually advance time since file access can be slow."""

class ManualTickingTime:
def __init__(self):
# Freeze time, calling `sleep` will manually advance it.
self._time = time.time()

def time(self):
return self._time

def sleep(self, seconds):
self._time += seconds

mocked_time = ManualTickingTime()
with (
mock.patch("django.core.cache.backends.filebased.time", new=mocked_time),
mock.patch("django.core.cache.backends.base.time", new=mocked_time),
mock.patch("cache.tests.time", new=mocked_time),
):
super().test_touch()


@unittest.skipUnless(RedisCache_params, "Redis backend not configured")
@override_settings(
Expand Down
81 changes: 80 additions & 1 deletion tests/gis_tests/gdal_tests/test_geom.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ def test_geomtype(self):
with self.assertRaises(GDALException):
OGRGeomType("fooD")
with self.assertRaises(GDALException):
OGRGeomType(9)
OGRGeomType(4001)

# Equivalence can take strings, ints, and other OGRGeomTypes
self.assertEqual(OGRGeomType(1), OGRGeomType(1))
Expand Down Expand Up @@ -635,3 +635,82 @@ def test_empty(self):
def test_empty_point_to_geos(self):
p = OGRGeometry("POINT EMPTY", srs=4326)
self.assertEqual(p.geos.ewkt, p.ewkt)

def test_geometry_types(self):
tests = [
("Point", 1, True),
("LineString", 2, True),
("Polygon", 3, True),
("MultiPoint", 4, True),
("Multilinestring", 5, True),
("MultiPolygon", 6, True),
("GeometryCollection", 7, True),
("CircularString", 8, False),
("CompoundCurve", 9, False),
("CurvePolygon", 10, False),
("MultiCurve", 11, False),
("MultiSurface", 12, False),
# 13 (Curve) and 14 (Surface) are abstract types.
("PolyhedralSurface", 15, False),
("TIN", 16, False),
("Triangle", 17, False),
("Linearring", 2, True),
# Types 1 - 7 with Z dimension have 2.5D enums.
("Point Z", -2147483647, True), # 1001
("LineString Z", -2147483646, True), # 1002
("Polygon Z", -2147483645, True), # 1003
("MultiPoint Z", -2147483644, True), # 1004
("Multilinestring Z", -2147483643, True), # 1005
("MultiPolygon Z", -2147483642, True), # 1006
("GeometryCollection Z", -2147483641, True), # 1007
("CircularString Z", 1008, False),
("CompoundCurve Z", 1009, False),
("CurvePolygon Z", 1010, False),
("MultiCurve Z", 1011, False),
("MultiSurface Z", 1012, False),
("PolyhedralSurface Z", 1015, False),
("TIN Z", 1016, False),
("Triangle Z", 1017, False),
("Point M", 2001, False),
("LineString M", 2002, False),
("Polygon M", 2003, False),
("MultiPoint M", 2004, False),
("MultiLineString M", 2005, False),
("MultiPolygon M", 2006, False),
("GeometryCollection M", 2007, False),
("CircularString M", 2008, False),
("CompoundCurve M", 2009, False),
("CurvePolygon M", 2010, False),
("MultiCurve M", 2011, False),
("MultiSurface M", 2012, False),
("PolyhedralSurface M", 2015, False),
("TIN M", 2016, False),
("Triangle M", 2017, False),
("Point ZM", 3001, False),
("LineString ZM", 3002, False),
("Polygon ZM", 3003, False),
("MultiPoint ZM", 3004, False),
("MultiLineString ZM", 3005, False),
("MultiPolygon ZM", 3006, False),
("GeometryCollection ZM", 3007, False),
("CircularString ZM", 3008, False),
("CompoundCurve ZM", 3009, False),
("CurvePolygon ZM", 3010, False),
("MultiCurve ZM", 3011, False),
("MultiSurface ZM", 3012, False),
("PolyhedralSurface ZM", 3015, False),
("TIN ZM", 3016, False),
("Triangle ZM", 3017, False),
]

for test in tests:
geom_type, num, supported = test
with self.subTest(geom_type=geom_type, num=num, supported=supported):
if supported:
g = OGRGeometry(f"{geom_type} EMPTY")
self.assertEqual(g.geom_type.num, num)
else:
type_ = geom_type.replace(" ", "")
msg = f"Unsupported geometry type: {type_}"
with self.assertRaisesMessage(TypeError, msg):
OGRGeometry(f"{geom_type} EMPTY")
36 changes: 36 additions & 0 deletions tests/modeladmin/test_checks.py
Original file line number Diff line number Diff line change
Expand Up @@ -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):
Expand Down Expand Up @@ -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):
Expand Down

0 comments on commit dd9cc39

Please sign in to comment.