djangoappengine / db /

from ..utils import appid, have_appserver, on_production_server
from ..boot import DATA_ROOT
from .creation import DatabaseCreation
from django.db.backends.util import format_number
from djangotoolbox.db.base import NonrelDatabaseFeatures, \
    NonrelDatabaseOperations, NonrelDatabaseWrapper, NonrelDatabaseClient, \
    NonrelDatabaseValidation, NonrelDatabaseIntrospection
from urllib2 import HTTPError, URLError
import logging
import os
import time

REMOTE_API_SCRIPT = '$PYTHON_LIB/google/appengine/ext/remote_api/'

def auth_func():
    import getpass
    return raw_input('Login via Google Account (see note above if login fails): '), getpass.getpass('Password: ')

def rpc_server_factory(*args, ** kwargs):
    from import appengine_rpc
    kwargs['save_cookies'] = True
    return appengine_rpc.HttpRpcServer(*args, ** kwargs)

def get_datastore_paths(options):
    """Returns a tuple with the path to the datastore and history file.

    The datastore is stored in the same location as dev_appserver uses by
    default, but the name is altered to be unique to this project so multiple
    Django projects can be developed on the same machine in parallel.

      (datastore_path, history_path)
    from import dev_appserver_main
    datastore_path = options.get('datastore_path',
                                 os.path.join(DATA_ROOT, 'datastore'))
    blobstore_path = options.get('blobstore_path',
                                 os.path.join(DATA_ROOT, 'blobstore'))
    history_path = options.get('history_path',
                               os.path.join(DATA_ROOT, 'history'))
    return datastore_path, blobstore_path, history_path

def get_test_datastore_paths(inmemory=True):
    """Returns a tuple with the path to the test datastore and history file.

    If inmemory is true, (None, None) is returned to request an in-memory
    datastore. If inmemory is false the path returned will be similar to the path
    returned by get_datastore_paths but with a different name.

      (datastore_path, history_path)
    if inmemory:
        return None, None, None
    datastore_path, blobstore_path, history_path = get_datastore_paths()
    datastore_path = datastore_path.replace('.datastore', '.testdatastore')
    blobstore_path = blobstore_path.replace('.blobstore', '.testblobstore')
    history_path = history_path.replace('.datastore', '.testdatastore')
    return datastore_path, blobstore_path, history_path

def destroy_datastore(*args):
    """Destroys the appengine datastore at the specified paths."""
    for path in args:
        if not path:
        except OSError, error:
            if error.errno != 2:
                logging.error("Failed to clear datastore: %s" % error)

class DatabaseFeatures(NonrelDatabaseFeatures):
    allows_primary_key_0 = True
    supports_dicts = True

class DatabaseOperations(NonrelDatabaseOperations):
    compiler_module = __name__.rsplit('.', 1)[0] + '.compiler'

    def value_to_db_decimal(self, value, max_digits, decimal_places):
        if value is None: 
            return None
        sign = value < 0 and u'-' or u''
        if sign: 
            value = abs(value)
        if max_digits is None: 
            max_digits = self.DEFAULT_MAX_DIGITS

        if decimal_places is None:
            value = unicode(value)
            value = format_number(value, max_digits, decimal_places)
        decimal_places = decimal_places or 0
        n = value.find('.')

        if n < 0:
            n = len(value)
        if n < max_digits - decimal_places:
            value = u"0" * (max_digits - decimal_places - n) + value
        return sign + value

    def sql_flush(self, style, tables, sequences):
        return []

class DatabaseClient(NonrelDatabaseClient):

class DatabaseValidation(NonrelDatabaseValidation):

class DatabaseIntrospection(NonrelDatabaseIntrospection):

