Пользовательское поле формы широты / долготы в Джанго - PullRequest
4 голосов
/ 28 августа 2011

Одна из моих моделей имеет поля широты и долготы, которые хранятся в базе данных как числа с плавающей запятой.Я хотел бы сохранить это так, потому что это позволяет мне работать с ними наиболее эффективно.

Я бы хотел, чтобы пользователи могли редактировать их в стандартном интерфейсе администратора в этом формате: (+/-) DD MM SS.S (именно так большинство устройств GPS представляют координаты конечному пользователю).

Я думал о трех способах реализации этого:

  1. Использовать GeoDjango - тожемного накладных расходов, мне просто не нужен полный каркас только для двух полей.
  2. Определите пользовательское поле модель , каким-либо образом таким образом .Похоже, много кода, и я не совсем уверен, смогу ли я легко получить доступ к представлению с плавающей запятой, используя интерфейс базы данных Django.
  3. Использование MultiValueField и MultiWidget - это не было бы совсем плохорешение, но довольно плохо документировано и также включает в себя немного кодирования и ненужные виджеты для градусов, минут и секунд.

Но в идеале я хотел бы сделать это:

  • Используйте пользовательское поле формы , которое будет использовать стандартный виджет формы TextInput и стандартное поле модели FloatField.

Я уверен, что метод to_python () может обрабатывать текствведите и конвертируйте его в float.Но как мне сказать Django преобразовать float в мое представление lat / lng при редактировании модели?И как мне все это соединить?

Ответы [ 2 ]

3 голосов
/ 28 августа 2011

Почему бы не добавить в модель еще несколько полей для хранения данных координат, а затем метод save() вашей модели преобразует их в значения широты и долготы? Затем в админке сделайте lat / lon доступным только для чтения, чтобы значения можно было просматривать, но не редактировать. Или вы можете решить вообще их не показывать!

Например:

class Location(models.Model):

    latitude = ...
    longitude = ...

    lat_degrees = models.IntegerField()
    lat_minutes = models.IntegerField()
    lat_seconds = models.FloatField()

    def save(self, *args, **kwargs):
        # Do the maths here to calculate lat/lon
        self.latitude = ... 
        self.longitude = ...
        super(Location, self).save(*args, **kwargs)

Полагаю, вам также понадобятся lon_degrees поля, наверное, я не эксперт по координатам. Я оставил это из примера. Вы также можете создать новый виджет для администратора, чтобы он хорошо отображался, или просто переопределить change_form.html, чтобы три поля появились в одной строке, но это немного выходит за рамки этого ответа.

0 голосов
/ 30 декабря 2017

У меня недавно было это требование, и я немного увлекся, но думал, что поделюсь. (Джанго 2.0.)

Я создал 30-символьное CharField для хранения введенных координат например N 35º 44.265 W 41º 085.155 (кстати, я понятия не имею, где это ...) для модели для хранения значений полей.

import re
from django.core.exceptions import ValidationError

COORDINATES_REGEX = r'(?:[NS])\s*([0-9]{2})[\º\°]?\s+([0-9]{1,3}\.[0-9]{3})\s*(?:[EW])\s*([0-9]{2,3})[\º\°]?\s+([0-9]{2,3}\.[0-9]{3})'

def decode_coords_string(str):
    """
    Given a string, converts it to a decimal (lat, lng, 'OK', matched_string) tuple.
    If invalid, returns "(None, None, <some reason>, None)."

    Test for errors by checking that the coordinate is not 'None.'

    'matched_string' returns the actual extent of the matched string regardless of where in the input-string it was,
      for sanitizing the input when storing it in the database.  (If the input string contains only blanks, we should
      store an empty-string.)
    The model will replace the field-value with this matched-string.
    """
    # Dispose of empty input, returning an empty string(!) as the 'matched_string' in this case.
    r = re.compile(r'^\s*$')
    if r.match(str):
        return (None, None, 'Empty string', '')

    # Build the regex for coordinates.
    r = re.compile(COORDINATES_REGEX, re.IGNORECASE)

    # Try to match the string
    p = r.match(str)
    if p is None:
        return (None, None, 'Syntax error', None)

    # Get the pieces and expressly convert them to numeric types
    (lat_degs, lat_mins, lng_degs, lng_mins) = p.groups()

    lat_degs = int(lat_degs)
    lat_mins = float(lat_mins)
    lng_degs = int(lng_degs)
    lng_mins = float(lng_mins)

    # Throw out anything that simply does not make sense
    if (lat_degs > 180) or (lng_degs > 180) or (lat_mins > 60.0) or (lng_mins > 60.0):
        return (None, None, 'Degs/Mins value(s) out of bounds')

    latitude  =  float(lat_degs) + (lat_mins / 60.0)
    longitude = (float(lng_degs) + (lng_mins / 60.0)) * -1.0

    return (latitude, longitude, 'OK', p.group())


def validate_coords(str):
    """
    Simple validator for a coordinate string.
    """
    (lat, lng, reason, str2) = decode_coords_string(str)
    if lat is None:
        raise ValidationError('Invalid coordinates: ' + reason)

На входе CharField указывается validators=[validate_coords] Также обратите внимание, что символ градусов можно указывать несколькими способами или вообще не указывать.

И Модель включает в себя следующий короткий метод:

def save(self, *args, **kwargs):
"""
Calculate and automatically populate the numeric lat/long figures.
This routine assumes that the string is either empty or that it has been validated.
An empty string – or, for that matter, an invalid one – will be (None, None).
"""

( lat, lng, reason, cleaned) = decode_coords_string(self.coordinate_str)

self.coordinate_str = cleaned
self.latitude       = lat
self.longitude      = lng
super().save(*args, **kwargs)

В admin.py я исключаю поля latitude и longitude (оба из которых являются полями с плавающей точкой), чтобы не вводить пользователя в заблуждение. Числовые поля рассчитываются автоматически, но не отображаются.

...