GeoDjango LayerMapping&外键

时间:2014-01-17 22:54:25

标签: python django geodjango

我正在尝试使用GeoDjango的LayerMapping功能将我的KML文件导入模型。我正在进行测试,在进行常规导入时没有问题。但是,我最近在我的模型中添加了一个外键。我的模型叫做PlaceMark,它现在有一个名为Layer的模型的FK。我想要

  1. 覆盖导入并手动设置外键字段的值或
  2. 更新我的KML文件以包含一个新元素,该元素通过图层的pk或name字段将PlaceMark连接到图层。
  3. 以下是我如何测试shell和相关错误:

    >>>from locator import load
    >>>load.run()
    ...
    TypeError: ForeignKey mapping must be of dictionary type.
    ....
    

    这是我的load.py文件:

    import os
    from django.contrib.gis.utils import LayerMapping
    from models import PlaceMark
    
    placemark_mapping = {
        'name' : 'Name',
        'description' : 'Description',
        # This line below is the one that is suspect #
        'layer': 'Layer',
        'geom' : 'POINT25D',
    }
    
    placemark_kml = os.path.abspath(os.path.join(os.path.dirname(__file__), 'data/claim.kml'))
    
    def run(verbose=True):
        lm = LayerMapping(PlaceMark, placemark_kml, placemark_mapping,
                          transform=False, encoding='iso-8859-1')
    
    lm.save(strict=True, verbose=verbose)
    

    KML文件:

    <?xml version="1.0" encoding="Windows-1252"?>
    <kml xmlns="http://earth.google.com/kml/2.1">
    <Folder>
      <description><![CDATA[TankSafe_Claims]]></description>
      <Placemark>
        <name><![CDATA[G2184729A]]></name>
        <description><![CDATA[<br><br><br>
        <table border="1" padding="0">
        <tr><td>Policy_Number</td><td>53645645</td></tr>
        <tr><td>Claim_Number</td><td>2342342234</td></tr>
        <tr><td>Policy_Type</td><td>TSP</td></tr>
        <tr><td>Name</td><td>Al's Total</td></tr>
        <tr><td>Street_Address</td><td>555 109th Avenue</td></tr>
        <tr><td>City</td><td>Pullman</td></tr>
        <tr><td>State</td><td>NY</td></tr>
        <tr><td>Zip_Code</td><td>55555</td></tr>
        <tr><td>County</td><td>Allegan</td></tr>
            ]]></description>
        <visibility>1</visibility>
        <open>0</open>
        <Point>
          <extrude>1</extrude>
          <altitudeMode>relativeToGround</altitudeMode>
          <coordinates>-86.092641,42.483953,0</coordinates>
        </Point>
        <!--- ***Should I add the line below?*** -->
        <Layer><name>claims</name></Layer>
      </Placemark>
    </Folder>
    </kml>
    

    我的目标是让所有导入的PlaceMark都引用相关图层。有什么想法吗?

    谢谢! 拉里

5 个答案:

答案 0 :(得分:5)

layer_mapping = {
    'fk': {'nm_field': 'NAME'}, # foreign key field
    'this_field': 'THIS',
    'that_field': 'THAT',
    'geom': 'POLYGON',
}

您收到的外键字段应该是字典的错误基本上是请求与外键相关的模型的附加映射。

在上面的代码段中:

  • &#39; FK&#39;是数据加载到的模型中的外键字段名称(让我们称之为加载模型&#39;)
  • &#39; nm_field&#39;是模型中的字段名称&#39;加载模型&#39;有外键关系(让我们称之为主要模型&#39;)
  • &#39; NAME&#39;是正在加载到“加载模型”中的数据的字段名称。它与主要模型&#39;
  • 保持着关系

更明确地说,想象一下,如果&#39;主要模型&#39;是一个湖泊的数据集,他们有一个名为&#39; nm_field&#39;这是一个字符串的湖名。

现在想象一下,&#39;加载模型&#39;是一个代表所有湖泊所有浮标的点数据集,并且有一个字段名称“fk”。这是一个ForeignKey到&#39;主要模型&#39;为每个浮标所属的湖泊分配。