class DatabaseWrapper(NonrelDatabaseWrapper):
    def __init__(self, *args, **kwds):
        super(DatabaseWrapper, self).__init__(*args, **kwds)
        self.features = DatabaseFeatures(self)
        self.ops = DatabaseOperations(self)
        self.client = DatabaseClient(self)
        self.creation = DatabaseCreation(self)
        self.validation = DatabaseValidation(self)
        self.introspection = DatabaseIntrospection(self)
        options = self.settings_dict
        self.use_test_datastore = False
        self.test_datastore_inmemory = True
        self.remote = options.get('REMOTE', False)
        if on_production_server:
            self.remote = False
        self.remote_app_id = options.get('REMOTE_APP_ID', appid)
        self.high_replication = options.get('HIGH_REPLICATION', False)
        self.domain = options.get('DOMAIN', '')
        self.remote_api_path = options.get('REMOTE_API_PATH', None)
        self.secure_remote_api = options.get('SECURE_REMOTE_API', True)

    def _get_paths(self):
        if self.use_test_datastore:
            return get_test_datastore_paths(self.test_datastore_inmemory)
            return get_datastore_paths(self.settings_dict)

    def _setup_stubs(self):
        # If this code is being run without an appserver (eg. via a django
        # commandline flag) then setup a default stub environment.
        if not have_appserver:
            from import dev_appserver_main
            args = dev_appserver_main.DEFAULT_ARGS.copy()
            args['datastore_path'], args['blobstore_path'], args['history_path'] = self._get_paths()
            from import dev_appserver
            dev_appserver.SetupStubs(appid, **args)
        # If we're supposed to set up the remote_api, do that now.
        if self.remote:

    def setup_remote(self):
        if not self.remote_api_path:
            from ..utils import appconfig
            for handler in appconfig.handlers:
                if handler.script == REMOTE_API_SCRIPT:
                    self.remote_api_path = handler.url.split('(', 1)[0]
        self.remote = True
        server = '%s.%s' % (self.remote_app_id, self.domain)
        remote_url = 'https://%s%s' % (server, self.remote_api_path)'Setting up remote_api for "%s" at %s' %
                     (self.remote_app_id, remote_url))
        if not have_appserver:
            print('Connecting to remote_api handler.\n\n'
                  'IMPORTANT: Check your login method settings in the '
                  'App Engine Dashboard if you have problems logging in. '
                  'Login is only supported for Google Accounts.\n')
        from google.appengine.ext.remote_api import remote_api_stub
        remote_app_id = self.remote_app_id
        if self.high_replication:
            remote_app_id = 's~' + remote_app_id
            self.remote_api_path, auth_func, servername=server,
        retry_delay = 1
        while retry_delay <= 16:
            except HTTPError, e:
                if not have_appserver:
                    print 'Retrying in %d seconds...' % retry_delay
                retry_delay *= 2
            except HTTPError, e:
                raise URLError("%s\n"
                               "Couldn't reach remote_api handler at %s.\n"
                               "Make sure you've deployed your project and "
                               "installed a remote_api handler in app.yaml. "
                               "Note that login is only supported for "
                               "Google Accounts. Make sure you've configured "
                               "the correct authentication method in the "
                               "App Engine Dashboard."
                               % (e, remote_url))'Now using the remote datastore for "%s" at %s' %
                     (self.remote_app_id, remote_url))

    def flush(self):
        """Helper function to remove the current datastore and re-open the stubs"""
        if self.remote:
            import random, string
            code = ''.join([random.choice(string.ascii_letters) for x in range(4)])
            print '\n\n!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!'
            print '!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!'
            print "Warning! You're about to delete the *production* datastore!"
            print 'Only models defined in your INSTALLED_APPS can be removed!'
            print 'If you want to clear the whole datastore you have to use the ' \
                  'datastore viewer in the dashboard. Also, in order to delete all '\
                  'unneeded indexes you have to run vacuum_indexes.'
            print 'In order to proceed you have to enter the following code:'
            print code
            response = raw_input('Repeat: ')
            if code == response:
                print 'Deleting...'
                from django.db import models
                from google.appengine.api import datastore as ds
                for model in models.get_models():
                    print 'Deleting %s...' % model._meta.db_table
                    while True:
                        data = ds.Query(model._meta.db_table, keys_only=True).Get(200)
                        if not data:
                print "Datastore flushed! Please check your dashboard's " \
                      'datastore viewer for any remaining entities and remove ' \
                      'all unneeded indexes with vacuum_indexes.'
                print 'Aborting'