| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164 |
- import argparse
- from django.contrib.gis import gdal
- from django.core.management.base import BaseCommand, CommandError
- from django.utils.inspect import get_func_args
- class LayerOptionAction(argparse.Action):
- """
- Custom argparse action for the `ogrinspect` `layer_key` keyword option
- which may be an integer or a string.
- """
- def __call__(self, parser, namespace, value, option_string=None):
- try:
- setattr(namespace, self.dest, int(value))
- except ValueError:
- setattr(namespace, self.dest, value)
- class ListOptionAction(argparse.Action):
- """
- Custom argparse action for `ogrinspect` keywords that require
- a string list. If the string is 'True'/'true' then the option
- value will be a boolean instead.
- """
- def __call__(self, parser, namespace, value, option_string=None):
- if value.lower() == "true":
- setattr(namespace, self.dest, True)
- else:
- setattr(namespace, self.dest, value.split(","))
- class Command(BaseCommand):
- help = (
- "Inspects the given OGR-compatible data source (e.g., a shapefile) and "
- "outputs\na GeoDjango model with the given model name. For example:\n"
- " ./manage.py ogrinspect zipcode.shp Zipcode"
- )
- requires_system_checks = []
- def add_arguments(self, parser):
- parser.add_argument("data_source", help="Path to the data source.")
- parser.add_argument("model_name", help="Name of the model to create.")
- parser.add_argument(
- "--blank",
- action=ListOptionAction,
- default=False,
- help="Use a comma separated list of OGR field names to add "
- "the `blank=True` option to the field definition. Set to `true` "
- "to apply to all applicable fields.",
- )
- parser.add_argument(
- "--decimal",
- action=ListOptionAction,
- default=False,
- help="Use a comma separated list of OGR float fields to "
- "generate `DecimalField` instead of the default "
- "`FloatField`. Set to `true` to apply to all OGR float fields.",
- )
- parser.add_argument(
- "--geom-name",
- default="geom",
- help="Specifies the model name for the Geometry Field (defaults to `geom`)",
- )
- parser.add_argument(
- "--layer",
- dest="layer_key",
- action=LayerOptionAction,
- default=0,
- help="The key for specifying which layer in the OGR data "
- "source to use. Defaults to 0 (the first layer). May be "
- "an integer or a string identifier for the layer.",
- )
- parser.add_argument(
- "--multi-geom",
- action="store_true",
- help="Treat the geometry in the data source as a geometry collection.",
- )
- parser.add_argument(
- "--name-field",
- help="Specifies a field name to return for the __str__() method.",
- )
- parser.add_argument(
- "--no-imports",
- action="store_false",
- dest="imports",
- help="Do not include `from django.contrib.gis.db import models` statement.",
- )
- parser.add_argument(
- "--null",
- action=ListOptionAction,
- default=False,
- help="Use a comma separated list of OGR field names to add "
- "the `null=True` option to the field definition. Set to `true` "
- "to apply to all applicable fields.",
- )
- parser.add_argument(
- "--srid",
- help="The SRID to use for the Geometry Field. If it can be "
- "determined, the SRID of the data source is used.",
- )
- parser.add_argument(
- "--mapping",
- action="store_true",
- help="Generate mapping dictionary for use with `LayerMapping`.",
- )
- def handle(self, *args, **options):
- data_source, model_name = options.pop("data_source"), options.pop("model_name")
- # Getting the OGR DataSource from the string parameter.
- try:
- ds = gdal.DataSource(data_source)
- except gdal.GDALException as msg:
- raise CommandError(msg)
- # Returning the output of ogrinspect with the given arguments
- # and options.
- from django.contrib.gis.utils.ogrinspect import _ogrinspect, mapping
- # Filter options to params accepted by `_ogrinspect`
- ogr_options = {
- k: v
- for k, v in options.items()
- if k in get_func_args(_ogrinspect) and v is not None
- }
- output = [s for s in _ogrinspect(ds, model_name, **ogr_options)]
- if options["mapping"]:
- # Constructing the keyword arguments for `mapping`, and
- # calling it on the data source.
- kwargs = {
- "geom_name": options["geom_name"],
- "layer_key": options["layer_key"],
- "multi_geom": options["multi_geom"],
- }
- mapping_dict = mapping(ds, **kwargs)
- # This extra legwork is so that the dictionary definition comes
- # out in the same order as the fields in the model definition.
- rev_mapping = {v: k for k, v in mapping_dict.items()}
- output.extend(
- [
- "",
- "",
- "# Auto-generated `LayerMapping` dictionary for %s model"
- % model_name,
- "%s_mapping = {" % model_name.lower(),
- ]
- )
- output.extend(
- " '%s': '%s'," % (rev_mapping[ogr_fld], ogr_fld)
- for ogr_fld in ds[options["layer_key"]].fields
- )
- output.extend(
- [
- " '%s': '%s',"
- % (options["geom_name"], mapping_dict[options["geom_name"]]),
- "}",
- ]
- )
- return "\n".join(output)
|