| LEFT | RIGHT |
|---|---|
| 1 #!/usr/bin/python2.4 | 1 #!/usr/bin/python2.4 |
| 2 # | 2 # |
| 3 # Copyright 2008 Google Inc. | 3 # Copyright 2008 Google Inc. |
| 4 # | 4 # |
| 5 # Licensed under the Apache License, Version 2.0 (the "License"); | 5 # Licensed under the Apache License, Version 2.0 (the "License"); |
| 6 # you may not use this file except in compliance with the License. | 6 # you may not use this file except in compliance with the License. |
| 7 # You may obtain a copy of the License at | 7 # You may obtain a copy of the License at |
| 8 # | 8 # |
| 9 # http://www.apache.org/licenses/LICENSE-2.0 | 9 # http://www.apache.org/licenses/LICENSE-2.0 |
| 10 # | 10 # |
| 11 # Unless required by applicable law or agreed to in writing, software | 11 # Unless required by applicable law or agreed to in writing, software |
| 12 # distributed under the License is distributed on an "AS IS" BASIS, | 12 # distributed under the License is distributed on an "AS IS" BASIS, |
| 13 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | 13 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| 14 # See the License for the specific language governing permissions and | 14 # See the License for the specific language governing permissions and |
| 15 # limitations under the License. | 15 # limitations under the License. |
| 16 | 16 |
| 17 """Support for integrating a Django project with the appengine infrastructure. | 17 """Support for integrating a Django project with the appengine infrastructure. |
| 18 | 18 |
| 19 This works with both Django 0.96 (via much monkey patching) and Django | 19 This works with both Django 0.96 (via much monkey patching) and Django |
| 20 0.97.pre1. | 20 0.97.pre1. |
| 21 | 21 |
| 22 This module enables you to use the Django manage.py utility and *some* of it's | 22 This module enables you to use the Django manage.py utility and *some* of it's |
| 23 subcommands. View the help of manage.py for exact details. | 23 subcommands. View the help of manage.py for exact details. |
| 24 | 24 |
| 25 Additionally this module takes care of initialising the datastore (and a test | 25 Additionally this module takes care of initialising the datastore (and a test |
| 26 datastore) so that the Django test infrastructure can be used for your | 26 datastore) so that the Django test infrastructure can be used for your |
| 27 appengine project. | 27 appengine project. |
| 28 | 28 |
| 29 To use this module add the following two lines to your main.py and manage.py | 29 To use this module add the following two lines to your main.py and manage.py |
| 30 scripts at the end of your imports: | 30 scripts at the end of your imports: |
| 31 from appengine_django import InstallAppengineHelperForDjango | 31 from appengine_django import InstallAppengineHelperForDjango |
| 32 InstallAppengineHelperForDjango() | 32 InstallAppengineHelperForDjango() |
| 33 | 33 |
| 34 If you would like to use a version of Django other than that provided by the | 34 If you would like to use a version of Django other than that provided by the |
| 35 system all you need to do is include it in a directory just above this helper, | 35 system all you need to do is include it in a directory just above this helper, |
| 36 eg: | 36 eg: |
| 37 appengine_django/__init__.py - This file | 37 appengine_django/__init__.py - This file |
| 38 django/... - your private copy of Django. | 38 django/... - your private copy of Django. |
| 39 """ | 39 """ |
| 40 | 40 |
| 41 import logging | 41 import logging |
| 42 import os | 42 import os |
| 43 import sys | 43 import sys |
| 44 | 44 |
| 45 | 45 |
| 46 DIR_PATH = os.path.abspath(os.path.dirname(__file__)) | 46 DIR_PATH = os.path.abspath(os.path.dirname(__file__)) |
| 47 PARENT_DIR = os.path.dirname(DIR_PATH) | 47 PARENT_DIR = os.path.dirname(DIR_PATH) |
| 48 | 48 |
| 49 # Add this project to the start of sys path to enable direct imports. | 49 # Add this project to the start of sys path to enable direct imports. |
| 50 sys.path = [PARENT_DIR,] + sys.path | 50 sys.path = [PARENT_DIR,] + sys.path |
| 51 | 51 |
| 52 # Try to import the appengine code from the system path. | 52 # Try to import the appengine code, if it fails check for a local copy of |
| 53 # the SDK to add to the sys.path | |
| 53 try: | 54 try: |
| 54 from google.appengine.api import apiproxy_stub_map | 55 from google.appengine.api import apiproxy_stub_map |
| 55 except ImportError, e: | 56 except ImportError, e: |
| 56 # Not on the system path. Build a list of alternative paths where it may be. | 57 SDK_PATH = None |
| 57 # First look within the project for a local copy, then look for where the Mac | 58 paths = [os.path.join(PARENT_DIR, 'google_appengine'), |
| 58 # OS SDK installs it. | |
| 59 paths = [os.path.join(PARENT_DIR, '.google_appengine'), | |
| 60 os.path.join(PARENT_DIR, 'google_appengine'), | |
| 61 '/usr/local/google_appengine'] | 59 '/usr/local/google_appengine'] |
| 62 # Then if on windows, look for where the Windows SDK installed it. | |
| 63 try: | |
| 64 import win32api | |
| 65 ROOT_PATH = os.path.dirname(win32api.GetWindowsDirectory()) | |
| 66 paths.append(os.path.join(ROOT_PATH, 'Program Files', 'Google', | |
| 67 'google_appengine')) | |
| 68 except ImportError, e: | |
| 69 # Not windows. | |
| 70 pass | |
| 71 # Loop through all possible paths and look for the SDK dir. | |
| 72 SDK_PATH = None | |
| 73 for sdk_path in paths: | 60 for sdk_path in paths: |
| 74 if os.path.exists(sdk_path): | 61 if os.path.exists(sdk_path): |
| 75 SDK_PATH = sdk_path | 62 SDK_PATH = sdk_path |
| 76 break | 63 break |
| 77 if SDK_PATH is None: | 64 if SDK_PATH is None: |
| 78 # The SDK could not be found in any known location. | 65 raise |
| 79 sys.stderr.write("The Google App Engine SDK could not be found!\n") | |
| 80 sys.stderr.write("See README for installation instructions.\n") | |
| 81 sys.exit(1) | |
| 82 if SDK_PATH == os.path.join(PARENT_DIR, 'google_appengine'): | |
| 83 logging.warn('Loading the SDK from the \'google_appengine\' subdirectory ' | |
| 84 'is now deprecated!') | |
| 85 logging.warn('Please move the SDK to a subdirectory named ' | |
| 86 '\'.google_appengine\' instead.') | |
| 87 logging.warn('See README for further details.') | |
| 88 # Add the SDK and the libraries within it to the system path. | |
| 89 EXTRA_PATHS = [ | 66 EXTRA_PATHS = [ |
| 90 SDK_PATH, | 67 SDK_PATH, |
| 91 os.path.join(SDK_PATH, 'lib', 'django'), | 68 os.path.join(SDK_PATH, 'lib', 'django'), |
| 92 os.path.join(SDK_PATH, 'lib', 'webob'), | 69 os.path.join(SDK_PATH, 'lib', 'webob'), |
| 93 os.path.join(SDK_PATH, 'lib', 'yaml', 'lib'), | 70 os.path.join(SDK_PATH, 'lib', 'yaml', 'lib'), |
| 94 ] | 71 ] |
| 95 sys.path = EXTRA_PATHS + sys.path | 72 sys.path.extend(EXTRA_PATHS) |
| 96 from google.appengine.api import apiproxy_stub_map | 73 from google.appengine.api import apiproxy_stub_map |
| 97 | 74 |
| 98 | 75 |
| 99 # Remove the standard version of Django if a local copy has been provided. | 76 # Remove the standard version of Django if a local copy has been provided. |
| 100 if os.path.exists(os.path.join(PARENT_DIR, 'django')): | 77 if os.path.exists(os.path.join(PARENT_DIR, 'django')): |
| 101 for k in [k for k in sys.modules if k.startswith('django')]: | 78 for k in [k for k in sys.modules if k.startswith('django')]: |
| 102 del sys.modules[k] | 79 del sys.modules[k] |
| 103 | 80 |
| 104 from django import VERSION | 81 from django import VERSION |
| 105 from django.conf import settings | 82 from django.conf import settings |
| 106 | 83 |
| 107 | 84 |
| 108 # Flags made available this module | 85 # Flags made available this module |
| 109 appid = None | 86 appid = None |
| 110 appconfig = None | 87 appconfig = None |
| 111 have_appserver = False | 88 have_appserver = False |
| 112 | 89 |
| 113 # Hide everything other than the flags above and the install function. | 90 # Hide everything other than the flags above and the install function. |
| 114 __all__ = ("appid", "appconfig", "have_appserver", | 91 __all__ = ("appid", "appconfig", "have_appserver", |
| 115 "InstallAppengineHelperForDjango") | 92 "InstallAppengineHelperForDjango") |
| 116 | 93 |
| 117 | 94 |
| 118 INCOMPATIBLE_COMMANDS = ["adminindex", "createcachetable", "dbshell", | 95 INCOMPATIBLE_COMMANDS = ["adminindex", "createcachetable", "dbshell", |
| 119 "inspectdb", "runfcgi", "syncdb", "validate"] | 96 "inspectdb", "runfcgi", "syncdb", "validate"] |
| 120 | 97 |
| 121 | 98 |
| 122 def LoadAppengineEnvironment(): | 99 def LoadAppengineEnvironment(): |
| 123 """Loads the appengine environment. | 100 """Loads the appengine environment. |
| 124 | 101 |
| 125 Returns: | 102 Returns: |
| 126 This function has no return value, but it sets the following parameters on | 103 This function has no return value, but it sets the following parameters on |
| 127 this package: | 104 this package: |
| 128 - appid: The name of the application as read from the config file. | 105 - appid: The name of the application as read from the config file. |
| 129 - appconfig: The appserver configuration dictionary for the application, as | 106 - appconfig: The appserver configuration dictionary for the application, as |
| 130 read from the config file. | 107 read from the config file. |
| 131 - have_appserver: Boolean parameter which is True if the code is being run | 108 - have_appserver: Boolean parameter which is True if the code is being run |
| 132 from within the appserver environment. | 109 from within the appserver environment. |
| 133 """ | 110 """ |
| 134 global appid, appconfig, have_appserver | 111 global appid, appconfig, have_appserver |
| 135 | 112 |
| 136 # Load the application configuration. | 113 # Load the application configuration. |
| 137 try: | 114 try: |
| 138 from google.appengine.tools import dev_appserver | 115 from google.appengine.tools import dev_appserver |
| 139 appconfig, unused_matcher = dev_appserver.LoadAppConfig(".", {}) | 116 appconfig, unused_matcher = dev_appserver.LoadAppConfig(".", {}) |
| 140 appid = appconfig.application | 117 appid = appconfig.application |
| 141 except ImportError: | 118 except ImportError: |
| 142 # Running under the real appserver. | 119 # Running under the real appserver. |
| 143 appconfig = {} | 120 appconfig = {} |
| 144 appid = "unknown" | 121 appid = "unknown" |
| 145 | 122 |
| 146 # Detect if we are running under an appserver. | 123 # Detect if we are running under an appserver. |
| 147 have_appserver = False | 124 have_appserver = False |
| 148 stub = apiproxy_stub_map.apiproxy.GetStub("datastore_v3") | 125 stub = apiproxy_stub_map.apiproxy.GetStub("datastore_v3") |
| 149 if stub: | 126 if stub: |
| 150 have_appserver = True | 127 have_appserver = True |
| 151 logging.debug("Loading application '%s' %s an appserver" % | 128 logging.debug("Loading application '%s' %s an appserver" % |
| 152 (appid, have_appserver and "with" or "without")) | 129 (appid, have_appserver and "with" or "without")) |
| 153 | 130 |
| 154 | 131 |
| 155 def InstallAppengineDatabaseBackend(): | 132 def InstallAppengineDatabaseBackend(): |
| 156 """Installs the appengine database backend into Django. | 133 """Installs the appengine database backend into Django. |
| 157 | 134 |
| 158 The appengine database lives in the db/ subdirectory of this package, but is | 135 The appengine database lives in the db/ subdirectory of this package, but is |
| 159 known as "appengine" to Django. This function installs the module where | 136 known as "appengine" to Django. This function installs the module where |
| 160 Django expects to find its database backends. | 137 Django expects to find its database backends. |
| 161 """ | 138 """ |
| 162 from appengine_django import db | 139 from appengine_django import db |
| 163 sys.modules['django.db.backends.appengine'] = db | 140 sys.modules['django.db.backends.appengine'] = db |
| 164 PatchTestDBCreationFunctions() | 141 PatchTestDBCreationFunctions() |
| 165 logging.debug("Installed appengine database backend") | 142 logging.debug("Installed appengine database backend") |
| 166 | 143 |
| 167 | 144 |
| 168 def PatchTestDBCreationFunctions(): | 145 def PatchTestDBCreationFunctions(): |
| 169 """Installs the functions that create/remove the test database. | 146 """Installs the functions that create/remove the test database. |
| 170 | 147 |
| 171 Only required for Django 0.96. Django 0.97 finds these functions in the | 148 Only required for Django 0.96. Django 0.97 finds these functions in the |
| 172 backend and uses them automatically. Also skipped if running under an | 149 backend and uses them automatically. Also skipped if running under an |
| 173 appserver. | 150 appserver. |
| 174 """ | 151 """ |
| 175 if VERSION >= (0, 97, None) or have_appserver: | 152 if VERSION >= (0, 97, None) or have_appserver: |
| 176 return | 153 return |
| 177 from django.test import utils | 154 from django.test import utils |
| 178 from appengine_django.db import creation | 155 from appengine_django.db import creation |
| 179 utils.create_test_db = creation.create_test_db | 156 utils.create_test_db = creation.create_test_db |
| 180 utils.destroy_test_db = creation.destroy_test_db | 157 utils.destroy_test_db = creation.destroy_test_db |
| 181 logging.debug("Installed test database create/destroy functions") | 158 logging.debug("Installed test database create/destroy functions") |
| 182 | 159 |
| 183 | 160 |
| 184 def PatchDjangoSerializationModules(): | 161 def PatchDjangoSerializationModules(): |
| 185 """Monkey patches the Django serialization modules. | 162 """Monkey patches the Django serialization modules. |
| 186 | 163 |
| 187 The standard Django serialization modules to not correctly handle the | 164 The standard Django serialization modules to not correctly handle the |
| 188 datastore models provided by this package. This method installs replacements | 165 datastore models provided by this package. This method installs replacements |
| 189 for selected modules and methods to give Django the capability to correctly | 166 for selected modules and methods to give Django the capability to correctly |
| 190 serialize and deserialize datastore models. | 167 serialize and deserialize datastore models. |
| 191 """ | 168 """ |
| 192 # These can't be imported until InstallAppengineDatabaseBackend has run. | 169 # These can't be imported until InstallAppengineDatabaseBackend has run. |
| 193 from django.core.serializers import python | 170 from django.core.serializers import python |
| 194 from appengine_django.serializer.python import Deserializer | 171 from appengine_django.serializer.python import Deserializer |
| 195 if not hasattr(settings, "SERIALIZATION_MODULES"): | 172 if not hasattr(settings, "SERIALIZATION_MODULES"): |
| 196 settings.SERIALIZATION_MODULES = {} | 173 settings.SERIALIZATION_MODULES = {} |
| 197 base_module = "appengine_django" | 174 base_module = "appengine_django" |
| 198 settings.SERIALIZATION_MODULES["xml"] = "%s.serializer.xml" % base_module | 175 settings.SERIALIZATION_MODULES["xml"] = "%s.serializer.xml" % base_module |
| 199 python.Deserializer = Deserializer | 176 python.Deserializer = Deserializer |
| 200 PatchDeserializedObjectClass() | 177 PatchDeserializedObjectClass() |
| 201 DisableModelValidation() | 178 DisableModelValidation() |
| 202 logging.debug("Installed appengine json and python serialization modules") | 179 logging.debug("Installed appengine json and python serialization modules") |
| 203 | 180 |
| 204 | 181 |
| 205 def PatchDeserializedObjectClass(): | 182 def PatchDeserializedObjectClass(): |
| 206 """Patches the DeserializedObject class. | 183 """Patches the DeserializedObject class. |
| 207 | 184 |
| 208 The default implementation calls save directly on the django Model base | 185 The default implementation calls save directly on the django Model base |
| 209 class to avoid pre-save handlers. The model class provided by this package | 186 class to avoid pre-save handlers. The model class provided by this package |
| 210 is not derived from the Django Model class and therefore must be called | 187 is not derived from the Django Model class and therefore must be called |
| 211 directly. | 188 directly. |
| 212 | 189 |
| 213 Additionally we need to clear the internal _parent attribute as it may | 190 Only required for Django 0.97. |
| 214 contain a FakeParent class that is used to deserialize instances without | 191 """ |
| 215 needing to load the parent instance itself. See the PythonDeserializer for | 192 if VERSION < (0, 97, None): |
| 216 more details. | 193 return |
| 217 """ | |
| 218 # This can't be imported until InstallAppengineDatabaseBackend has run. | 194 # This can't be imported until InstallAppengineDatabaseBackend has run. |
| 219 from django.core.serializers import base | 195 from django.core.serializers import base |
| 220 class NewDeserializedObject(base.DeserializedObject): | 196 class NewDeserializedObject(base.DeserializedObject): |
| 221 def save(self, save_m2m=True): | 197 def save(self, save_m2m=True): |
| 222 self.object.save() | 198 self.object.save() |
| 223 self.object._parent = None | |
| 224 base.DeserializedObject = NewDeserializedObject | 199 base.DeserializedObject = NewDeserializedObject |
| 225 logging.debug("Replacement DeserializedObject class installed") | 200 logging.debug("Replacement DeserializedObject class installed") |
| 226 | 201 |
| 227 def DisableModelValidation(): | 202 def DisableModelValidation(): |
| 228 """Disables Django's model validation routines. | 203 """Disables Django's model validation routines. |
| 229 | 204 |
| 230 The model validation is primarily concerned with validating foreign key | 205 The model validation is primarily concerned with validating foreign key |
| 231 references. There is no equivalent checking code for datastore References at | 206 references. There is no equivalent checking code for datastore References at |
| 232 this time. | 207 this time. |
| 233 | 208 |
| 234 Validation needs to be disabled or serialization/deserialization will fail. | 209 Validation needs to be disabled or serialization/deserialization will fail. |
| 235 """ | 210 """ |
| 236 # Imports must be performed in this function as they differ between versions. | 211 # Imports must be performed in this function as they differ between versions. |
| 237 if VERSION < (0, 97, None): | 212 if VERSION < (0, 97, None): |
| 238 from django.core import management | 213 from django.core import management |
| 239 management.get_validation_errors = lambda x, y=0: 0 | 214 management.get_validation_errors = lambda x, y=0: 0 |
| 240 else: | 215 else: |
| 241 from django.core.management import validation | 216 from django.core.management import validation |
| 242 validation.get_validation_errors = lambda x, y=0: 0 | 217 validation.get_validation_errors = lambda x, y=0: 0 |
| 243 logging.debug("Django SQL model validation disabled") | 218 logging.debug("Django SQL model validation disabled") |
| 244 | 219 |
| 245 def CleanupDjangoSettings(): | 220 def CleanupDjangoSettings(): |
| 246 """Removes incompatible entries from the django settings module.""" | 221 """Removes incompatible entries from the django settings module.""" |
| 247 | 222 |
| 248 # Ensure this module is installed as an application. | 223 # Ensure this module is installed as an application. |
| 249 apps = getattr(settings, "INSTALLED_APPS", ()) | 224 apps = getattr(settings, "INSTALLED_APPS", ()) |
| 250 found = False | 225 found = False |
| 251 for app in apps: | 226 for app in apps: |
| 252 if app.endswith("appengine_django"): | 227 if app.endswith("appengine_django"): |
| 253 found = True | 228 found = True |
| 254 break | 229 break |
| 255 if not found: | 230 if not found: |
| 256 logging.warn("appengine_django module is not listed as an application!") | 231 logging.warn("appengine_django module is not listed as an application!") |
| 257 apps += ("appengine_django",) | 232 apps += ("appengine_django",) |
| 258 setattr(settings, "INSTALLED_APPS", apps) | 233 setattr(settings, "INSTALLED_APPS", apps) |
| 259 logging.info("Added 'appengine_django' as an application") | 234 logging.info("Added 'appengine_django' as an application") |
| 260 | 235 |
| 261 # Ensure the database backend is appropriately configured. | 236 # Ensure the database backend is appropriately configured. |
| 262 dbe = getattr(settings, "DATABASE_ENGINE", "") | 237 dbe = getattr(settings, "DATABASE_ENGINE", "") |
| 263 if dbe != "appengine": | 238 if dbe != "appengine": |
| 264 settings.DATABASE_ENGINE = "appengine" | 239 settings.DATABASE_ENGINE = "appengine" |
| 265 logging.warn("DATABASE_ENGINE is not configured as 'appengine'. " | 240 logging.warn("DATABASE_ENGINE is not configured as 'appengine'. " |
| 266 "Value overriden!") | 241 "Value overriden!") |
| 267 for var in ["NAME", "USER", "PASSWORD", "HOST", "PORT"]: | 242 for var in ["NAME", "USER", "PASSWORD", "HOST", "PORT"]: |
| 268 val = getattr(settings, "DATABASE_%s" % var, "") | 243 val = getattr(settings, "DATABASE_%s" % var, "") |
| 269 if val: | 244 if val: |
| 270 setattr(settings, "DATABASE_%s" % var, "") | 245 setattr(settings, "DATABASE_%s" % var, "") |
| 271 logging.warn("DATABASE_%s should be blank. Value overriden!") | 246 logging.warn("DATABASE_%s should be blank. Value overriden!") |
| 272 | 247 |
| 273 # Remove incompatible middleware modules. | 248 # Remove incompatible middleware modules. |
| 274 mw_mods = list(getattr(settings, "MIDDLEWARE_CLASSES", ())) | 249 mw_mods = list(getattr(settings, "MIDDLEWARE_CLASSES", ())) |
| 275 disallowed_middleware_mods = ( | 250 disallowed_middleware_mods = ( |
| 276 'django.contrib.sessions.middleware.SessionMiddleware', | 251 'django.contrib.sessions.middleware.SessionMiddleware', |
| 277 'django.middleware.doc.XViewMiddleware',) | 252 'django.middleware.doc.XViewMiddleware',) |
| 278 for modname in mw_mods[:]: | 253 for modname in mw_mods[:]: |
| 279 if modname in disallowed_middleware_mods: | 254 if modname in disallowed_middleware_mods: |
| 280 # Currently only the CommonMiddleware has been ported. As other base modu les | 255 # Currently only the CommonMiddleware has been ported. As other base modu les |
| 281 # are converted, remove from the disallowed_middleware_mods tuple. | 256 # are converted, remove from the disallowed_middleware_mods tuple. |
| 282 mw_mods.remove(modname) | 257 mw_mods.remove(modname) |
| 283 logging.warn("Middleware module '%s' is not compatible. Removed!" % | 258 logging.warn("Middleware module '%s' is not compatible. Removed!" % |
| 284 modname) | 259 modname) |
| 285 setattr(settings, "MIDDLEWARE_CLASSES", tuple(mw_mods)) | 260 setattr(settings, "MIDDLEWARE_CLASSES", tuple(mw_mods)) |
| 286 | 261 |
| 287 # Remove incompatible application modules | 262 # Remove incompatible application modules |
| 288 app_mods = list(getattr(settings, "INSTALLED_APPS", ())) | 263 app_mods = list(getattr(settings, "INSTALLED_APPS", ())) |
| 289 disallowed_apps = ( | 264 allowed_apps = ("django.contrib.auth",) |
|
mattbrown.nz
2008/05/18 11:23:43
We're using a blacklist for middleware modules, fo
aalbrecht
2008/05/18 19:17:47
On 2008/05/18 11:23:43, mattbrown.nz wrote:
> We'r
| |
| 290 'django.contrib.contenttypes', | |
| 291 'django.contrib.sessions', | |
| 292 'django.contrib.sites',) | |
| 293 for app in app_mods[:]: | 265 for app in app_mods[:]: |
| 294 if app in disallowed_apps: | 266 if app.startswith("django.") and app not in allowed_apps: |
| 295 # TODO(mglb): Again this is probably overly broad. | 267 # TODO(mglb): Again this is probably overly broad. |
| 296 app_mods.remove(app) | 268 app_mods.remove(app) |
| 297 logging.warn("Application module '%s' is not compatible. Removed!" % app) | 269 logging.warn("Application module '%s' is not compatible. Removed!" % app) |
| 298 setattr(settings, "INSTALLED_APPS", tuple(app_mods)) | 270 setattr(settings, "INSTALLED_APPS", tuple(app_mods)) |
| 271 | |
| 272 # Remove incompatible context processors. | |
| 273 bad_processors = tuple() | |
| 274 ctx_procs = list(getattr(settings, "TEMPLATE_CONTEXT_PROCESSORS", ())) | |
| 275 for proc in ctx_procs[:]: | |
| 276 if proc in bad_processors: | |
| 277 ctx_procs.remove(proc) | |
| 278 logging.warn("Template Context Processor '%s' is incompatible. Removed!" | |
| 279 % proc) | |
| 280 setattr(settings, "TEMPLATE_CONTEXT_PROCESSORS", tuple(ctx_procs)) | |
| 299 | 281 |
| 300 | 282 |
| 301 def ModifyAvailableCommands(): | 283 def ModifyAvailableCommands(): |
| 302 """Removes incompatible commands and installs replacements where possible.""" | 284 """Removes incompatible commands and installs replacements where possible.""" |
| 303 if have_appserver: | 285 if have_appserver: |
| 304 # Commands are not used when running from an appserver. | 286 # Commands are not used when running from an appserver. |
| 305 return | 287 return |
| 306 from django.core import management | 288 from django.core import management |
| 307 if VERSION < (0, 97, None): | 289 if VERSION < (0, 97, None): |
| 308 RemoveCommands(management.DEFAULT_ACTION_MAPPING) | 290 RemoveCommands(management.DEFAULT_ACTION_MAPPING) |
| 309 from appengine_django.management.commands import runserver | 291 from appengine_django.management.commands import runserver |
| 310 management.DEFAULT_ACTION_MAPPING['runserver'] = runserver.v096_command | 292 management.DEFAULT_ACTION_MAPPING['runserver'] = runserver.v096_command |
| 311 from appengine_django.management.commands import flush | 293 from appengine_django.management.commands import flush |
| 312 management.DEFAULT_ACTION_MAPPING['flush'] = flush.v096_command | 294 management.DEFAULT_ACTION_MAPPING['flush'] = flush.v096_command |
| 313 from appengine_django.management.commands import reset | 295 from appengine_django.management.commands import reset |
| 314 management.DEFAULT_ACTION_MAPPING['reset'] = reset.v096_command | 296 management.DEFAULT_ACTION_MAPPING['reset'] = reset.v096_command |
| 315 else: | 297 else: |
| 316 management.get_commands() | 298 management.get_commands() |
| 317 RemoveCommands(management._commands) | 299 RemoveCommands(management._commands) |
| 318 # Django 0.97 will install the replacements automatically. | 300 # Django 0.97 will install the replacements automatically. |
| 319 logging.debug("Removed incompatible Django manage.py commands") | 301 logging.debug("Removed incompatible Django manage.py commands") |
| 320 | 302 |
| 321 | 303 |
| 322 def RemoveCommands(command_dict): | 304 def RemoveCommands(command_dict): |
| 323 """Removes incompatible commands from the specified command dictionary.""" | 305 """Removes incompatible commands from the specified command dictionary.""" |
| 324 for cmd in command_dict.keys(): | 306 for cmd in command_dict.keys(): |
| 325 if cmd.startswith("sql"): | 307 if cmd.startswith("sql"): |
| 326 del command_dict[cmd] | 308 del command_dict[cmd] |
| 327 elif cmd in INCOMPATIBLE_COMMANDS: | 309 elif cmd in INCOMPATIBLE_COMMANDS: |
| 328 del command_dict[cmd] | 310 del command_dict[cmd] |
| 329 | 311 |
| 330 | 312 |
| 331 def InstallReplacementImpModule(): | 313 def InstallReplacementImpModule(): |
| 332 """Install a replacement for the imp module removed by the appserver. | 314 """Install a replacement for the imp module removed by the appserver. |
| 333 | 315 |
| 334 This is only required for Django 0.97 which uses imp.find_module to find | 316 This is only required for Django 0.97 which uses imp.find_module to find |
| 335 mangement modules provided by applications. | 317 mangement modules provided by applications. |
| 336 """ | 318 """ |
| 337 if VERSION < (0, 97, None) or not have_appserver: | 319 if VERSION < (0, 97, None) or not have_appserver: |
| 338 return | 320 return |
| 339 modname = 'appengine_django.replacement_imp' | 321 modname = 'appengine_django.replacement_imp' |
| 340 imp_mod = __import__(modname, {}, [], ['']) | 322 imp_mod = __import__(modname, {}, [], ['']) |
| 341 sys.modules['imp'] = imp_mod | 323 sys.modules['imp'] = imp_mod |
| 342 logging.debug("Installed replacement imp module") | 324 logging.debug("Installed replacement imp module") |
| 343 | 325 |
| 344 | 326 |
| 345 def InstallAppengineHelperForDjango(): | 327 def InstallAppengineHelperForDjango(): |
| 346 """Installs and Patches all of the classes/methods required for integration. | 328 """Installs and Patches all of the classes/methods required for integration. |
| 347 | 329 |
| 348 If the variable DEBUG_APPENGINE_DJANGO is set in the environment verbose | 330 If the variable DEBUG_APPENGINE_DJANGO is set in the environment verbose |
| 349 logging of the actions taken will be enabled. | 331 logging of the actions taken will be enabled. |
| 350 """ | 332 """ |
| 351 if os.getenv("DEBUG_APPENGINE_DJANGO"): | 333 if os.getenv("DEBUG_APPENGINE_DJANGO"): |
| 352 logging.getLogger().setLevel(logging.DEBUG) | 334 logging.getLogger().setLevel(logging.DEBUG) |
| 353 else: | 335 else: |
| 354 logging.getLogger().setLevel(logging.INFO) | 336 logging.getLogger().setLevel(logging.INFO) |
| 355 logging.debug("Loading the Google App Engine Helper for Django...") | 337 logging.debug("Loading the Google App Engine Helper for Django...") |
| 356 | 338 |
| 357 # Force Django to reload its settings. | 339 # Force Django to reload its settings. |
| 358 settings._target = None | 340 settings._target = None |
| 359 | 341 |
| 360 # Must set this env var *before* importing any more of Django. | 342 # Must set this env var *before* importing any more of Django. |
| 361 os.environ['DJANGO_SETTINGS_MODULE'] = 'settings' | 343 os.environ['DJANGO_SETTINGS_MODULE'] = 'settings' |
| 362 | 344 |
| 363 LoadAppengineEnvironment() | 345 LoadAppengineEnvironment() |
| 364 InstallReplacementImpModule() | 346 InstallReplacementImpModule() |
| 365 InstallAppengineDatabaseBackend() | 347 InstallAppengineDatabaseBackend() |
| 366 PatchDjangoSerializationModules() | 348 PatchDjangoSerializationModules() |
| 367 CleanupDjangoSettings() | 349 CleanupDjangoSettings() |
| 368 ModifyAvailableCommands() | 350 ModifyAvailableCommands() |
| 369 InstallGoogleSMTPConnection() | 351 InstallGoogleSMTPConnection() |
| 370 InstallAuthentication() | 352 InstallAuthentication() |
| 371 | 353 |
| 372 logging.debug("Successfully loaded the Google App Engine Helper for Django.") | 354 logging.debug("Successfully loaded the Google App Engine Helper for Django.") |
| 373 | 355 |
| 374 | 356 |
| 375 def InstallGoogleSMTPConnection(): | 357 def InstallGoogleSMTPConnection(): |
| 376 from appengine_django import mail as gmail | 358 from appengine_django import mail as gmail |
| 377 from django.core import mail | 359 from django.core import mail |
| 378 if VERSION >= (0, 97, None): | 360 if VERSION >= (0, 97, None): |
| 379 logging.debug("Installing Google Email Adapter for Django 0.97+") | 361 logging.debug("Installing Google Email Adapter for Django 0.97+") |
| 380 mail.SMTPConnection = gmail.GoogleSMTPConnection | 362 mail.SMTPConnection = gmail.GoogleSMTPConnection |
| 381 else: | 363 else: |
| 382 logging.debug("Installing Google Email Adapter for Django 0.96") | 364 logging.debug("Installing Google Email Adapter for Django 0.96") |
| 383 mail.send_mass_mail = gmail.send_mass_mail | 365 mail.send_mass_mail = gmail.send_mass_mail |
| 384 mail.mail_admins = gmail.mail_admins | 366 mail.mail_admins = gmail.mail_admins |
| 385 mail.mail_managers = gmail.mail_managers | 367 mail.mail_managers = gmail.mail_managers |
| 386 | 368 |
| 387 def InstallAuthentication(): | 369 def InstallAuthentication(): |
| 388 logging.debug("Installing authentication framework") | 370 logging.debug("Installing authentication framework") |
| 389 from django.contrib.auth import models as django_models | 371 from django.contrib.auth import models as django_models |
| 390 from appengine_django.auth.models import DjangoUser | 372 from appengine_django.auth.models import DjangoUser |
| 391 django_models.User = DjangoUser | 373 django_models.User = DjangoUser |
| 392 from django.contrib.auth import middleware as django_middleware | 374 from django.contrib.auth import middleware as django_middleware |
| 393 from appengine_django.auth import middleware | 375 from appengine_django.auth import middleware |
| 394 django_middleware.AuthenticationMiddleware = middleware.AuthenticationMiddlewa re | 376 django_middleware.AuthenticationMiddleware = middleware.AuthenticationMiddlewa re |
| 395 if VERSION >= (0, 97, None): | |
| 396 from appengine_django.auth import tests | |
| 397 from django.contrib.auth import tests as django_tests | |
| 398 django_tests.__doc__ = tests.__doc__ | |
| LEFT | RIGHT |