web/lib/django/contrib/gis/db/backend/postgis/creation.py
changeset 29 cc9b7e14412b
parent 28 b758351d191f
child 30 239f9bcae806
equal deleted inserted replaced
28:b758351d191f 29:cc9b7e14412b
     1 import os, re, sys
       
     2 
       
     3 from django.conf import settings
       
     4 from django.core.management import call_command
       
     5 from django.db import connection
       
     6 from django.db.backends.creation import TEST_DATABASE_PREFIX
       
     7 from django.contrib.gis.db.backend.util import getstatusoutput
       
     8 
       
     9 def create_lang(db_name, verbosity=1):
       
    10     "Sets up the pl/pgsql language on the given database."
       
    11 
       
    12     # Getting the command-line options for the shell command
       
    13     options = get_cmd_options(db_name)
       
    14 
       
    15     # Constructing the 'createlang' command.
       
    16     createlang_cmd = 'createlang %splpgsql' % options
       
    17     if verbosity >= 1: print createlang_cmd
       
    18 
       
    19     # Must have database super-user privileges to execute createlang -- it must
       
    20     # also be in your path.
       
    21     status, output = getstatusoutput(createlang_cmd)
       
    22 
       
    23     # Checking the status of the command, 0 => execution successful
       
    24     if status:
       
    25         raise Exception("Error executing 'plpgsql' command: %s\n" % output)
       
    26 
       
    27 def _create_with_cursor(db_name, verbosity=1, autoclobber=False):
       
    28     "Creates database with psycopg2 cursor."
       
    29     qn = connection.ops.quote_name
       
    30 
       
    31     # Constructing the necessary SQL to create the database.
       
    32     create_sql = 'CREATE DATABASE %s' % qn(db_name)
       
    33 
       
    34     # If there's a template database for PostGIS set, then use it.
       
    35     if hasattr(settings, 'POSTGIS_TEMPLATE'):
       
    36         create_sql += ' TEMPLATE %s' % qn(settings.POSTGIS_TEMPLATE)
       
    37 
       
    38     # The DATABASE_USER must possess the privileges to create a spatial database.
       
    39     if settings.DATABASE_USER:
       
    40         create_sql += ' OWNER %s' % qn(settings.DATABASE_USER)
       
    41 
       
    42     cursor = connection.cursor()
       
    43     connection.creation.set_autocommit()
       
    44 
       
    45     try:
       
    46         # Trying to create the database first.
       
    47         cursor.execute(create_sql)
       
    48     except Exception, e:
       
    49         if 'already exists' in e.pgerror.lower():
       
    50             # Database already exists, drop and recreate if user agrees.
       
    51             if not autoclobber:
       
    52                 confirm = raw_input("\nIt appears the database, %s, already exists. Type 'yes' to delete it, or 'no' to cancel: " % db_name)
       
    53             if autoclobber or confirm == 'yes':
       
    54                 if verbosity >= 1: print 'Destroying old spatial database...'
       
    55                 drop_db(db_name)
       
    56                 if verbosity >= 1: print 'Creating new spatial database...'
       
    57                 cursor.execute(create_sql)
       
    58             else:
       
    59                 raise Exception('Spatial database creation canceled.')
       
    60         else:
       
    61             raise Exception('Spatial database creation failed: "%s"' % e.pgerror.strip())
       
    62 
       
    63 created_regex = re.compile(r'^createdb: database creation failed: ERROR:  database ".+" already exists')
       
    64 def _create_with_shell(db_name, verbosity=1, autoclobber=False):
       
    65     """
       
    66     If no spatial database already exists, then using a cursor will not work.
       
    67     Thus, a `createdb` command will be issued through the shell to bootstrap
       
    68     creation of the spatial database.
       
    69 
       
    70     TODO: Actually allow this method to be used without a spatial database
       
    71     in place first.
       
    72     """
       
    73     # Getting the command-line options for the shell command
       
    74     options = get_cmd_options(False)
       
    75     if hasattr(settings, 'POSTGIS_TEMPLATE'):
       
    76         options += '-T %s ' % settings.POSTGIS_TEMPlATE
       
    77 
       
    78     create_cmd = 'createdb -O %s %s%s' % (settings.DATABASE_USER, options, db_name)
       
    79     if verbosity >= 1: print create_cmd
       
    80 
       
    81     # Attempting to create the database.
       
    82     status, output = getstatusoutput(create_cmd)
       
    83 
       
    84     if status:
       
    85         if created_regex.match(output):
       
    86             if not autoclobber:
       
    87                 confirm = raw_input("\nIt appears the database, %s, already exists. Type 'yes' to delete it, or 'no' to cancel: " % db_name)
       
    88             if autoclobber or confirm == 'yes':
       
    89                 if verbosity >= 1: print 'Destroying old spatial database...'
       
    90                 drop_cmd = 'dropdb %s%s' % (options, db_name)
       
    91                 status, output = getstatusoutput(drop_cmd)
       
    92                 if status != 0:
       
    93                     raise Exception('Could not drop database %s: %s' % (db_name, output))
       
    94                 if verbosity >= 1: print 'Creating new spatial database...'
       
    95                 status, output = getstatusoutput(create_cmd)
       
    96                 if status != 0:
       
    97                     raise Exception('Could not create database after dropping: %s' % output)
       
    98             else:
       
    99                 raise Exception('Spatial Database Creation canceled.')
       
   100         else:
       
   101             raise Exception('Unknown error occurred in creating database: %s' % output)
       
   102 
       
   103 def create_test_spatial_db(verbosity=1, autoclobber=False, interactive=False):
       
   104     "Creates a test spatial database based on the settings."
       
   105 
       
   106     # Making sure we're using PostgreSQL and psycopg2
       
   107     if settings.DATABASE_ENGINE != 'postgresql_psycopg2':
       
   108         raise Exception('Spatial database creation only supported postgresql_psycopg2 platform.')
       
   109 
       
   110     # Getting the spatial database name
       
   111     db_name = get_spatial_db(test=True)
       
   112     _create_with_cursor(db_name, verbosity=verbosity, autoclobber=autoclobber)
       
   113 
       
   114     # If a template database is used, then don't need to do any of the following.
       
   115     if not hasattr(settings, 'POSTGIS_TEMPLATE'):
       
   116         # Creating the db language, does not need to be done on NT platforms
       
   117         # since the PostGIS installer enables this capability.
       
   118         if os.name != 'nt':
       
   119             create_lang(db_name, verbosity=verbosity)
       
   120 
       
   121         # Now adding in the PostGIS routines.
       
   122         load_postgis_sql(db_name, verbosity=verbosity)
       
   123 
       
   124     if verbosity >= 1: print 'Creation of spatial database %s successful.' % db_name
       
   125 
       
   126     # Closing the connection
       
   127     connection.close()
       
   128     settings.DATABASE_NAME = db_name
       
   129     connection.settings_dict["DATABASE_NAME"] = db_name
       
   130     can_rollback = connection.creation._rollback_works()
       
   131     settings.DATABASE_SUPPORTS_TRANSACTIONS = can_rollback
       
   132     connection.settings_dict["DATABASE_SUPPORTS_TRANSACTIONS"] = can_rollback
       
   133 
       
   134     # Syncing the database
       
   135     call_command('syncdb', verbosity=verbosity, interactive=interactive)
       
   136 
       
   137 def drop_db(db_name=False, test=False):
       
   138     """
       
   139     Drops the given database (defaults to what is returned from
       
   140     get_spatial_db()). All exceptions are propagated up to the caller.
       
   141     """
       
   142     if not db_name: db_name = get_spatial_db(test=test)
       
   143     cursor = connection.cursor()
       
   144     cursor.execute('DROP DATABASE %s' % connection.ops.quote_name(db_name))
       
   145 
       
   146 def get_cmd_options(db_name):
       
   147     "Obtains the command-line PostgreSQL connection options for shell commands."
       
   148     # The db_name parameter is optional
       
   149     options = ''
       
   150     if db_name:
       
   151         options += '-d %s ' % db_name
       
   152     if settings.DATABASE_USER:
       
   153         options += '-U %s ' % settings.DATABASE_USER
       
   154     if settings.DATABASE_HOST:
       
   155         options += '-h %s ' % settings.DATABASE_HOST
       
   156     if settings.DATABASE_PORT:
       
   157         options += '-p %s ' % settings.DATABASE_PORT
       
   158     return options
       
   159 
       
   160 def get_spatial_db(test=False):
       
   161     """
       
   162     Returns the name of the spatial database.  The 'test' keyword may be set
       
   163     to return the test spatial database name.
       
   164     """
       
   165     if test:
       
   166         if settings.TEST_DATABASE_NAME:
       
   167             test_db_name = settings.TEST_DATABASE_NAME
       
   168         else:
       
   169             test_db_name = TEST_DATABASE_PREFIX + settings.DATABASE_NAME
       
   170         return test_db_name
       
   171     else:
       
   172         if not settings.DATABASE_NAME:
       
   173             raise Exception('must configure DATABASE_NAME in settings.py')
       
   174         return settings.DATABASE_NAME
       
   175 
       
   176 def load_postgis_sql(db_name, verbosity=1):
       
   177     """
       
   178     This routine loads up the PostGIS SQL files lwpostgis.sql and
       
   179     spatial_ref_sys.sql.
       
   180     """
       
   181     # Getting the path to the PostGIS SQL
       
   182     try:
       
   183         # POSTGIS_SQL_PATH may be placed in settings to tell GeoDjango where the
       
   184         # PostGIS SQL files are located.  This is especially useful on Win32
       
   185         # platforms since the output of pg_config looks like "C:/PROGRA~1/..".
       
   186         sql_path = settings.POSTGIS_SQL_PATH
       
   187     except AttributeError:
       
   188         status, sql_path = getstatusoutput('pg_config --sharedir')
       
   189         if status:
       
   190             sql_path = '/usr/local/share'
       
   191 
       
   192     # The PostGIS SQL post-creation files.
       
   193     lwpostgis_file = os.path.join(sql_path, 'lwpostgis.sql')
       
   194     srefsys_file = os.path.join(sql_path, 'spatial_ref_sys.sql')
       
   195     if not os.path.isfile(lwpostgis_file):
       
   196         raise Exception('Could not find PostGIS function definitions in %s' % lwpostgis_file)
       
   197     if not os.path.isfile(srefsys_file):
       
   198         raise Exception('Could not find PostGIS spatial reference system definitions in %s' % srefsys_file)
       
   199 
       
   200     # Getting the psql command-line options, and command format.
       
   201     options = get_cmd_options(db_name)
       
   202     cmd_fmt = 'psql %s-f "%%s"' % options
       
   203 
       
   204     # Now trying to load up the PostGIS functions
       
   205     cmd = cmd_fmt % lwpostgis_file
       
   206     if verbosity >= 1: print cmd
       
   207     status, output = getstatusoutput(cmd)
       
   208     if status:
       
   209         raise Exception('Error in loading PostGIS lwgeometry routines.')
       
   210 
       
   211     # Now trying to load up the Spatial Reference System table
       
   212     cmd = cmd_fmt % srefsys_file
       
   213     if verbosity >= 1: print cmd
       
   214     status, output = getstatusoutput(cmd)
       
   215     if status:
       
   216         raise Exception('Error in loading PostGIS spatial_ref_sys table.')
       
   217 
       
   218     # Setting the permissions because on Windows platforms the owner
       
   219     # of the spatial_ref_sys and geometry_columns tables is always
       
   220     # the postgres user, regardless of how the db is created.
       
   221     if os.name == 'nt': set_permissions(db_name)
       
   222 
       
   223 def set_permissions(db_name):
       
   224     """
       
   225     Sets the permissions on the given database to that of the user specified
       
   226     in the settings.  Needed specifically for PostGIS on Win32 platforms.
       
   227     """
       
   228     cursor = connection.cursor()
       
   229     user = settings.DATABASE_USER
       
   230     cursor.execute('ALTER TABLE geometry_columns OWNER TO %s' % user)
       
   231     cursor.execute('ALTER TABLE spatial_ref_sys OWNER TO %s' % user)