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, это было бы очень полезно.