| OLD | NEW |
| 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 |
| (...skipping 176 matching lines...) Show 10 above Show 10 below |
| 227 def DisableModelValidation(): | 227 def DisableModelValidation(): |
| 228 """Disables Django's model validation routines. | 228 """Disables Django's model validation routines. |
| 229 | 229 |
| 230 The model validation is primarily concerned with validating foreign key | 230 The model validation is primarily concerned with validating foreign key |
| 231 references. There is no equivalent checking code for datastore References at | 231 references. There is no equivalent checking code for datastore References at |
| 232 this time. | 232 this time. |
| 233 | 233 |
| 234 Validation needs to be disabled or serialization/deserialization will fail. | 234 Validation needs to be disabled or serialization/deserialization will fail. |
| 235 """ | 235 """ |
| 236 # Imports must be performed in this function as they differ between versions. | 236 # Imports must be performed in this function as they differ between versions. |
| 237 if VERSION < (0, 97, None): | 237 if VERSION < (0, 97, None): |
| 238 from django.core import management | 238 from django.core import management |
| 239 management.get_validation_errors = lambda x, y=0: 0 | 239 management.get_validation_errors = lambda x, y=0: 0 |
| 240 else: | 240 else: |
| 241 from django.core.management import validation | 241 from django.core.management import validation |
| 242 validation.get_validation_errors = lambda x, y=0: 0 | 242 validation.get_validation_errors = lambda x, y=0: 0 |
| 243 logging.debug("Django SQL model validation disabled") | 243 logging.debug("Django SQL model validation disabled") |
| 244 | 244 |
| 245 def CleanupDjangoSettings(): | 245 def CleanupDjangoSettings(): |
| 246 """Removes incompatible entries from the django settings module.""" | 246 """Removes incompatible entries from the django settings module.""" |
| 247 | 247 |
| 248 # Ensure this module is installed as an application. | 248 # Ensure this module is installed as an application. |
| 249 apps = getattr(settings, "INSTALLED_APPS", ()) | 249 apps = getattr(settings, "INSTALLED_APPS", ()) |
| 250 found = False | 250 found = False |
| 251 for app in apps: | 251 for app in apps: |
| 252 if app.endswith("appengine_django"): | 252 if app.endswith("appengine_django"): |
| 253 found = True | 253 found = True |
| 254 break | 254 break |
| 255 if not found: | 255 if not found: |
| 256 logging.warn("appengine_django module is not listed as an application!") | 256 logging.warn("appengine_django module is not listed as an application!") |
| 257 apps += ("appengine_django",) | 257 apps += ("appengine_django",) |
| 258 setattr(settings, "INSTALLED_APPS", apps) | 258 setattr(settings, "INSTALLED_APPS", apps) |
| 259 logging.info("Added 'appengine_django' as an application") | 259 logging.info("Added 'appengine_django' as an application") |
| 260 | 260 |
| 261 # Ensure the database backend is appropriately configured. | 261 # Ensure the database backend is appropriately configured. |
| 262 dbe = getattr(settings, "DATABASE_ENGINE", "") | 262 dbe = getattr(settings, "DATABASE_ENGINE", "") |
| 263 if dbe != "appengine": | 263 if dbe != "appengine": |
| 264 settings.DATABASE_ENGINE = "appengine" | 264 settings.DATABASE_ENGINE = "appengine" |
| 265 logging.warn("DATABASE_ENGINE is not configured as 'appengine'. " | 265 logging.warn("DATABASE_ENGINE is not configured as 'appengine'. " |
| 266 "Value overriden!") | 266 "Value overriden!") |
| 267 for var in ["NAME", "USER", "PASSWORD", "HOST", "PORT"]: | 267 for var in ["NAME", "USER", "PASSWORD", "HOST", "PORT"]: |
| 268 val = getattr(settings, "DATABASE_%s" % var, "") | 268 val = getattr(settings, "DATABASE_%s" % var, "") |
| 269 if val: | 269 if val: |
| 270 setattr(settings, "DATABASE_%s" % var, "") | 270 setattr(settings, "DATABASE_%s" % var, "") |
| 271 logging.warn("DATABASE_%s should be blank. Value overriden!") | 271 logging.warn("DATABASE_%s should be blank. Value overriden!") |
| 272 | 272 |
| 273 # Remove incompatible middleware modules. | 273 # Remove incompatible middleware modules. |
| 274 mw_mods = list(getattr(settings, "MIDDLEWARE_CLASSES", ())) | 274 mw_mods = list(getattr(settings, "MIDDLEWARE_CLASSES", ())) |
| 275 disallowed_middleware_mods = ( | 275 disallowed_middleware_mods = ( |
| 276 'django.contrib.sessions.middleware.SessionMiddleware', | 276 'django.contrib.sessions.middleware.SessionMiddleware', |
| 277 'django.contrib.auth.middleware.AuthenticationMiddleware', | |
| 278 'django.middleware.doc.XViewMiddleware',) | 277 'django.middleware.doc.XViewMiddleware',) |
| 279 for modname in mw_mods[:]: | 278 for modname in mw_mods[:]: |
| 280 if modname in disallowed_middleware_mods: | 279 if modname in disallowed_middleware_mods: |
| 281 # Currently only the CommonMiddleware has been ported. As other base modu
les | 280 # Currently only the CommonMiddleware has been ported. As other base modu
les |
| 282 # are converted, remove from the disallowed_middleware_mods tuple. | 281 # are converted, remove from the disallowed_middleware_mods tuple. |
| 283 mw_mods.remove(modname) | 282 mw_mods.remove(modname) |
| 284 logging.warn("Middleware module '%s' is not compatible. Removed!" % | 283 logging.warn("Middleware module '%s' is not compatible. Removed!" % |
| 285 modname) | 284 modname) |
| 286 setattr(settings, "MIDDLEWARE_CLASSES", tuple(mw_mods)) | 285 setattr(settings, "MIDDLEWARE_CLASSES", tuple(mw_mods)) |
| 287 | 286 |
| 288 # Remove incompatible application modules | 287 # Remove incompatible application modules |
| 289 app_mods = list(getattr(settings, "INSTALLED_APPS", ())) | 288 app_mods = list(getattr(settings, "INSTALLED_APPS", ())) |
| 289 disallowed_apps = ( |
| 290 'django.contrib.contenttypes', |
| 291 'django.contrib.sessions', |
| 292 'django.contrib.sites',) |
| 290 for app in app_mods[:]: | 293 for app in app_mods[:]: |
| 291 if app.startswith("django."): | 294 if app in disallowed_apps: |
| 292 # TODO(mglb): Again this is probably overly broad. | 295 # TODO(mglb): Again this is probably overly broad. |
| 293 app_mods.remove(app) | 296 app_mods.remove(app) |
| 294 logging.warn("Application module '%s' is not compatible. Removed!" % app) | 297 logging.warn("Application module '%s' is not compatible. Removed!" % app) |
| 295 setattr(settings, "INSTALLED_APPS", tuple(app_mods)) | 298 setattr(settings, "INSTALLED_APPS", tuple(app_mods)) |
| 296 | 299 |
| 297 # Remove incompatible context processors. | |
| 298 bad_processors = ('django.core.context_processors.auth',) | |
| 299 ctx_procs = list(getattr(settings, "TEMPLATE_CONTEXT_PROCESSORS", ())) | |
| 300 for proc in ctx_procs[:]: | |
| 301 if proc in bad_processors: | |
| 302 ctx_procs.remove(proc) | |
| 303 logging.warn("Template Context Processor '%s' is incompatible. Removed!" | |
| 304 % proc) | |
| 305 setattr(settings, "TEMPLATE_CONTEXT_PROCESSORS", tuple(ctx_procs)) | |
| 306 | |
| 307 | 300 |
| 308 def ModifyAvailableCommands(): | 301 def ModifyAvailableCommands(): |
| 309 """Removes incompatible commands and installs replacements where possible.""" | 302 """Removes incompatible commands and installs replacements where possible.""" |
| 310 if have_appserver: | 303 if have_appserver: |
| 311 # Commands are not used when running from an appserver. | 304 # Commands are not used when running from an appserver. |
| 312 return | 305 return |
| 313 from django.core import management | 306 from django.core import management |
| 314 if VERSION < (0, 97, None): | 307 if VERSION < (0, 97, None): |
| 315 RemoveCommands(management.DEFAULT_ACTION_MAPPING) | 308 RemoveCommands(management.DEFAULT_ACTION_MAPPING) |
| 316 from appengine_django.management.commands import runserver | 309 from appengine_django.management.commands import runserver |
| 317 management.DEFAULT_ACTION_MAPPING['runserver'] = runserver.v096_command | 310 management.DEFAULT_ACTION_MAPPING['runserver'] = runserver.v096_command |
| 318 from appengine_django.management.commands import flush | 311 from appengine_django.management.commands import flush |
| 319 management.DEFAULT_ACTION_MAPPING['flush'] = flush.v096_command | 312 management.DEFAULT_ACTION_MAPPING['flush'] = flush.v096_command |
| 320 from appengine_django.management.commands import reset | 313 from appengine_django.management.commands import reset |
| 321 management.DEFAULT_ACTION_MAPPING['reset'] = reset.v096_command | 314 management.DEFAULT_ACTION_MAPPING['reset'] = reset.v096_command |
| 322 else: | 315 else: |
| 323 management.get_commands() | 316 management.get_commands() |
| 324 RemoveCommands(management._commands) | 317 RemoveCommands(management._commands) |
| 325 # Django 0.97 will install the replacements automatically. | 318 # Django 0.97 will install the replacements automatically. |
| 326 logging.debug("Removed incompatible Django manage.py commands") | 319 logging.debug("Removed incompatible Django manage.py commands") |
| 327 | 320 |
| 328 | 321 |
| 329 def RemoveCommands(command_dict): | 322 def RemoveCommands(command_dict): |
| 330 """Removes incompatible commands from the specified command dictionary.""" | 323 """Removes incompatible commands from the specified command dictionary.""" |
| 331 for cmd in command_dict.keys(): | 324 for cmd in command_dict.keys(): |
| 332 if cmd.startswith("sql"): | 325 if cmd.startswith("sql"): |
| 333 del command_dict[cmd] | 326 del command_dict[cmd] |
| 334 elif cmd in INCOMPATIBLE_COMMANDS: | 327 elif cmd in INCOMPATIBLE_COMMANDS: |
| 335 del command_dict[cmd] | 328 del command_dict[cmd] |
| 336 | 329 |
| 337 | 330 |
| 338 def InstallReplacementImpModule(): | 331 def InstallReplacementImpModule(): |
| 339 """Install a replacement for the imp module removed by the appserver. | 332 """Install a replacement for the imp module removed by the appserver. |
| 340 | 333 |
| 341 This is only required for Django 0.97 which uses imp.find_module to find | 334 This is only required for Django 0.97 which uses imp.find_module to find |
| 342 mangement modules provided by applications. | 335 mangement modules provided by applications. |
| 343 """ | 336 """ |
| 344 if VERSION < (0, 97, None) or not have_appserver: | 337 if VERSION < (0, 97, None) or not have_appserver: |
| 345 return | 338 return |
| 346 modname = 'appengine_django.replacement_imp' | 339 modname = 'appengine_django.replacement_imp' |
| 347 imp_mod = __import__(modname, {}, [], ['']) | 340 imp_mod = __import__(modname, {}, [], ['']) |
| 348 sys.modules['imp'] = imp_mod | 341 sys.modules['imp'] = imp_mod |
| 349 logging.debug("Installed replacement imp module") | 342 logging.debug("Installed replacement imp module") |
| 350 | 343 |
| 351 | 344 |
| 352 def InstallAppengineHelperForDjango(): | 345 def InstallAppengineHelperForDjango(): |
| 353 """Installs and Patches all of the classes/methods required for integration. | 346 """Installs and Patches all of the classes/methods required for integration. |
| 354 | 347 |
| 355 If the variable DEBUG_APPENGINE_DJANGO is set in the environment verbose | 348 If the variable DEBUG_APPENGINE_DJANGO is set in the environment verbose |
| 356 logging of the actions taken will be enabled. | 349 logging of the actions taken will be enabled. |
| 357 """ | 350 """ |
| 358 if os.getenv("DEBUG_APPENGINE_DJANGO"): | 351 if os.getenv("DEBUG_APPENGINE_DJANGO"): |
| 359 logging.getLogger().setLevel(logging.DEBUG) | 352 logging.getLogger().setLevel(logging.DEBUG) |
| 360 else: | 353 else: |
| 361 logging.getLogger().setLevel(logging.INFO) | 354 logging.getLogger().setLevel(logging.INFO) |
| 362 logging.debug("Loading the Google App Engine Helper for Django...") | 355 logging.debug("Loading the Google App Engine Helper for Django...") |
| 363 | 356 |
| 364 # Force Django to reload its settings. | 357 # Force Django to reload its settings. |
| 365 settings._target = None | 358 settings._target = None |
| 366 | 359 |
| 367 # Must set this env var *before* importing any more of Django. | 360 # Must set this env var *before* importing any more of Django. |
| 368 os.environ['DJANGO_SETTINGS_MODULE'] = 'settings' | 361 os.environ['DJANGO_SETTINGS_MODULE'] = 'settings' |
| 369 | 362 |
| 370 LoadAppengineEnvironment() | 363 LoadAppengineEnvironment() |
| 371 InstallReplacementImpModule() | 364 InstallReplacementImpModule() |
| 372 InstallAppengineDatabaseBackend() | 365 InstallAppengineDatabaseBackend() |
| 373 PatchDjangoSerializationModules() | 366 PatchDjangoSerializationModules() |
| 374 CleanupDjangoSettings() | 367 CleanupDjangoSettings() |
| 375 ModifyAvailableCommands() | 368 ModifyAvailableCommands() |
| 376 InstallGoogleSMTPConnection() | 369 InstallGoogleSMTPConnection() |
| 370 InstallAuthentication() |
| 377 | 371 |
| 378 logging.debug("Successfully loaded the Google App Engine Helper for Django.") | 372 logging.debug("Successfully loaded the Google App Engine Helper for Django.") |
| 379 | 373 |
| 380 | 374 |
| 381 def InstallGoogleSMTPConnection(): | 375 def InstallGoogleSMTPConnection(): |
| 382 from appengine_django import mail as gmail | 376 from appengine_django import mail as gmail |
| 383 from django.core import mail | 377 from django.core import mail |
| 384 if VERSION >= (0, 97, None): | 378 if VERSION >= (0, 97, None): |
| 385 logging.debug("Installing Google Email Adapter for Django 0.97+") | 379 logging.debug("Installing Google Email Adapter for Django 0.97+") |
| 386 mail.SMTPConnection = gmail.GoogleSMTPConnection | 380 mail.SMTPConnection = gmail.GoogleSMTPConnection |
| 387 else: | 381 else: |
| 388 logging.debug("Installing Google Email Adapter for Django 0.96") | 382 logging.debug("Installing Google Email Adapter for Django 0.96") |
| 389 mail.send_mass_mail = gmail.send_mass_mail | 383 mail.send_mass_mail = gmail.send_mass_mail |
| 390 mail.mail_admins = gmail.mail_admins | 384 mail.mail_admins = gmail.mail_admins |
| 391 mail.mail_managers = gmail.mail_managers | 385 mail.mail_managers = gmail.mail_managers |
| 386 |
| 387 def InstallAuthentication(): |
| 388 logging.debug("Installing authentication framework") |
| 389 from django.contrib.auth import models as django_models |
| 390 from appengine_django.auth.models import DjangoUser |
| 391 django_models.User = DjangoUser |
| 392 from django.contrib.auth import middleware as django_middleware |
| 393 from appengine_django.auth import middleware |
| 394 django_middleware.AuthenticationMiddleware = middleware.AuthenticationMiddlewa
re |
| OLD | NEW |