最后,您要将数据加载到“加载模型”中。有一个名为&#39; NAME&#39;的字符串字段。它包含每个浮标所属的湖泊的预先填充的名称。该字符串名称是关系领带。它允许负载模型&#39;使用该名称来识别主要模型中的哪个湖泊&#39;它应该用。建立一个外键。

答案 1 :(得分:2)

我欺骗了LayerMapper在创建表格后将ForeignKey字段作为普通数据类型加载。

  • 向USState提供USCounty FK“州”并运行manage.py syncdb
  • 将“state”替换为“state_id”和实际数据类型, 通常models.IntegerField并执行load.run()LayerMapper。
  • 将“州”FK归还给USCounty模型。
  • 正常使用Django。

    在我的情况下,“状态”键是2个字符的FIPS代码。

    class USCounty(models.Model):
        state = models.ForeignKey(USState)
        ## state_id = models.CharField(max_length=2)
        ...
        geom = models.MultiPolygonField(srid=4326)
        objects = models.GeoManager()
    

答案 2 :(得分:1)

我通过手动添加临时pre_save回调来解决这个问题。您只需创建记录就可以连接它,然后在LayerMapping完成其工作后立即断开连接。

请参阅“我的解决方案”here - 我所指的“黑匣子”方法实际上就是这个用例。

适用于我的代码:

def pre_save_callback(sender, instance, *args, **kwargs):
    fkey = some_method_that_gets_the_foreign_key()
    instance.type = fkey

# other mappings defined as usual
mapping = {
    'key1': 'KEY1',
    ...,
}

lm = LayerMapping(models.MyModel, PATH_TO_SHAPEFILE, mapping, transform=True)
# temporarily connect pre_save method
pre_save.connect(pre_save_callback, sender=models.MyModel)
try:
    lm.save(strict=True)
except Exception as exc:
    optional_error_handling()
    raise
finally:
    # disconnect pre_save callback
    pre_save.disconnect(pre_save_callback, sender=models.MyModel)

答案 3 :(得分:0)

看起来似乎没有一种简单的方法来挂接外键字段的LayerMapping。我通过使用for循环和get_geoms()调用解决了这个问题。感谢http://invisibleroads.com/tutorials/geodjango-googlemaps-build.html

以下是我所做的一个例子:

placemark_kml = os.path.abspath(os.path.join(os.path.dirname(locator.__file__), 'data/claim.kml'))
datasource = DataSource(placemark_kml)
lyr = datasource[0]
waypointNames = lyr.get_fields('Name')
waypointDescriptions = lyr.get_fields('Description')
waypointGeometries = lyr.get_geoms()
for waypointName, waypointGeometry, waypointDescription in itertools.izip(waypointNames, waypointGeometries, waypointDescriptions):
    placemark = PlaceMark(name=waypointName, description=waypointDescription, geom=waypointGeometry.wkt)
    placemark.layer = Layer.objects.get(pk=8)
    placemark.save()

答案 4 :(得分:0)

不是答案,但希望有一个提示。

抛出的错误来自代码的这一部分。 line~220 of layermapping.py

elif isinstance(model_field, models.ForeignKey):
    if isinstance(ogr_name, dict):
        # Is every given related model mapping field in the Layer?
        rel_model = model_field.rel.to
        for rel_name, ogr_field in ogr_name.items():
            idx = check_ogr_fld(ogr_field)
            try:
                rel_model._meta.get_field(rel_name)
            except models.fields.FieldDoesNotExist:
                raise LayerMapError('ForeignKey mapping field "%s" not in %s fields.' %
                                    (rel_name, rel_model.__class__.__name__))
        fields_val = rel_model
    else:
        raise TypeError('ForeignKey mapping must be of dictionary type.')

在for循环的开头,它查找一个dict:ogr_name.items()

ogr_name实际上被定义为映射字典的值部分。 dict应该由组织字段名称和相关模型中的相关字段名称组成。

如果有人理解该ogr_name dict的来源,那将非常有用。