GeoDjango LayerMapping и внешний ключ

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

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

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

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

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

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


person Larry Morroni    schedule 17.01.2014    source источник


Ответы (5)


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

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

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

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

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

теперь представьте, что «модель нагрузки» — это набор данных точек, представляющих все буи на всех озерах, и имеет имя поля «fk», которое является внешним ключом к «основной модели» для назначения озера, к которому принадлежит каждый буй.

наконец, данные, которые вы загружаете в «модель загрузки», имеют строковое поле «ИМЯ» и содержат предварительно заполненное название озера, которому принадлежит каждый буй. это строковое имя является связующим звеном. это позволяет «загрузочной модели» использовать это имя для определения того, с каким озером в «первичной модели» он должен установить внешний ключ.

person smudge services    schedule 11.10.2017

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

  • Дайте USCounty «состояние» FK для USState и запустите manage.py syncdb
  • Замените «state» на «state_id» и реальный тип данных, обычно models.IntegerField, и выполните load.run() LayerMapper.
  • Верните FK "state" в модель USCounty.
  • Используйте Django в обычном режиме.

    В моем случае ниже ключи «состояния» представляют собой двухсимвольные коды FIPS.

    class USCounty(models.Model):
        state = models.ForeignKey(USState)
        ## state_id = models.CharField(max_length=2)
        ...
        geom = models.MultiPolygonField(srid=4326)
        objects = models.GeoManager()
    
person Andrew    schedule 22.02.2014
comment
Я делаю то же самое, но я действительно недоволен этим взломом. Я хотел бы иметь возможность создавать и загружать базу данных из моего файла .shp без ручного вмешательства - person Antoine Claval; 06.10.2015

Я обошел это, вручную добавив временный обратный вызов 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)
person Gabriel    schedule 02.09.2014


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

Ошибка возникает из-за этой части кода. строка ~220 файла 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 ищет словарь: ogr_name.items()

ogr_name на самом деле определяется как часть значения в словаре сопоставления. Предполагается, что dict должен состоять из имени поля организации и имени связанного поля из связанной модели.

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

person jcs    schedule 12.07.2016