4

我的一个模型有纬度和经度字段,它们作为浮点数存储在数据库中。我喜欢保持这种方式,因为它可以让我最有效地与他们合作。

我希望用户能够以这种格式在库存管理界面中编辑它们:(+/-)DD MM SS.S(这是大多数 GPS 设备向最终用户呈现坐标的方式)。

我想到了三种实现方式:

  1. 使用 GeoDjango - 开销太大,我根本不需要仅针对两个字段的完整框架。
  2. 以某种方式定义自定义模型字段。似乎有很多编码,我不完全确定是否能够使用 Django 数据库接口轻松访问浮点表示。
  3. 使用 MultiValueField 和 MultiWidget - 这不是一个完全糟糕的解决方案,但文档记录很差,并且还涉及一些编码和不必要的度数、分钟和秒小部件。

但理想情况下,我想这样做:

  • 使用将使用标准 TextInput 表单小部件和标准 FloatField 模型字段的自定义表单字段。

我确信 to_python() 方法可以处理文本输入并将其转换为浮点数。但是我如何告诉 Django 在编辑模型时将浮点数转换为我的 lat/lng 表示?我怎么把它们粘在一起?

4

2 回答 2

3

Why not add some more fields to your model to hold the co-ordinate data, and then have the save() method of your model convert these to latitude and longitude figures? Then in the admin, make lat/lon read only so that the values can be viewed, but not edited. Or, you may decide not to show them at all!

For example:

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)

I assume you'll also need lon_degrees fields, I'm guessing, I'm no expert on co-ordinates. I've left those out of the example. You may also want to create a new widget for the admin to make it display nicely, or just override change_form.html to make the three fields appear on the same line, but that's slightly beyond the scope of this answer.

于 2011-08-28T10:40:59.967 回答
0

我最近有这个要求,有点得意忘形,但我想我会分享。 (Django 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我从视图中排除latitudeandlongitude字段(两者都是 Float 字段),以避免混淆用户。数字字段会自动计算,但不会显示。

于 2017-12-29T22:08:20.070 回答