Django address autocomplete and reverse geocoding: Google plus free national-register providers (CZ RÚIAN, SK Register adries).
Project description
harriers-django-geocoding
A self-contained Django app for address autocomplete and reverse geocoding behind a provider-agnostic interface. Ships a metered Google provider (Places Autocomplete New + Geocoding API) as the default for any country, plus free local providers backed by national address registers — Czech (ČÚZK RÚIAN) and Slovak (ZBGIS Register adries) today.
Requirements
-
Python 3.10–3.14, Django ≥ 5.0.
-
PostgreSQL with PostGIS, reached through GeoDjango and psycopg 3 (
"ENGINE": "django.contrib.gis.db.backends.postgis"). Migration0001creates thepostgisandpg_trgmextensions and LIST-partitions theAddresstable by country, so the database user must be allowed to create extensions. -
DRF is a dependency.
drf-spectacularis not, but is used when the host has it: the views then carry full@extend_schemaannotations. Two extras:Extra Adds Without it unfolddjango-unfold admin: country dropdown filter, one-click import buttons plain django.contrib.adminregistrationcelerythe import tasks in harriers_django_geocoding.tasksrun the import commands from cron or by hand
Install
-
pip install harriers-django-geocoding— orharriers-django-geocoding[unfold]for the rich django-unfold admin (plain admin fallback otherwise). -
Add
"harriers_django_geocoding"toINSTALLED_APPSand configure. The app label isgeocoding— that, not the import path, is what names the table, the migrations and the admin URLs. With the[unfold]extra, also add"unfold"and"unfold.contrib.filters"(the dropdown filter's templates live there) abovedjango.contrib.admin:GEOCODING = { "GOOGLE_MAPS_API_KEY": os.environ.get("GOOGLE_MAPS_API_KEY", ""), # DRF permission classes as dotted paths. Default: [IsAuthenticated]. "PERMISSION_CLASSES": ["myapp.permissions.IsStaff"], # Language for Google responses. Default: "en". "LANGUAGE": "cs", # How far (m) a reverse-geocoded match may lie from the queried point; # farther means 204 "no address here". Default: 500. "REVERSE_MAX_DISTANCE_M": 500, # How many addresses a country must have imported before its local provider # takes over from Google, so a partial load does not serve the country from # a handful of rows. Default: 10000. "LOCAL_MIN_ADDRESSES": 10000, # Country a request without a `country` parameter is served as. Unset, such a # request goes to Google whatever is imported. Default: "" (none). "DEFAULT_COUNTRY": "cz", # Shortest word the local providers search a register for; below it the # trigram index cannot serve the query and every keystroke would scan the # country's whole partition. Default: 3. "LOCAL_MIN_QUERY_LENGTH": 3, }
-
Mount the URLs (they define
geocode,geocode/details,geocode/reverse):path("", include("harriers_django_geocoding.urls")),
-
python manage.py migrate.
HTTP API
All endpoints are GET and permissioned by GEOCODING["PERMISSION_CLASSES"].
| Endpoint | Query params | Returns |
|---|---|---|
…/geocode |
q, country (ISO alpha-2) |
[{ id, label }] autocomplete predictions |
…/geocode/details |
placeId, country |
one resolved address (below), or 204 |
…/geocode/reverse |
lat, lng, country |
one resolved address (below), or 204 |
Resolved address (GeocodeResult):
{ "label": "...", "address": "...", "address_line_2": "...", "city": "...",
"postcode": "...", "country_code": "cz", "latitude": 0.0, "longitude": 0.0,
"source": "local:cz" }
Serializers use snake_case; with djangorestframework-camel-case the responses are
camelCased, and the details view accepts both placeId spellings either way.
Address data import
A local provider serves a country only once its addresses are imported — at least
GEOCODING["LOCAL_MIN_ADDRESSES"] of them, so a partial load does not take the
country off Google and then answer from a handful of rows. Until then the country
falls back to Google automatically. Both registers are free and keyless.
| Country | Source | Command / Celery task |
|---|---|---|
cz |
ČÚZK RÚIAN monthly bulk CSV (~3M addresses) | import_cz_addresses |
sk |
ZBGIS Register adries feature service | import_sk_addresses |
python manage.py import_cz_addresses
Every command takes --limit N for testing; the CZ one takes --url to override
dataset auto-detection. A limited run disables pruning and writes only to a country
whose partition is still empty — it holds an arbitrary slice of the register, which
merged into a populated partition would overwrite the labels a full run disambiguated.
Each run stages the whole snapshot and merges the partition from there: only changed rows are written, missing rows are pruned (skipped if the run collected suspiciously few rows), and the live partition is untouched until the snapshot is complete. Imports are idempotent, keyed on the register's own identifier, so retries and overlapping runs are safe.
With the [celery] extra the same imports run as tasks, scheduled via
CELERY_BEAT_SCHEDULE. RÚIAN publishes monthly a few days after month
end, Register adries updates daily, harvested weekly because a full pass takes hours:
CELERY_BEAT_SCHEDULE = {
"import-cz-addresses": {
"task": "geocoding.import_cz_addresses",
"schedule": crontab(day_of_month=6, hour=3, minute=0),
},
"import-sk-addresses": {
"task": "geocoding.import_sk_addresses",
"schedule": crontab(day_of_week=0, hour=4, minute=0),
},
}
Run imports in a Celery worker or stable container, not a process that can be killed mid-run. CZ takes a few minutes; SK is paced by the remote service (1000 records per response, with pauses and backoff).
Google setup
Enable both the Geocoding API and the Places API (New) on the key's project. Both are billed per request — check Google's current pricing before pointing a busy picker at them. A country whose register is imported is served for free instead.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file harriers_django_geocoding-0.1.0.tar.gz.
File metadata
- Download URL: harriers_django_geocoding-0.1.0.tar.gz
- Upload date:
- Size: 53.9 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: uv/0.9.30 {"installer":{"name":"uv","version":"0.9.30","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Debian GNU/Linux","version":"12","id":"bookworm","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
8e507a4242aa982580f8238a3db9d506532d3925d0c4d8cd38a139baaae16409
|
|
| MD5 |
f8eab0f6a4489ab43c1d16a8dfb4aa7b
|
|
| BLAKE2b-256 |
3678ed02afe5b6ac2fd6cc925f8941c9aca409713f0d6c112f285e0dbdb61c14
|
File details
Details for the file harriers_django_geocoding-0.1.0-py3-none-any.whl.
File metadata
- Download URL: harriers_django_geocoding-0.1.0-py3-none-any.whl
- Upload date:
- Size: 48.2 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: uv/0.9.30 {"installer":{"name":"uv","version":"0.9.30","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Debian GNU/Linux","version":"12","id":"bookworm","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
96ec9998d9ac57d8e11f58d1dfbef8fb63e65835cb3cd24438672b6a56191238
|
|
| MD5 |
c20c6325baa5b65c1a4d081ee177a693
|
|
| BLAKE2b-256 |
4140e16ab48f8fb333eaece334a7d8d65d38d3ff47eb1ad917dfe684261cdabe
|