13

PointField 是否有一个小部件作为单独的纬度/经度输入?就像 DateTimeField 的 SplitDateTimeWidget。

4

6 回答 6

10

这是我的工作自定义字段和小部件:

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
于 2013-06-11T09:42:30.400 回答
8

答案非常适合构建您自己的解决方案,但如果您想要一个看起来不错且易于使用的简单解决方案,请尝试: http ://django-map-widgets.readthedocs.io/en/latest/index.html

你可以:

  • 输入坐标
  • 直接在地图上选择
  • 从 Google 地方查找项目。

来自 Django 地图小部件的屏幕截图

于 2017-12-19T20:07:48.593 回答
7

@Ramast以更 Python 的方式解决了同样的问题。我已将更改合并到我的代码中,不断改进感觉很棒。


这就是我管理的方式 - 最后 - 为纬度和经度保留单独的字段,而不必将它们保存在数据库中,因为这些值已经保存在 PointField 中。

这个想法是:

  • 如果我们要插入一个新条目,纬度和经度字段将用于设置 PointField
  • 如果我们打开一个现有的 PointField 条目,它将用于在相关的 FormFields 中提供纬度和经度值。

模型.py

from django.contrib.gis.db import models as geomodels


class Entry(geomodels.Model):
    point = geomodels.PointField(
        srid=4326,
        blank=True,
        )

管理员.py

from myapp.forms import EntryForm
from django.contrib import admin


class EntryAdmin(admin.ModelAdmin):
    form = EntryForm


admin.site.register(Entry, EntryAdmin)

表格.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
于 2014-03-10T19:12:52.483 回答
4

这是基于@rara_tiru 的解决方案,但有一些改进

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

这基本上允许用户在地图上设置位置或手动输入纬度/经度值。保存后,地图将反映指定的纬度/经度值

于 2015-10-26T06:43:18.040 回答
0

SelectDateWidget 为此创建一个自定义小部件,您可以从这里获得灵感。

于 2013-06-10T12:27:23.157 回答
0

我自己也在寻找这个问题的答案。作为初学者,我对许多不同的解决方案感到很困惑。但我能够用下面的代码实现它。

  1. 只需要输入经纬度文本来填充 PointField,不需要地图输入,并且在表单中也看不到地图
  2. PointField 可以有空值
  3. 验证 Lat Long 值的组合是否设置正确,即两者都不应该为空或两者都应该为空。

模型类

class DataSet(models.Model):
    id = models.AutoField(primary_key=True)
    name = models.CharField(max_length=20, blank=False, null=False, unique=True)
    latlong = geo_models.PointField(null=True,blank=True)

模型表单类

class DataSetForm(ModelForm):
lat = forms.FloatField(
    min_value=-90,
    max_value=90,
    required=False,
)
lng = forms.FloatField(
    min_value=-180,
    max_value=180,
    required=False,
)

class Meta:
    model = DataSet
    fields = ["name"]
    labels = {"name": "Name", "lat": "Latitude", "lng": "Longitude"}

def clean(self):
    super().clean()
    data = self.cleaned_data
    if (data['lat'] is None and data['lng'] is not None) or ( 
      data['lat'] is not None and data['lng'] is None):
       raise forms.ValidationError(
          _('Both Lat Long Values must be set'), code='invalid')

查看课程

class AddDataSet(CreateView):
    model = DataSet
    form_class = DataSetForm
    template_name = "create.html"

    def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs)
        context.update({"title": "Create Dataset"})
        return context

    def form_valid(self, form):
        lat = form.cleaned_data['lat']
        lng = form.cleaned_data['lng']
        response = super(CreateView, self).form_valid(form)
        if (lat is not None and lng is not None):
          self.object.latlong = Point(lat, lng)
        self.object.save()
        return response

我不确定这种方法是否存在缺陷。但就我测试而言,它有效。

于 2021-06-22T07:46:46.143 回答