Skip to Content

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:

PropertyTypeDescription
geoint | NoneCity ID
geo_displaystr”City, State, Country”
geo_cityCityDTO | NoneFull city data
geo_stateStateDTO | NoneState/region data
geo_countryCountryDTO | NoneCountry data
geo_coordinatestuple | None(latitude, longitude)
geo_flagstrCountry 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 query

Admin 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 needed

CountryField

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

PropertyTypeDescription
countrystr | NoneISO2 code (“US”, “KR”)
country_namestrFull country name
country_flagstrFlag emoji
country_dataCountryDTO | NoneFull 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

PropertyTypeDescription
cityint | NoneCity ID
city_namestrCity name
city_dataCityDTO | NoneFull 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

Last updated on