8000 Fixed #35841 -- Restored support for DB-IP databases in GeoIP2. · commit-0/django@3fad712 · GitHub
[go: up one dir, main page]

Skip to content

Commit 3fad712

Browse files
ngnpopenessita
andcommitted
Fixed #35841 -- Restored support for DB-IP databases in GeoIP2.
Thanks Felix Farquharson for the report and Claude Paroz for the review. Regression in 40b5b15. Co-authored-by: Natalia <124304+nessita@users.noreply.github.com>
1 parent 5873f10 commit 3fad712

File tree

6 files changed

+67
-8
lines changed

6 files changed

+67
-8
lines changed

django/contrib/gis/geoip2.py

Lines changed: 23 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,18 @@
3434
__all__ += ["GeoIP2", "GeoIP2Exception"]
3535

3636

37+
# These are the values stored in the `database_type` field of the metadata.
8000 38+
# See https://maxmind.github.io/MaxMind-DB/#database_type for details.
39+
SUPPORTED_DATABASE_TYPES = {
40+
"DBIP-City-Lite",
41+
"DBIP-Country-Lite",
42+
"GeoIP2-City",
43+
"GeoIP2-Country",
44+
"GeoLite2-City",
45+
"GeoLite2-Country",
46+
}
47+
48+
3749
class GeoIP2Exception(Exception):
3850
pass
3951

@@ -106,7 +118,7 @@ def __init__(self, path=None, cache=0, country=None, city=None):
106118
)
107119

108120
database_type = self._metadata.database_type
109-
if not database_type.endswith(("City", "Country")):
121+
if database_type not in SUPPORTED_DATABASE_TYPES:
110122
raise GeoIP2Exception(f"Unable to handle database edition: {database_type}")
111123

112124
def __del__(self):
@@ -123,16 +135,22 @@ def __repr__(self):
123135
def _metadata(self):
124136
return self._reader.metadata()
125137

138+
@cached_property
139+
def is_city(self):
140+
return "City" in self._metadata.database_type
141+
142+
@cached_property
143+
def is_country(self):
144+
return "Country" in self._metadata.database_type
145+
126146
def _query(self, query, *, require_city=False):
127147
if not isinstance(query, (str, ipaddress.IPv4Address, ipaddress.IPv6Address)):
128148
raise TypeError(
129149
"GeoIP query must be a string or instance of IPv4Address or "
130150
"IPv6Address, not type %s" % type(query).__name__,
131151
)
132152

133-
is_city = self._metadata.database_type.endswith("City")
134-
135-
if require_city and not is_city:
153+
if require_city and not self.is_city:
136154
raise GeoIP2Exception(f"Invalid GeoIP city data file: {self._path}")
137155

138156
try:
@@ -141,7 +159,7 @@ def _query(self, query, *, require_city=False):
141159
# GeoIP2 only takes IP addresses, so try to resolve a hostname.
142160
query = socket.gethostbyname(query)
143161

144-
function = self._reader.city if is_city else self._reader.country
162+
function = self._reader.city if self.is_city else self._reader.country
145163
return function(query)
146164

147165
def city(self, query):

docs/releases/5.1.3.txt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,3 +14,6 @@ Bugfixes
1414
:class:`~django.core.validators.DomainNameValidator` accepted any input value
1515
that contained a valid domain name, rather than only input values that were a
1616
valid domain name (:ticket:`35845`).
17+
18+
* Fixed a regression in Django 5.1 that prevented the use of DB-IP databases
19+
with :class:`~django.contrib.gis.geoip2.GeoIP2` (:ticket:`35841`).

tests/gis_tests/data/geoip2/README.md

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,3 +12,17 @@ Updates can be found in [this repository][1].
1212

1313
[0]: https://github.com/maxmind/MaxMind-DB/blob/main/LICENSE-MIT
1414
[1]: https://github.com/maxmind/MaxMind-DB/tree/main/test-data
15+
16+
# DB-IP Lite Test Databases
17+
18+
The following test databases are provided under [this license][2]:
19+
20+
- `dbip-city-lite-test.mmdb`
21+
- `dbip-country-lite-test.mmdb`
22+
23+
They have been modified to strip them down to a minimal dataset for testing.
24+
25+
Updates can be found at [this download page][3] from DB-IP.
26+
27+
[2]: https://creativecommons.org/licenses/by/4.0/
28+
[3]: https://db-ip.com/db/lite.php
Binary file not shown.
Binary file not shown.

tests/gis_tests/test_geoip2.py

Lines changed: 27 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -124,7 +124,8 @@ def test_bad_query(self):
124124

125125
def test_country(self):
126126
g = GeoIP2(city="<invalid>")
127-
self.assertIs(g._metadata.database_type.endswith("Country"), True)
127+
self.assertIs(g.is_city, False)
128+
self.assertIs(g.is_country, True)
128129
for query in self.query_values:
129130
with self.subTest(query=query):
130131
self.assertEqual(g.country(query), self.expected_country)
@@ -137,7 +138,8 @@ def test_country(self):
137138

138139
def test_country_using_city_database(self):
139140
g = GeoIP2(country="<invalid>")
140-
self.assertIs(g._metadata.database_type.endswith("City"), True)
141+
self.assertIs(g.is_city, True)
142+
self.assertIs(g.is_country, False)
141143
for query in self.query_values:
142144
with self.subTest(query=query):
143145
self.assertEqual(g.country(query), self.expected_country)
@@ -150,7 +152,8 @@ def test_country_using_city_database(self):
150152

151153
def test_city(self):
152154
g = GeoIP2(country="<invalid>")
153-
self.assertIs(g._metadata.database_type.endswith("City"), True)
155+
self.assertIs(g.is_city, True)
156+
self.assertIs(g.is_country, False)
154157
for query in self.query_values:
155158
with self.subTest(query=query):
156159
self.assertEqual(g.city(query), self.expected_city)
@@ -224,6 +227,27 @@ class GeoIP2Test(GeoLite2Test):
224227
"""Non-free GeoIP2 databases are supported."""
225228

226229

230+
@skipUnless(HAS_GEOIP2, "GeoIP2 is required.")
231+
@override_settings(
232+
GEOIP_CITY="dbip-city-lite-test.mmdb",
233+
GEOIP_COUNTRY="dbip-country-lite-test.mmdb",
234+
)
235+
class DBIPLiteTest(GeoLite2Test):
236+
"""DB-IP Lite databases are supported."""
237+
238+
expected_city = GeoLite2Test.expected_city | {
239+
"accuracy_radius": None,
240+
"city": "London (Shadwell)",
241+
"latitude": 51.5181,
242+
"longitude": -0.0714189,
243+
"postal_code": None,
244+
"region_code": None,
245+
"time_zone": None,
246+
# Kept for backward compatibility.
247+
"region": None,
248+
}
249+
250+
227251
@skipUnless(HAS_GEOIP2, "GeoIP2 is required.")
228252
class ErrorTest(SimpleTestCase):
229253
def test_missing_path(self):

0 commit comments

Comments
 (0)
0