Django Integration¶
Setup¶
settings.py¶
urls.py (optional, for the API endpoints)¶
from django.urls import include, path
urlpatterns = [
path("api/v1/yoro/", include("yoro.django.urls")),
]
Migrate¶
YoroField — The Simplest Way¶
YoroField is a Django model field that stores a Yoro code as a database column. It's the recommended way to add geographic addressing to your models.
Basic usage¶
from django.db import models
from yoro.django.fields import YoroField
class Shop(models.Model):
name = models.CharField(max_length=100)
address = YoroField(domain="CI", precision=12)
That's it. Your model now has a address column that:
- Stores Yoro codes as strings (
"CI-PV9XD") - Auto-encodes from
(lat, lon)tuples - Validates codes on form submission
- Renders an interactive map picker in forms and Django admin
Assigning values¶
# Direct code assignment
shop = Shop(name="Boutique Abidjan")
shop.address = "CI-PV9XD"
shop.save()
# Auto-encode from GPS coordinates
shop.address = (5.345, -4.028) # tuple (lat, lon)
shop.save()
print(shop.address) # "CI-PV9XD" — encoded automatically
# Also works with lists
shop.address = [5.345, -4.028]
shop.save()
Reading and decoding¶
# The field stores a string
print(shop.address) # "CI-PV9XD"
print(type(shop.address)) # <class 'str'>
# Decode to get coordinates and cell bounds
import yoro
decoded = yoro.decode(shop.address)
print(decoded["lat"]) # 5.34519...
print(decoded["lon"]) # -4.02868...
print(decoded["bounds"]) # cell bounding box
print(decoded["precision"]) # 12
Field options¶
class DeliveryPoint(models.Model):
address = YoroField(
domain="CI", # ISO country code (default: "CI")
precision=14, # Hilbert order (default: 12 → ~173m)
map_height=400, # Widget map height in px (default: 300)
verbose_name="Delivery address",
help_text="Click on the map or enter a Yoro code",
blank=True, # Optional field (default: True)
)
Available precision levels¶
| Precision | Code Length | Resolution (CI) | Use Case |
|---|---|---|---|
| 7 | 3 chars | ~5.5 km | City/district |
| 9 | 4 chars | ~1.4 km | Neighborhood |
| 12 | 5 chars | ~173 m | Block (default) |
| 14 | 6 chars | ~43 m | Building |
| 17 | 7 chars | ~5.4 m | Entrance |
| 19 | 8 chars | ~1.35 m | Meter-level |
Querying¶
Since YoroField is a CharField, you can use standard Django lookups:
# Exact match
Shop.objects.filter(address="CI-PV9XD")
# All shops in Cote d'Ivoire
Shop.objects.filter(address__startswith="CI-")
# All shops with an address assigned
Shop.objects.exclude(address="")
# Find nearby: encode, get neighbors, query
import yoro
neighbors = yoro.neighbors("CI-PV9XD")
Shop.objects.filter(address__in=["CI-PV9XD"] + neighbors)
Spatial proximity queries
For efficient spatial queries, combine YoroField with Yoro's neighbors() function. Since nearby locations share similar codes (Hilbert curve property), querying for a code and its 8 neighbors gives you a ~500m radius search at precision 12.
YoroWidget — Interactive Map Picker¶
Every YoroField automatically uses the YoroWidget in forms. But you can also use the widget independently on any CharField.
What the widget provides¶
The widget renders:
- A monospace text input — for typing/pasting Yoro codes
- A Leaflet map — click anywhere to encode that location
- Cell visualization — the Hilbert cell is drawn as an orange dashed rectangle
- Info display — resolution in meters, GPS coordinates, precision level
All encoding/decoding happens client-side in the browser (via yoro-codec.min.js). No AJAX calls, no server round-trips, no latency.
In Django Admin¶
The widget works in Django admin out of the box. Just register your model:
from django.contrib import admin
from myapp.models import Shop
@admin.register(Shop)
class ShopAdmin(admin.ModelAdmin):
list_display = ("name", "address")
search_fields = ("name", "address")
The admin form will show the map picker for the address field automatically.
In custom forms¶
from django import forms
from yoro.django.widgets import YoroWidget
class ShopForm(forms.Form):
name = forms.CharField(max_length=100)
address = forms.CharField(
widget=YoroWidget(
domain="CI",
precision=12,
map_height=350,
),
required=False,
)
In templates¶
The widget includes its own CSS and JS via Django's Media framework. Make sure your template renders form media:
<head>
{{ form.media }}
</head>
<body>
<form method="post">
{% csrf_token %}
{{ form.as_div }}
<button type="submit">Save</button>
</form>
</body>
The widget automatically loads:
- Leaflet CSS + JS (from CDN)
yoro-codec.min.js(client-side Hilbert codec)yoro-widget.js(map interaction logic)yoro-widget.css(styling)
Customizing the tile provider¶
# Satellite tiles
address = YoroField(
domain="CI",
map_attrs={
"tile_url": "https://server.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer/tile/{z}/{y}/{x}",
"tile_attribution": "© Esri",
},
)
# Or on the widget directly
widget = YoroWidget(
tile_url="https://{s}.basemaps.cartocdn.com/light_all/{z}/{x}/{y}{r}.png",
tile_attribution="© CARTO",
)
Customizing the CSS¶
The widget uses minimal, scoped CSS classes that you can override:
/* Make the code input larger */
.yoro-code-input {
font-size: 20px;
width: 200px;
}
/* Change the map height */
.yoro-map {
min-height: 400px;
}
/* Style the info text */
.yoro-info {
color: #333;
font-size: 14px;
}
No Bootstrap, Tailwind, or any CSS framework is imposed. The widget works with any design system.
Model: GeoAltiusCode¶
For advanced use cases (storing cell geometry, spatial queries on cell polygons), Yoro also provides the GeoAltiusCode model with GeoDjango fields:
| Field | Type | Description |
|---|---|---|
code |
CharField(20) |
Yoro code (unique, indexed) |
domain |
CharField(5) |
ISO domain prefix |
location |
PointField |
GPS center point (SRID 4326) |
cell |
PolygonField |
Hilbert cell bounding polygon |
precision |
SmallIntegerField |
Hilbert order |
resolution_m |
FloatField |
Cell size in meters |
label |
CharField(255) |
Optional human label |
created_at |
DateTimeField |
Auto-set on creation |
YoroField vs GeoAltiusCode
Use YoroField when you just need a Yoro code as an address (most cases). It's a simple CharField — no PostGIS needed for the field itself.
Use GeoAltiusCode when you need to store the cell geometry for spatial queries (e.g. "find all codes that intersect this polygon"). It requires GeoDjango + PostGIS.
Services¶
get_or_create_yoro¶
from yoro.django.services import get_or_create_yoro
obj = get_or_create_yoro(lat=5.345, lon=-4.028, domain="CI")
print(obj.code) # "CI-PV9XD"
print(obj.cell) # Polygon geometry
assign_yoro¶
Assigns a Yoro code to any model instance that has location (PointField) and yoro (ForeignKey) fields. Modifies the instance in place but does not save.
from yoro.django.services import assign_yoro
class MyLocation(models.Model):
location = gis_models.PointField(srid=4326)
yoro = models.ForeignKey(
"yoro.GeoAltiusCode",
null=True, blank=True,
on_delete=models.SET_NULL,
)
def save(self, **kwargs):
if self.location and not self.yoro_id:
assign_yoro(self, domain="CI")
super().save(**kwargs)
API Endpoints¶
| Method | URL | Description |
|---|---|---|
| GET | /encode/?lat=6.8&lon=-5.3&domain=CI |
Encode GPS to code |
| GET | /decode/?code=CI-PV9XD |
Decode code to GPS |
| GET | /precisions/?domain=CI |
List canonical precisions |
| GET | /domains/ |
List all domains |
Admin¶
GeoAltiusCode is automatically registered with the GIS admin, showing the map widget for location and cell fields.