GeoDjango LayerMapping & Foreign Key

Я пытаюсь импортировать мой KML файл в модель, используя функциональность GeoDjango LayerMapping. Я выполнял тесты и не имел проблем при регулярном импорте. Однако недавно я добавил к моей модели внешний ключ. Моя модель называется PlaceMark, и теперь она имеет FK для модели под названием Layer. Я хотел бы либо

  • переопределить импорт и вручную установить значение поля внешнего ключа или
  • обновите мой KML файл, чтобы он содержал новый элемент, который соединяет PlaceMark со слоем через поле pk или имя слоя.

Вот как я тестирую оболочку и соответствующую ошибку:

>>>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 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>

Моя цель состоит в том, чтобы просто импортировать все PlaceMarks с ссылками на соответствующий слой. Любые идеи?

Спасибо! Ларри

Ответ 1

Я обманул LayerMapper в загрузке поля ForeignKey как простой тип данных после создания таблиц.

  • Дайте USCounty FK "state" для USState и запустите manage.py syncdb
  • Замените "состояние" на "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

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

ошибка, которую вы получаете, что поле Foreign Key должно быть словарем, в основном запрашивает дополнительное сопоставление модели, к которой относится внешний ключ.

в приведенном выше фрагменте:

  • 'fk' - это имя поля внешнего ключа из модели, в которую загружаются данные (позволяет называть это "модель загрузки" )
  • 'nm_field' - это имя поля из модели, в которой "модель нагрузки" имеет отношение внешнего ключа к (позволяет назвать его "первичной моделью" )
  • "ИМЯ" - это имя поля из загружаемых данных в "модель нагрузки" , которая содержит отношение к "первичной модели"

более подробно, представьте, что "первичная модель" - это набор данных озер, и у них есть поле под названием "nm_field", которое является именем озера в виде строки.

Теперь представьте себе, "модель нагрузки" представляет собой набор данных, представляющих все буи на всех озерах, и имеет имя поля "fk", которое является ForeignKey для "первичной модели" для назначения озера, каждый буй принадлежит.

наконец, данные, которые вы загружаете в "модель загрузки", имеют строковое поле под названием "NAME" и содержат предварительно заполненное имя озера, к которому принадлежит каждый буй. это строковое имя - связь отношений. он позволяет "модели нагрузки" использовать это имя, чтобы определить, какое озеро в "первичной модели" должно установить внешний ключ.

Ответ 3

Я работал над этим, вручную добавляя временный обратный вызов pre_save. Вы можете подключить его только для создания записи, а затем отключите, как только LayerMapping выполнит свою работу.

См. "Мое решение" здесь - метод "черного ящика", на который я ссылаюсь, на самом деле является именно этим прецедентом.

Код, который работает для меня:

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)

Ответ 4

Не похоже, что есть простой способ подключиться к 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()

Ответ 5

Не ответ, но, надеюсь, намек.

Исправленная ошибка исходит из этой части кода. строка ~ 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. Предполагается, что dict состоит из имени поля org и связанного имени поля из связанной модели.

Если кто-то понимает происхождение этого ogr_name dict, это было бы очень полезно.