Is there a widget for PointField as separate latitude/longitude inputs? Like SplitDateTimeWidget for DateTimeField.
Here's my working custom field and widget:
class LatLongWidget(forms.MultiWidget):
    """
    A Widget that splits Point input into two latitude/longitude boxes.
    """
    def __init__(self, attrs=None, date_format=None, time_format=None):
        widgets = (forms.TextInput(attrs=attrs),
                   forms.TextInput(attrs=attrs))
        super(LatLongWidget, self).__init__(widgets, attrs)
    def decompress(self, value):
        if value:
            return tuple(reversed(value.coords))
        return (None, None)
class LatLongField(forms.MultiValueField):
    widget = LatLongWidget
    srid = 4326
    default_error_messages = {
        'invalid_latitude' : _('Enter a valid latitude.'),
        'invalid_longitude' : _('Enter a valid longitude.'),
    }
    def __init__(self, *args, **kwargs):
        fields = (forms.FloatField(min_value=-90, max_value=90), 
                  forms.FloatField(min_value=-180, max_value=180))
        super(LatLongField, self).__init__(fields, *args, **kwargs)
    def compress(self, data_list):
        if data_list:
            # Raise a validation error if latitude or longitude is empty
            # (possible if LatLongField has required=False).
            if data_list[0] in validators.EMPTY_VALUES:
                raise forms.ValidationError(self.error_messages['invalid_latitude'])
            if data_list[1] in validators.EMPTY_VALUES:
                raise forms.ValidationError(self.error_messages['invalid_longitude'])
            # SRID=4326;POINT(1.12345789 1.123456789)
            srid_str = 'SRID=%d'%self.srid
            point_str = 'POINT(%f %f)'%tuple(reversed(data_list))
            return ';'.join([srid_str, point_str])
        return None
The answers are great for building your own solution, but if you want a simple solution that looks good and is easy to use, try: http://django-map-widgets.readthedocs.io/en/latest/index.html
You can:

@Ramast, approaches the same issue in a more pythonic way. I have incorporated the changes to my code, it feels great to always improve.
This is how I managed -at last- to keep separate fields for lattitude and longitude without having to save them in the database since the values are already saved in the PointField.
The idea is :
models.py
from django.contrib.gis.db import models as geomodels
class Entry(geomodels.Model):
    point = geomodels.PointField(
        srid=4326,
        blank=True,
        )
admin.py
from myapp.forms import EntryForm
from django.contrib import admin
class EntryAdmin(admin.ModelAdmin):
    form = EntryForm
admin.site.register(Entry, EntryAdmin)
forms.py
from django import forms
from myapp.models import Entry
from django.contrib.gis.geos import Point
class MarketEntryForm(forms.ModelForm):
    latitude = forms.FloatField(
        min_value=-90,
        max_value=90,
        required=True,
    )
    longitude = forms.FloatField(
        min_value=-180,
        max_value=180,
        required=True,
    )
    class Meta(object):
        model = MarketEntry
        exclude = []
        widgets = {'point': forms.HiddenInput()}
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        coordinates = self.initial.get('point', None)
        if isinstance(coordinates, Point):
            self.initial['longitude'], self.initial['latitude'] = coordinates.tuple
    def clean(self):
        data = super().clean()
        latitude = data.get('latitude')
        longitude = data.get('longitude')
        point = data.get('point')
        if latitude and longitude and not point:
            data['point'] = Point(longitude, latitude)
        return data
This is based on @rara_tiru's solution but with some improvements
class GisForm(forms.ModelForm):
    """
    This form can be used by any model that has "coordinates" field.
    It will show a better looking map than the default one
    """
    lat = forms.FloatField(required=False)
    lng = forms.FloatField(required=False)
    coordinates = PointField(widget=CustomPointWidget(), required=False, srid=4326)
    def __init__(self, *args, **kwargs):
        super(GisForm, self).__init__(*args, **kwargs)
        coordinates = self.initial.get("coordinates", None)
        if isinstance(coordinates, Point):
            self.initial["lng"], self.initial["lat"] = coordinates.tuple
    def clean(self):
        data = super(GisForm, self).clean()
        if "lat" in self.changed_data or "lng" in self.changed_data:
            lat, lng = data.pop("lat", None), data.pop("lng", None)
            data["coordinates"] = Point(lng, lat, srid=4326)
        if not (data.get("coordinates") or data.get("lat")):
            raise forms.ValidationError({"coordinates": "Coordinates is required"})
        return data
This will basically allow users to either set location on map or manually put lat/lng values. once saved the map will reflect the specificed lat/lng value
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With