Geographic Fields
Django-CFG provides specialized model fields for geographic data with automatic property resolution and admin integration.
LocationField
The primary field for storing location references. Stores a city ID and provides auto-properties for accessing related data.
Basic Usage
from django.db import models
from django_cfg.apps.tools.geo.fields import LocationField
class Property(models.Model):
geo = LocationField(
null=True,
blank=True,
verbose_name="Location",
help_text="Select city from geo database",
)Auto-Properties
LocationField automatically creates these properties on your model:
| Property | Type | Description |
|---|---|---|
geo | int | None | City ID |
geo_display | str | ”City, State, Country” |
geo_city | CityDTO | None | Full city data |
geo_state | StateDTO | None | State/region data |
geo_country | CountryDTO | None | Country data |
geo_coordinates | tuple | None | (latitude, longitude) |
geo_flag | str | Country flag emoji |
Example
property = Property.objects.get(id=1)
# Raw value
property.geo # 56263
# Display string
property.geo_display # "Denpasar, Bali, ID"
# City details
city = property.geo_city
city.name # "Denpasar"
city.latitude # -8.6500
city.longitude # 115.2167
# State details
state = property.geo_state
state.name # "Bali"
state.iso2 # "BA"
# Country details
country = property.geo_country
country.name # "Indonesia"
country.iso2 # "ID"
country.emoji # "🇮🇩"
# Coordinates tuple
property.geo_coordinates # (-8.6500, 115.2167)
# Flag emoji
property.geo_flag # "🇮🇩"Caching
All auto-properties use GeoDatabase’s LRU cache. Multiple accesses don’t trigger extra queries:
# First access - cache miss, queries database
display = property.geo_display
# Subsequent accesses - cache hit
flag = property.geo_flag # No query
coords = property.geo_coordinates # No queryAdmin Widget
LocationField automatically uses LocationFieldWidget in Django admin:
- Autocomplete - Type-ahead city search
- Map - Click to select location
- Display - Shows flag, city, state, country
# In admin, the widget is auto-applied
# No extra configuration neededCountryField
Field for storing country reference by ISO2 code.
from django_cfg.apps.tools.geo.fields import CountryField
class Organization(models.Model):
country = CountryField(
null=True,
blank=True,
verbose_name="Country",
)Auto-Properties
| Property | Type | Description |
|---|---|---|
country | str | None | ISO2 code (“US”, “KR”) |
country_name | str | Full country name |
country_flag | str | Flag emoji |
country_data | CountryDTO | None | Full country data |
Example
org = Organization.objects.get(id=1)
org.country # "KR"
org.country_name # "South Korea"
org.country_flag # "🇰🇷"
org.country_data # CountryDTO(name="South Korea", iso2="KR", ...)CityField
Field for storing city reference by ID.
from django_cfg.apps.tools.geo.fields import CityField
class Branch(models.Model):
city = CityField(
null=True,
blank=True,
verbose_name="City",
)Auto-Properties
| Property | Type | Description |
|---|---|---|
city | int | None | City ID |
city_name | str | City name |
city_data | CityDTO | None | Full city data |
Form Widgets
LocationFieldWidget
Full-featured widget with map, autocomplete, and geocoding.
from django_cfg.apps.tools.geo.widgets import LocationFieldWidget
class PropertyForm(forms.ModelForm):
class Meta:
model = Property
fields = ['geo']
widgets = {
'geo': LocationFieldWidget(attrs={
'data-placeholder': 'Search for a city...',
}),
}Features:
- Autocomplete search (Photon API)
- Interactive map (click to select)
- Reverse geocoding (coordinates → city)
- Flag display
- Mobile-friendly
Select2 Widgets
Simpler Select2-based widgets for basic use cases:
from django_cfg.apps.tools.geo.widgets import (
CountrySelect2Widget,
CitySelect2Widget,
)
class SimpleForm(forms.ModelForm):
class Meta:
widgets = {
'country': CountrySelect2Widget(),
'city': CitySelect2Widget(),
}Migration Example
When adding LocationField to existing model:
# migrations/0002_add_geo_field.py
from django.db import migrations
from django_cfg.apps.tools.geo.fields import LocationField
class Migration(migrations.Migration):
dependencies = [
('myapp', '0001_initial'),
]
operations = [
migrations.AddField(
model_name='property',
name='geo',
field=LocationField(blank=True, null=True),
),
]Populate from Coordinates
After adding the field, populate from existing coordinates:
# management command or script
from django_cfg.apps.tools.geo.services import resolve_city_from_coordinates
for prop in Property.objects.filter(geo__isnull=True, latitude__isnull=False):
city_id = resolve_city_from_coordinates(
float(prop.latitude),
float(prop.longitude),
radius_km=50,
)
if city_id:
prop.geo = city_id
prop.save(update_fields=['geo'])Best Practices
DO: Use LocationField for Primary Location
class Property(models.Model):
# GOOD - Single source of truth
geo = LocationField()
# Keep coordinates for precise location
latitude = models.DecimalField(...)
longitude = models.DecimalField(...)DON’T: Store Location as Text
class Property(models.Model):
# BAD - No standardization, hard to query
location = models.CharField(max_length=100)DO: Auto-Populate on Ingestion
def create_property(data):
property = Property.objects.create(
latitude=data['lat'],
longitude=data['lng'],
geo=resolve_city_from_coordinates(
data['lat'],
data['lng'],
radius_km=50,
),
)See Also
- Services Reference - GeoDatabase and geocoding
- Models Reference - DTOs and data structures
- Overview - Architecture and quick start