summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNick Pope <nick@nickpope.me.uk>2024-10-15 09:48:59 +0100
committernessita <124304+nessita@users.noreply.github.com>2024-10-17 21:40:53 -0300
commit3fad712a91a8a8f6f6f904aff3d895e3b06b24c7 (patch)
tree897d0a53619eb0e894b8a81ebe496a4301aa7ea9
parent5873f10177ebda66d38e698218cf85dc6397e7d9 (diff)
Fixed #35841 -- Restored support for DB-IP databases in GeoIP2.
Thanks Felix Farquharson for the report and Claude Paroz for the review. Regression in 40b5b1596f7505416bd30d5d7582b5a9004ea7d5. Co-authored-by: Natalia <124304+nessita@users.noreply.github.com>
-rw-r--r--django/contrib/gis/geoip2.py28
-rw-r--r--docs/releases/5.1.3.txt3
-rw-r--r--tests/gis_tests/data/geoip2/README.md14
-rw-r--r--tests/gis_tests/data/geoip2/dbip-city-lite-test.mmdbbin0 -> 1481 bytes
-rw-r--r--tests/gis_tests/data/geoip2/dbip-country-lite-test.mmdbbin0 -> 1314 bytes
-rw-r--r--tests/gis_tests/test_geoip2.py30
6 files changed, 67 insertions, 8 deletions
diff --git a/django/contrib/gis/geoip2.py b/django/contrib/gis/geoip2.py
index f5058c1c05..a5fe429b89 100644
--- a/django/contrib/gis/geoip2.py
+++ b/django/contrib/gis/geoip2.py
@@ -34,6 +34,18 @@ else:
__all__ += ["GeoIP2", "GeoIP2Exception"]
+# These are the values stored in the `database_type` field of the metadata.
+# See https://maxmind.github.io/MaxMind-DB/#database_type for details.
+SUPPORTED_DATABASE_TYPES = {
+ "DBIP-City-Lite",
+ "DBIP-Country-Lite",
+ "GeoIP2-City",
+ "GeoIP2-Country",
+ "GeoLite2-City",
+ "GeoLite2-Country",
+}
+
+
class GeoIP2Exception(Exception):
pass
@@ -106,7 +118,7 @@ class GeoIP2:
)
database_type = self._metadata.database_type
- if not database_type.endswith(("City", "Country")):
+ if database_type not in SUPPORTED_DATABASE_TYPES:
raise GeoIP2Exception(f"Unable to handle database edition: {database_type}")
def __del__(self):
@@ -123,6 +135,14 @@ class GeoIP2:
def _metadata(self):
return self._reader.metadata()
+ @cached_property
+ def is_city(self):
+ return "City" in self._metadata.database_type
+
+ @cached_property
+ def is_country(self):
+ return "Country" in self._metadata.database_type
+
def _query(self, query, *, require_city=False):
if not isinstance(query, (str, ipaddress.IPv4Address, ipaddress.IPv6Address)):
raise TypeError(
@@ -130,9 +150,7 @@ class GeoIP2:
"IPv6Address, not type %s" % type(query).__name__,
)
- is_city = self._metadata.database_type.endswith("City")
-
- if require_city and not is_city:
+ if require_city and not self.is_city:
raise GeoIP2Exception(f"Invalid GeoIP city data file: {self._path}")
try:
@@ -141,7 +159,7 @@ class GeoIP2:
# GeoIP2 only takes IP addresses, so try to resolve a hostname.
query = socket.gethostbyname(query)
- function = self._reader.city if is_city else self._reader.country
+ function = self._reader.city if self.is_city else self._reader.country
return function(query)
def city(self, query):
diff --git a/docs/releases/5.1.3.txt b/docs/releases/5.1.3.txt
index e3c62072b5..0dd5b42cb8 100644
--- a/docs/releases/5.1.3.txt
+++ b/docs/releases/5.1.3.txt
@@ -14,3 +14,6 @@ Bugfixes
:class:`~django.core.validators.DomainNameValidator` accepted any input value
that contained a valid domain name, rather than only input values that were a
valid domain name (:ticket:`35845`).
+
+* Fixed a regression in Django 5.1 that prevented the use of DB-IP databases
+ with :class:`~django.contrib.gis.geoip2.GeoIP2` (:ticket:`35841`).
diff --git a/tests/gis_tests/data/geoip2/README.md b/tests/gis_tests/data/geoip2/README.md
index 36328671b2..f2a703b457 100644
--- a/tests/gis_tests/data/geoip2/README.md
+++ b/tests/gis_tests/data/geoip2/README.md
@@ -12,3 +12,17 @@ Updates can be found in [this repository][1].
[0]: https://github.com/maxmind/MaxMind-DB/blob/main/LICENSE-MIT
[1]: https://github.com/maxmind/MaxMind-DB/tree/main/test-data
+
+# DB-IP Lite Test Databases
+
+The following test databases are provided under [this license][2]:
+
+- `dbip-city-lite-test.mmdb`
+- `dbip-country-lite-test.mmdb`
+
+They have been modified to strip them down to a minimal dataset for testing.
+
+Updates can be found at [this download page][3] from DB-IP.
+
+[2]: https://creativecommons.org/licenses/by/4.0/
+[3]: https://db-ip.com/db/lite.php
diff --git a/tests/gis_tests/data/geoip2/dbip-city-lite-test.mmdb b/tests/gis_tests/data/geoip2/dbip-city-lite-test.mmdb
new file mode 100644
index 0000000000..5f0d657c84
--- /dev/null
+++ b/tests/gis_tests/data/geoip2/dbip-city-lite-test.mmdb
Binary files differ
diff --git a/tests/gis_tests/data/geoip2/dbip-country-lite-test.mmdb b/tests/gis_tests/data/geoip2/dbip-country-lite-test.mmdb
new file mode 100644
index 0000000000..8410d1893c
--- /dev/null
+++ b/tests/gis_tests/data/geoip2/dbip-country-lite-test.mmdb
Binary files differ
diff --git a/tests/gis_tests/test_geoip2.py b/tests/gis_tests/test_geoip2.py
index f6f6ab0397..61b3565d1c 100644
--- a/tests/gis_tests/test_geoip2.py
+++ b/tests/gis_tests/test_geoip2.py
@@ -124,7 +124,8 @@ class GeoLite2Test(SimpleTestCase):
def test_country(self):
g = GeoIP2(city="<invalid>")
- self.assertIs(g._metadata.database_type.endswith("Country"), True)
+ self.assertIs(g.is_city, False)
+ self.assertIs(g.is_country, True)
for query in self.query_values:
with self.subTest(query=query):
self.assertEqual(g.country(query), self.expected_country)
@@ -137,7 +138,8 @@ class GeoLite2Test(SimpleTestCase):
def test_country_using_city_database(self):
g = GeoIP2(country="<invalid>")
- self.assertIs(g._metadata.database_type.endswith("City"), True)
+ self.assertIs(g.is_city, True)
+ self.assertIs(g.is_country, False)
for query in self.query_values:
with self.subTest(query=query):
self.assertEqual(g.country(query), self.expected_country)
@@ -150,7 +152,8 @@ class GeoLite2Test(SimpleTestCase):
def test_city(self):
g = GeoIP2(country="<invalid>")
- self.assertIs(g._metadata.database_type.endswith("City"), True)
+ self.assertIs(g.is_city, True)
+ self.assertIs(g.is_country, False)
for query in self.query_values:
with self.subTest(query=query):
self.assertEqual(g.city(query), self.expected_city)
@@ -225,6 +228,27 @@ class GeoIP2Test(GeoLite2Test):
@skipUnless(HAS_GEOIP2, "GeoIP2 is required.")
+@override_settings(
+ GEOIP_CITY="dbip-city-lite-test.mmdb",
+ GEOIP_COUNTRY="dbip-country-lite-test.mmdb",
+)
+class DBIPLiteTest(GeoLite2Test):
+ """DB-IP Lite databases are supported."""
+
+ expected_city = GeoLite2Test.expected_city | {
+ "accuracy_radius": None,
+ "city": "London (Shadwell)",
+ "latitude": 51.5181,
+ "longitude": -0.0714189,
+ "postal_code": None,
+ "region_code": None,
+ "time_zone": None,
+ # Kept for backward compatibility.
+ "region": None,
+ }
+
+
+@skipUnless(HAS_GEOIP2, "GeoIP2 is required.")
class ErrorTest(SimpleTestCase):
def test_missing_path(self):
msg = "GeoIP path must be provided via parameter or the GEOIP_PATH setting."