import requests from constance import config from django.contrib.gis.geos import Point from django.db import models class AddressManager(models.Manager): def create(self, raw_data: str, **kwargs): """ Create an Address instance by geocoding the provided raw address string. Args: raw_data (str): The raw address input from the user (e.g., '36 Mornington Rd Loughton England'). **kwargs: Additional fields to pass to the Address model (e.g., user). """ if not raw_data: raise ValueError("'raw_data' (address string) must be provided.") # Query Nominatim params = { 'format': 'json', 'addressdetails': 1, 'q': raw_data, } resp = requests.get(config.NOMINATIM_URL, params=params) resp.raise_for_status() results = resp.json() if not results: raise ValueError(f"No geocoding result for address: {raw_data}") data = results[0] # Parse address components addr = data.get('address', {}) street = addr.get('road') or addr.get('pedestrian') or '' district = addr.get('city_district') or addr.get('suburb') or '' city = addr.get('city') or addr.get('town') or addr.get('village') or '' region = addr.get('state') or addr.get('region') or '' postal_code = addr.get('postcode') or '' country = addr.get('country') or '' # Parse location try: lat = float(data.get('lat')) lon = float(data.get('lon')) location = Point(lon, lat, srid=4326) except (TypeError, ValueError): location = None # Create the model instance, storing both the input string and full API response return super().create( raw_data=raw_data, street=street, district=district, city=city, region=region, postal_code=postal_code, country=country, location=location, api_response=data, **kwargs )