From 3f7215035a90fa5d559d562a4cdbcefd1bab2910 Mon Sep 17 00:00:00 2001 From: j <0x006A@0x2620.org> Date: Thu, 20 Nov 2014 16:34:55 +0000 Subject: [PATCH] include copy of django_extension, dont install django from git --- contrib/django_extensions/.gitignore | 11 + contrib/django_extensions/.travis.yml | 29 + contrib/django_extensions/.tx/config | 8 + contrib/django_extensions/LICENSE | 19 + contrib/django_extensions/MANIFEST.in | 5 + contrib/django_extensions/README.rst | 73 ++ .../django_extensions.egg-info/PKG-INFO | 22 + .../django_extensions.egg-info/SOURCES.txt | 152 +++ .../dependency_links.txt | 1 + .../django_extensions.egg-info/requires.txt | 1 + .../django_extensions.egg-info/top_level.txt | 1 + .../django_extensions/__init__.py | 13 + .../django_extensions/admin/__init__.py | 145 +++ .../django_extensions/admin/widgets.py | 94 ++ .../conf/app_template/__init__.py.tmpl | 0 .../conf/app_template/forms.py.tmpl | 3 + .../conf/app_template/models.py.tmpl | 3 + .../conf/app_template/urls.py.tmpl | 6 + .../conf/app_template/views.py.tmpl | 1 + .../management/__init__.py.tmpl | 0 .../management/commands/__init__.py.tmpl | 0 .../management/commands/sample.py.tmpl | 7 + .../conf/jobs_template/jobs/__init__.py.tmpl | 0 .../jobs_template/jobs/daily/__init__.py.tmpl | 0 .../jobs/hourly/__init__.py.tmpl | 0 .../jobs/monthly/__init__.py.tmpl | 0 .../conf/jobs_template/jobs/sample.py.tmpl | 8 + .../jobs/weekly/__init__.py.tmpl | 0 .../jobs/yearly/__init__.py.tmpl | 0 .../django_extensions/db/__init__.py | 0 .../django_extensions/db/fields/__init__.py | 287 ++++ .../django_extensions/db/fields/encrypted.py | 138 ++ .../django_extensions/db/fields/json.py | 98 ++ .../django_extensions/db/models.py | 78 ++ .../django_extensions/future_1_5.py | 16 + .../django_extensions/jobs/__init__.py | 0 .../django_extensions/jobs/daily/__init__.py | 0 .../jobs/daily/cache_cleanup.py | 50 + .../jobs/daily/daily_cleanup.py | 16 + .../django_extensions/jobs/hourly/__init__.py | 0 .../jobs/minutely/__init__.py | 0 .../jobs/monthly/__init__.py | 0 .../django_extensions/jobs/weekly/__init__.py | 0 .../django_extensions/jobs/yearly/__init__.py | 0 .../locale/cs/LC_MESSAGES/django.mo | Bin 0 -> 451 bytes .../locale/cs/LC_MESSAGES/django.po | 77 ++ .../locale/da/LC_MESSAGES/django.mo | Bin 0 -> 797 bytes .../locale/da/LC_MESSAGES/django.po | 79 ++ .../locale/de/LC_MESSAGES/django.mo | Bin 0 -> 1227 bytes .../locale/de/LC_MESSAGES/django.po | 77 ++ .../locale/el/LC_MESSAGES/django.mo | Bin 0 -> 1581 bytes .../locale/el/LC_MESSAGES/django.po | 79 ++ .../locale/en/LC_MESSAGES/django.mo | Bin 0 -> 367 bytes .../locale/en/LC_MESSAGES/django.po | 76 ++ .../locale/es/LC_MESSAGES/django.mo | Bin 0 -> 1260 bytes .../locale/es/LC_MESSAGES/django.po | 77 ++ .../locale/et/LC_MESSAGES/django.mo | Bin 0 -> 398 bytes .../locale/et/LC_MESSAGES/django.po | 75 ++ .../locale/fr/LC_MESSAGES/django.mo | Bin 0 -> 743 bytes .../locale/fr/LC_MESSAGES/django.po | 82 ++ .../locale/hu/LC_MESSAGES/django.mo | Bin 0 -> 1242 bytes .../locale/hu/LC_MESSAGES/django.po | 77 ++ .../locale/it/LC_MESSAGES/django.mo | Bin 0 -> 1247 bytes .../locale/it/LC_MESSAGES/django.po | 77 ++ .../locale/ja/LC_MESSAGES/django.mo | Bin 0 -> 1397 bytes .../locale/ja/LC_MESSAGES/django.po | 77 ++ .../locale/pl/LC_MESSAGES/django.mo | Bin 0 -> 1236 bytes .../locale/pl/LC_MESSAGES/django.po | 77 ++ .../locale/pt/LC_MESSAGES/django.mo | Bin 0 -> 1262 bytes .../locale/pt/LC_MESSAGES/django.po | 77 ++ .../locale/pt_BR/LC_MESSAGES/django.mo | Bin 0 -> 1310 bytes .../locale/pt_BR/LC_MESSAGES/django.po | 79 ++ .../locale/ro/LC_MESSAGES/django.mo | Bin 0 -> 1352 bytes .../locale/ro/LC_MESSAGES/django.po | 80 ++ .../locale/ru/LC_MESSAGES/django.mo | Bin 0 -> 498 bytes .../locale/ru/LC_MESSAGES/django.po | 78 ++ .../locale/sk/LC_MESSAGES/django.mo | Bin 0 -> 451 bytes .../locale/sk/LC_MESSAGES/django.po | 77 ++ .../locale/tr/LC_MESSAGES/django.mo | Bin 0 -> 417 bytes .../locale/tr/LC_MESSAGES/django.po | 77 ++ .../locale/zh_CN/LC_MESSAGES/django.mo | Bin 0 -> 420 bytes .../locale/zh_CN/LC_MESSAGES/django.po | 77 ++ .../django_extensions/management/__init__.py | 0 .../django_extensions/management/base.py | 53 + .../django_extensions/management/color.py | 19 + .../management/commands/__init__.py | 0 .../management/commands/clean_pyc.py | 37 + .../management/commands/compile_pyc.py | 30 + .../management/commands/create_app.py | 138 ++ .../management/commands/create_command.py | 81 ++ .../management/commands/create_jobs.py | 56 + .../management/commands/describe_form.py | 66 + .../management/commands/dumpscript.py | 751 +++++++++++ .../management/commands/export_emails.py | 133 ++ .../management/commands/find_template.py | 35 + .../commands/generate_secret_key.py | 11 + .../management/commands/graph_models.py | 143 ++ .../management/commands/mail_debug.py | 80 ++ .../management/commands/notes.py | 48 + .../management/commands/passwd.py | 42 + .../management/commands/pipchecker.py | 256 ++++ .../management/commands/print_settings.py | 72 ++ .../commands/print_user_for_session.py | 66 + .../management/commands/reset_db.py | 177 +++ .../management/commands/runjob.py | 52 + .../management/commands/runjobs.py | 89 ++ .../management/commands/runprofileserver.py | 291 +++++ .../management/commands/runscript.py | 147 +++ .../management/commands/runserver_plus.py | 293 +++++ .../management/commands/set_fake_emails.py | 81 ++ .../management/commands/set_fake_passwords.py | 49 + .../management/commands/shell_plus.py | 166 +++ .../management/commands/show_templatetags.py | 100 ++ .../management/commands/show_urls.py | 103 ++ .../management/commands/sqlcreate.py | 89 ++ .../management/commands/sqldiff.py | 710 ++++++++++ .../management/commands/sync_media_s3.py | 292 +++++ .../management/commands/sync_s3.py | 359 +++++ .../management/commands/syncdata.py | 201 +++ .../management/commands/unreferenced_files.py | 47 + .../management/commands/update_permissions.py | 21 + .../management/commands/validate_templates.py | 71 + .../django_extensions/management/jobs.py | 177 +++ .../django_extensions/management/modelviz.py | 281 ++++ .../management/notebook_extension.py | 6 + .../django_extensions/management/shells.py | 116 ++ .../django_extensions/management/signals.py | 12 + .../management/technical_response.py | 6 + .../django_extensions/management/utils.py | 69 + .../migrations/0001_empty.py | 17 + .../django_extensions/migrations/__init__.py | 0 .../django_extensions/models.py | 0 .../django_extensions/mongodb/__init__.py | 0 .../mongodb/fields/__init__.py | 248 ++++ .../mongodb/fields/encrypted.py | 61 + .../django_extensions/mongodb/fields/json.py | 77 ++ .../django_extensions/mongodb/models.py | 69 + .../django_extensions/settings.py | 7 + .../css/jquery.autocomplete.css | 38 + .../django_extensions/img/indicator.gif | Bin 0 -> 1553 bytes .../django_extensions/js/jquery.ajaxQueue.js | 119 ++ .../js/jquery.autocomplete.js | 1152 +++++++++++++++++ .../js/jquery.bgiframe.min.js | 10 + .../graph_models/digraph.dot | 26 + .../django_extensions/graph_models/label.dot | 30 + .../graph_models/relation.dot | 10 + .../widgets/foreignkey_searchinput.html | 60 + .../templatetags/__init__.py | 0 .../templatetags/highlighting.py | 91 ++ .../templatetags/indent_text.py | 55 + .../templatetags/syntax_color.py | 97 ++ .../templatetags/truncate_letters.py | 28 + .../django_extensions/templatetags/widont.py | 62 + .../django_extensions/tests/__init__.py | 19 + .../tests/encrypted_fields.py | 270 ++++ .../django_extensions/tests/fields.py | 105 ++ .../django_extensions/tests/json_field.py | 37 + .../tests/management/__init__.py | 0 .../tests/management/commands/__init__.py | 0 .../commands/error_raising_command.py | 10 + .../tests/management_command.py | 60 + .../django_extensions/tests/models.py | 21 + .../tests/test_dumpscript.py | 94 ++ .../django_extensions/tests/urls.py | 0 .../django_extensions/tests/utils.py | 76 ++ .../django_extensions/tests/uuid_field.py | 58 + .../django_extensions/utils/__init__.py | 0 .../django_extensions/utils/dia2django.py | 215 +++ .../django_extensions/utils/text.py | 23 + .../utils/validatingtemplatetags.py | 91 ++ contrib/django_extensions/docs/AUTHORS | 21 + contrib/django_extensions/docs/Makefile | 75 ++ .../docs/admin_extensions.rst | 10 + .../docs/command_extension_ideas.rst | 7 + .../docs/command_extensions.rst | 95 ++ contrib/django_extensions/docs/conf.py | 194 +++ contrib/django_extensions/docs/create_app.rst | 41 + .../docs/creating_release.txt | 58 + contrib/django_extensions/docs/dumpscript.rst | 101 ++ .../django_extensions/docs/export_emails.rst | 81 ++ .../docs/field_extensions.rst | 27 + .../django_extensions/docs/graph_models.rst | 88 ++ contrib/django_extensions/docs/index.rst | 79 ++ .../docs/installation_instructions.rst | 100 ++ .../docs/jobs_scheduling.rst | 44 + .../docs/model_extensions.rst | 11 + .../docs/namespace_proposal.rst | 29 + .../django_extensions/docs/print_settings.rst | 55 + .../docs/runprofileserver.rst | 84 ++ contrib/django_extensions/docs/runscript.rst | 65 + .../django_extensions/docs/runserver_plus.rst | 125 ++ contrib/django_extensions/docs/shell_plus.rst | 127 ++ contrib/django_extensions/docs/sqlcreate.rst | 45 + contrib/django_extensions/docs/sqldiff.rst | 39 + contrib/django_extensions/docs/sync_s3.rst | 50 + .../docs/validate_templates.rst | 35 + contrib/django_extensions/run_tests.py | 67 + contrib/django_extensions/setup.py | 105 ++ contrib/django_extensions/tox.ini | 39 + requirements.txt | 8 +- 200 files changed, 14119 insertions(+), 4 deletions(-) create mode 100644 contrib/django_extensions/.gitignore create mode 100644 contrib/django_extensions/.travis.yml create mode 100644 contrib/django_extensions/.tx/config create mode 100644 contrib/django_extensions/LICENSE create mode 100644 contrib/django_extensions/MANIFEST.in create mode 100644 contrib/django_extensions/README.rst create mode 100644 contrib/django_extensions/django_extensions.egg-info/PKG-INFO create mode 100644 contrib/django_extensions/django_extensions.egg-info/SOURCES.txt create mode 100644 contrib/django_extensions/django_extensions.egg-info/dependency_links.txt create mode 100644 contrib/django_extensions/django_extensions.egg-info/requires.txt create mode 100644 contrib/django_extensions/django_extensions.egg-info/top_level.txt create mode 100644 contrib/django_extensions/django_extensions/__init__.py create mode 100644 contrib/django_extensions/django_extensions/admin/__init__.py create mode 100644 contrib/django_extensions/django_extensions/admin/widgets.py create mode 100644 contrib/django_extensions/django_extensions/conf/app_template/__init__.py.tmpl create mode 100644 contrib/django_extensions/django_extensions/conf/app_template/forms.py.tmpl create mode 100644 contrib/django_extensions/django_extensions/conf/app_template/models.py.tmpl create mode 100644 contrib/django_extensions/django_extensions/conf/app_template/urls.py.tmpl create mode 100644 contrib/django_extensions/django_extensions/conf/app_template/views.py.tmpl create mode 100644 contrib/django_extensions/django_extensions/conf/command_template/management/__init__.py.tmpl create mode 100644 contrib/django_extensions/django_extensions/conf/command_template/management/commands/__init__.py.tmpl create mode 100644 contrib/django_extensions/django_extensions/conf/command_template/management/commands/sample.py.tmpl create mode 100644 contrib/django_extensions/django_extensions/conf/jobs_template/jobs/__init__.py.tmpl create mode 100644 contrib/django_extensions/django_extensions/conf/jobs_template/jobs/daily/__init__.py.tmpl create mode 100644 contrib/django_extensions/django_extensions/conf/jobs_template/jobs/hourly/__init__.py.tmpl create mode 100644 contrib/django_extensions/django_extensions/conf/jobs_template/jobs/monthly/__init__.py.tmpl create mode 100644 contrib/django_extensions/django_extensions/conf/jobs_template/jobs/sample.py.tmpl create mode 100644 contrib/django_extensions/django_extensions/conf/jobs_template/jobs/weekly/__init__.py.tmpl create mode 100644 contrib/django_extensions/django_extensions/conf/jobs_template/jobs/yearly/__init__.py.tmpl create mode 100644 contrib/django_extensions/django_extensions/db/__init__.py create mode 100644 contrib/django_extensions/django_extensions/db/fields/__init__.py create mode 100644 contrib/django_extensions/django_extensions/db/fields/encrypted.py create mode 100644 contrib/django_extensions/django_extensions/db/fields/json.py create mode 100644 contrib/django_extensions/django_extensions/db/models.py create mode 100644 contrib/django_extensions/django_extensions/future_1_5.py create mode 100644 contrib/django_extensions/django_extensions/jobs/__init__.py create mode 100644 contrib/django_extensions/django_extensions/jobs/daily/__init__.py create mode 100644 contrib/django_extensions/django_extensions/jobs/daily/cache_cleanup.py create mode 100644 contrib/django_extensions/django_extensions/jobs/daily/daily_cleanup.py create mode 100644 contrib/django_extensions/django_extensions/jobs/hourly/__init__.py create mode 100644 contrib/django_extensions/django_extensions/jobs/minutely/__init__.py create mode 100644 contrib/django_extensions/django_extensions/jobs/monthly/__init__.py create mode 100644 contrib/django_extensions/django_extensions/jobs/weekly/__init__.py create mode 100644 contrib/django_extensions/django_extensions/jobs/yearly/__init__.py create mode 100644 contrib/django_extensions/django_extensions/locale/cs/LC_MESSAGES/django.mo create mode 100644 contrib/django_extensions/django_extensions/locale/cs/LC_MESSAGES/django.po create mode 100644 contrib/django_extensions/django_extensions/locale/da/LC_MESSAGES/django.mo create mode 100644 contrib/django_extensions/django_extensions/locale/da/LC_MESSAGES/django.po create mode 100644 contrib/django_extensions/django_extensions/locale/de/LC_MESSAGES/django.mo create mode 100644 contrib/django_extensions/django_extensions/locale/de/LC_MESSAGES/django.po create mode 100644 contrib/django_extensions/django_extensions/locale/el/LC_MESSAGES/django.mo create mode 100644 contrib/django_extensions/django_extensions/locale/el/LC_MESSAGES/django.po create mode 100644 contrib/django_extensions/django_extensions/locale/en/LC_MESSAGES/django.mo create mode 100644 contrib/django_extensions/django_extensions/locale/en/LC_MESSAGES/django.po create mode 100644 contrib/django_extensions/django_extensions/locale/es/LC_MESSAGES/django.mo create mode 100644 contrib/django_extensions/django_extensions/locale/es/LC_MESSAGES/django.po create mode 100644 contrib/django_extensions/django_extensions/locale/et/LC_MESSAGES/django.mo create mode 100644 contrib/django_extensions/django_extensions/locale/et/LC_MESSAGES/django.po create mode 100644 contrib/django_extensions/django_extensions/locale/fr/LC_MESSAGES/django.mo create mode 100644 contrib/django_extensions/django_extensions/locale/fr/LC_MESSAGES/django.po create mode 100644 contrib/django_extensions/django_extensions/locale/hu/LC_MESSAGES/django.mo create mode 100644 contrib/django_extensions/django_extensions/locale/hu/LC_MESSAGES/django.po create mode 100644 contrib/django_extensions/django_extensions/locale/it/LC_MESSAGES/django.mo create mode 100644 contrib/django_extensions/django_extensions/locale/it/LC_MESSAGES/django.po create mode 100644 contrib/django_extensions/django_extensions/locale/ja/LC_MESSAGES/django.mo create mode 100644 contrib/django_extensions/django_extensions/locale/ja/LC_MESSAGES/django.po create mode 100644 contrib/django_extensions/django_extensions/locale/pl/LC_MESSAGES/django.mo create mode 100644 contrib/django_extensions/django_extensions/locale/pl/LC_MESSAGES/django.po create mode 100644 contrib/django_extensions/django_extensions/locale/pt/LC_MESSAGES/django.mo create mode 100644 contrib/django_extensions/django_extensions/locale/pt/LC_MESSAGES/django.po create mode 100644 contrib/django_extensions/django_extensions/locale/pt_BR/LC_MESSAGES/django.mo create mode 100644 contrib/django_extensions/django_extensions/locale/pt_BR/LC_MESSAGES/django.po create mode 100644 contrib/django_extensions/django_extensions/locale/ro/LC_MESSAGES/django.mo create mode 100644 contrib/django_extensions/django_extensions/locale/ro/LC_MESSAGES/django.po create mode 100644 contrib/django_extensions/django_extensions/locale/ru/LC_MESSAGES/django.mo create mode 100644 contrib/django_extensions/django_extensions/locale/ru/LC_MESSAGES/django.po create mode 100644 contrib/django_extensions/django_extensions/locale/sk/LC_MESSAGES/django.mo create mode 100644 contrib/django_extensions/django_extensions/locale/sk/LC_MESSAGES/django.po create mode 100644 contrib/django_extensions/django_extensions/locale/tr/LC_MESSAGES/django.mo create mode 100644 contrib/django_extensions/django_extensions/locale/tr/LC_MESSAGES/django.po create mode 100644 contrib/django_extensions/django_extensions/locale/zh_CN/LC_MESSAGES/django.mo create mode 100644 contrib/django_extensions/django_extensions/locale/zh_CN/LC_MESSAGES/django.po create mode 100644 contrib/django_extensions/django_extensions/management/__init__.py create mode 100644 contrib/django_extensions/django_extensions/management/base.py create mode 100644 contrib/django_extensions/django_extensions/management/color.py create mode 100644 contrib/django_extensions/django_extensions/management/commands/__init__.py create mode 100644 contrib/django_extensions/django_extensions/management/commands/clean_pyc.py create mode 100644 contrib/django_extensions/django_extensions/management/commands/compile_pyc.py create mode 100644 contrib/django_extensions/django_extensions/management/commands/create_app.py create mode 100644 contrib/django_extensions/django_extensions/management/commands/create_command.py create mode 100644 contrib/django_extensions/django_extensions/management/commands/create_jobs.py create mode 100644 contrib/django_extensions/django_extensions/management/commands/describe_form.py create mode 100644 contrib/django_extensions/django_extensions/management/commands/dumpscript.py create mode 100644 contrib/django_extensions/django_extensions/management/commands/export_emails.py create mode 100644 contrib/django_extensions/django_extensions/management/commands/find_template.py create mode 100644 contrib/django_extensions/django_extensions/management/commands/generate_secret_key.py create mode 100644 contrib/django_extensions/django_extensions/management/commands/graph_models.py create mode 100644 contrib/django_extensions/django_extensions/management/commands/mail_debug.py create mode 100644 contrib/django_extensions/django_extensions/management/commands/notes.py create mode 100644 contrib/django_extensions/django_extensions/management/commands/passwd.py create mode 100644 contrib/django_extensions/django_extensions/management/commands/pipchecker.py create mode 100644 contrib/django_extensions/django_extensions/management/commands/print_settings.py create mode 100644 contrib/django_extensions/django_extensions/management/commands/print_user_for_session.py create mode 100644 contrib/django_extensions/django_extensions/management/commands/reset_db.py create mode 100644 contrib/django_extensions/django_extensions/management/commands/runjob.py create mode 100644 contrib/django_extensions/django_extensions/management/commands/runjobs.py create mode 100644 contrib/django_extensions/django_extensions/management/commands/runprofileserver.py create mode 100644 contrib/django_extensions/django_extensions/management/commands/runscript.py create mode 100644 contrib/django_extensions/django_extensions/management/commands/runserver_plus.py create mode 100644 contrib/django_extensions/django_extensions/management/commands/set_fake_emails.py create mode 100644 contrib/django_extensions/django_extensions/management/commands/set_fake_passwords.py create mode 100644 contrib/django_extensions/django_extensions/management/commands/shell_plus.py create mode 100644 contrib/django_extensions/django_extensions/management/commands/show_templatetags.py create mode 100644 contrib/django_extensions/django_extensions/management/commands/show_urls.py create mode 100644 contrib/django_extensions/django_extensions/management/commands/sqlcreate.py create mode 100644 contrib/django_extensions/django_extensions/management/commands/sqldiff.py create mode 100644 contrib/django_extensions/django_extensions/management/commands/sync_media_s3.py create mode 100644 contrib/django_extensions/django_extensions/management/commands/sync_s3.py create mode 100644 contrib/django_extensions/django_extensions/management/commands/syncdata.py create mode 100644 contrib/django_extensions/django_extensions/management/commands/unreferenced_files.py create mode 100644 contrib/django_extensions/django_extensions/management/commands/update_permissions.py create mode 100644 contrib/django_extensions/django_extensions/management/commands/validate_templates.py create mode 100644 contrib/django_extensions/django_extensions/management/jobs.py create mode 100644 contrib/django_extensions/django_extensions/management/modelviz.py create mode 100644 contrib/django_extensions/django_extensions/management/notebook_extension.py create mode 100644 contrib/django_extensions/django_extensions/management/shells.py create mode 100644 contrib/django_extensions/django_extensions/management/signals.py create mode 100644 contrib/django_extensions/django_extensions/management/technical_response.py create mode 100644 contrib/django_extensions/django_extensions/management/utils.py create mode 100644 contrib/django_extensions/django_extensions/migrations/0001_empty.py create mode 100644 contrib/django_extensions/django_extensions/migrations/__init__.py create mode 100644 contrib/django_extensions/django_extensions/models.py create mode 100644 contrib/django_extensions/django_extensions/mongodb/__init__.py create mode 100644 contrib/django_extensions/django_extensions/mongodb/fields/__init__.py create mode 100644 contrib/django_extensions/django_extensions/mongodb/fields/encrypted.py create mode 100644 contrib/django_extensions/django_extensions/mongodb/fields/json.py create mode 100644 contrib/django_extensions/django_extensions/mongodb/models.py create mode 100644 contrib/django_extensions/django_extensions/settings.py create mode 100644 contrib/django_extensions/django_extensions/static/django_extensions/css/jquery.autocomplete.css create mode 100644 contrib/django_extensions/django_extensions/static/django_extensions/img/indicator.gif create mode 100644 contrib/django_extensions/django_extensions/static/django_extensions/js/jquery.ajaxQueue.js create mode 100644 contrib/django_extensions/django_extensions/static/django_extensions/js/jquery.autocomplete.js create mode 100644 contrib/django_extensions/django_extensions/static/django_extensions/js/jquery.bgiframe.min.js create mode 100644 contrib/django_extensions/django_extensions/templates/django_extensions/graph_models/digraph.dot create mode 100644 contrib/django_extensions/django_extensions/templates/django_extensions/graph_models/label.dot create mode 100644 contrib/django_extensions/django_extensions/templates/django_extensions/graph_models/relation.dot create mode 100644 contrib/django_extensions/django_extensions/templates/django_extensions/widgets/foreignkey_searchinput.html create mode 100644 contrib/django_extensions/django_extensions/templatetags/__init__.py create mode 100644 contrib/django_extensions/django_extensions/templatetags/highlighting.py create mode 100644 contrib/django_extensions/django_extensions/templatetags/indent_text.py create mode 100644 contrib/django_extensions/django_extensions/templatetags/syntax_color.py create mode 100644 contrib/django_extensions/django_extensions/templatetags/truncate_letters.py create mode 100644 contrib/django_extensions/django_extensions/templatetags/widont.py create mode 100644 contrib/django_extensions/django_extensions/tests/__init__.py create mode 100644 contrib/django_extensions/django_extensions/tests/encrypted_fields.py create mode 100644 contrib/django_extensions/django_extensions/tests/fields.py create mode 100644 contrib/django_extensions/django_extensions/tests/json_field.py create mode 100644 contrib/django_extensions/django_extensions/tests/management/__init__.py create mode 100644 contrib/django_extensions/django_extensions/tests/management/commands/__init__.py create mode 100644 contrib/django_extensions/django_extensions/tests/management/commands/error_raising_command.py create mode 100644 contrib/django_extensions/django_extensions/tests/management_command.py create mode 100644 contrib/django_extensions/django_extensions/tests/models.py create mode 100644 contrib/django_extensions/django_extensions/tests/test_dumpscript.py create mode 100644 contrib/django_extensions/django_extensions/tests/urls.py create mode 100644 contrib/django_extensions/django_extensions/tests/utils.py create mode 100644 contrib/django_extensions/django_extensions/tests/uuid_field.py create mode 100644 contrib/django_extensions/django_extensions/utils/__init__.py create mode 100755 contrib/django_extensions/django_extensions/utils/dia2django.py create mode 100644 contrib/django_extensions/django_extensions/utils/text.py create mode 100644 contrib/django_extensions/django_extensions/utils/validatingtemplatetags.py create mode 100644 contrib/django_extensions/docs/AUTHORS create mode 100644 contrib/django_extensions/docs/Makefile create mode 100644 contrib/django_extensions/docs/admin_extensions.rst create mode 100644 contrib/django_extensions/docs/command_extension_ideas.rst create mode 100644 contrib/django_extensions/docs/command_extensions.rst create mode 100644 contrib/django_extensions/docs/conf.py create mode 100644 contrib/django_extensions/docs/create_app.rst create mode 100644 contrib/django_extensions/docs/creating_release.txt create mode 100644 contrib/django_extensions/docs/dumpscript.rst create mode 100644 contrib/django_extensions/docs/export_emails.rst create mode 100644 contrib/django_extensions/docs/field_extensions.rst create mode 100644 contrib/django_extensions/docs/graph_models.rst create mode 100644 contrib/django_extensions/docs/index.rst create mode 100644 contrib/django_extensions/docs/installation_instructions.rst create mode 100644 contrib/django_extensions/docs/jobs_scheduling.rst create mode 100644 contrib/django_extensions/docs/model_extensions.rst create mode 100644 contrib/django_extensions/docs/namespace_proposal.rst create mode 100644 contrib/django_extensions/docs/print_settings.rst create mode 100644 contrib/django_extensions/docs/runprofileserver.rst create mode 100644 contrib/django_extensions/docs/runscript.rst create mode 100644 contrib/django_extensions/docs/runserver_plus.rst create mode 100644 contrib/django_extensions/docs/shell_plus.rst create mode 100644 contrib/django_extensions/docs/sqlcreate.rst create mode 100644 contrib/django_extensions/docs/sqldiff.rst create mode 100644 contrib/django_extensions/docs/sync_s3.rst create mode 100644 contrib/django_extensions/docs/validate_templates.rst create mode 100755 contrib/django_extensions/run_tests.py create mode 100644 contrib/django_extensions/setup.py create mode 100644 contrib/django_extensions/tox.ini diff --git a/contrib/django_extensions/.gitignore b/contrib/django_extensions/.gitignore new file mode 100644 index 00000000..726691ab --- /dev/null +++ b/contrib/django_extensions/.gitignore @@ -0,0 +1,11 @@ +*.pyc +*.pyo +*.egg-info +MANIFEST +build +dist +docs/_build +docs/_static +*.egg-info +.tox +*.bak diff --git a/contrib/django_extensions/.travis.yml b/contrib/django_extensions/.travis.yml new file mode 100644 index 00000000..a4181ab8 --- /dev/null +++ b/contrib/django_extensions/.travis.yml @@ -0,0 +1,29 @@ +language: python + +python: + - 2.6 + - 2.7 + - 3.3 + +env: + - DJANGO=Django==1.3 --use-mirrors + - DJANGO=Django==1.4 --use-mirrors + - DJANGO=Django==1.5 --use-mirrors + +install: + - pip install -q $DJANGO + - pip install flake8 --use-mirrors + - if [[ $TRAVIS_PYTHON_VERSION < '3.0' ]]; then pip install -q python-keyczar --use-mirrors; fi + - pip install -q PyYAML --use-mirrors + - pip install -e . --use-mirrors + +script: + - flake8 --ignore=E501,W391 . + - python setup.py test + +matrix: + exclude: + - python: 3.3 + env: DJANGO=Django==1.3 --use-mirrors + - python: 3.3 + env: DJANGO=Django==1.4 --use-mirrors diff --git a/contrib/django_extensions/.tx/config b/contrib/django_extensions/.tx/config new file mode 100644 index 00000000..680d72f1 --- /dev/null +++ b/contrib/django_extensions/.tx/config @@ -0,0 +1,8 @@ +[django-extensions.master] +file_filter = django_extensions/locale//LC_MESSAGES/django.po +source_file = django_extensions/locale/en/LC_MESSAGES/django.po +source_lang = en + +[main] +host = https://www.transifex.net + diff --git a/contrib/django_extensions/LICENSE b/contrib/django_extensions/LICENSE new file mode 100644 index 00000000..e2e53d56 --- /dev/null +++ b/contrib/django_extensions/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2007 Michael Trier + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. \ No newline at end of file diff --git a/contrib/django_extensions/MANIFEST.in b/contrib/django_extensions/MANIFEST.in new file mode 100644 index 00000000..2d3edd80 --- /dev/null +++ b/contrib/django_extensions/MANIFEST.in @@ -0,0 +1,5 @@ +recursive-include django_extensions/conf *.tmpl +recursive-include django_extensions/templates *.html +recursive-include django_extensions/static * +recursive-include docs * +include LICENSE diff --git a/contrib/django_extensions/README.rst b/contrib/django_extensions/README.rst new file mode 100644 index 00000000..664cf4fe --- /dev/null +++ b/contrib/django_extensions/README.rst @@ -0,0 +1,73 @@ +=================== + Django Extensions +=================== + +.. image:: https://secure.travis-ci.org/django-extensions/django-extensions.png?branch=master + :alt: Build Status + :target: http://travis-ci.org/django-extensions/django-extensions + +Django Extensions is a collection of custom extensions for the Django Framework. + +Getting Started +=============== + +The easiest way to figure out what Django Extensions are all about is to watch the `excellent screencast by Eric Holscher`__. In a couple minutes Eric walks you through a half a dozen command extensions. + +Getting It +========== + +You can get Django Extensions by using pip or easy_install:: + + $ pip install django-extensions + or + $ easy_install django-extensions + +If you want to install it from source, grab the git repository from GitHub and run setup.py:: + + $ git clone git://github.com/django-extensions/django-extensions.git + $ cd django-extensions + $ python setup.py install + +Installing It +============= + +To enable `django_extensions` in your project you need to add it to `INSTALLED_APPS` in your projects `settings.py` file:: + + INSTALLED_APPS = ( + ... + 'django_extensions', + ... + ) + + +Using It +======== + +Generate (and view) a graphviz graph of app models:: + + $ python manage.py graph_models -a -o myapp_models.png + +Produce a tab-separated list of `(url_pattern, view_function, name)` tuples for a project:: + + $ python manage.py show_urls + +Getting Involved +================ + +Open Source projects can always use more help. Fixing a problem, documenting a feature, adding translation in your language. If you have some time to spare and like to help us, here are the places to do so: + +- GitHub: https://github.com/django-extensions/django-extensions +- Mailing list: http://groups.google.com/group/django-extensions +- Translations: https://www.transifex.net/projects/p/django-extensions/ + +Documentation +============= + +You can view documentation online at: + +- http://packages.python.org/django-extensions/ + +Or you can look at the docs/ directory in the repository. + +__ http://ericholscher.com/blog/2008/sep/12/screencast-django-command-extensions/ + diff --git a/contrib/django_extensions/django_extensions.egg-info/PKG-INFO b/contrib/django_extensions/django_extensions.egg-info/PKG-INFO new file mode 100644 index 00000000..e2e14bd9 --- /dev/null +++ b/contrib/django_extensions/django_extensions.egg-info/PKG-INFO @@ -0,0 +1,22 @@ +Metadata-Version: 1.1 +Name: django-extensions +Version: 1.2.3 +Summary: Extensions for Django +Home-page: http://github.com/django-extensions/django-extensions +Author: Bas van Oostveen +Author-email: v.oostveen@gmail.com +License: New BSD License +Description: django-extensions bundles several useful + additions for Django projects. See the project page for more information: + http://github.com/django-extensions/django-extensions +Platform: any +Classifier: Development Status :: 4 - Beta +Classifier: Development Status :: 5 - Production/Stable +Classifier: Environment :: Web Environment +Classifier: Framework :: Django +Classifier: Intended Audience :: Developers +Classifier: License :: OSI Approved :: BSD License +Classifier: Operating System :: OS Independent +Classifier: Programming Language :: Python +Classifier: Programming Language :: Python :: 3 +Classifier: Topic :: Utilities diff --git a/contrib/django_extensions/django_extensions.egg-info/SOURCES.txt b/contrib/django_extensions/django_extensions.egg-info/SOURCES.txt new file mode 100644 index 00000000..9a0f145e --- /dev/null +++ b/contrib/django_extensions/django_extensions.egg-info/SOURCES.txt @@ -0,0 +1,152 @@ +LICENSE +MANIFEST.in +README.rst +django_extensions/__init__.py +django_extensions/future_1_5.py +django_extensions/models.py +django_extensions/settings.py +django_extensions.egg-info/PKG-INFO +django_extensions.egg-info/SOURCES.txt +django_extensions.egg-info/dependency_links.txt +django_extensions.egg-info/requires.txt +django_extensions.egg-info/top_level.txt +django_extensions/admin/__init__.py +django_extensions/admin/widgets.py +django_extensions/conf/app_template/__init__.py.tmpl +django_extensions/conf/app_template/forms.py.tmpl +django_extensions/conf/app_template/models.py.tmpl +django_extensions/conf/app_template/urls.py.tmpl +django_extensions/conf/app_template/views.py.tmpl +django_extensions/conf/command_template/management/__init__.py.tmpl +django_extensions/conf/command_template/management/commands/__init__.py.tmpl +django_extensions/conf/command_template/management/commands/sample.py.tmpl +django_extensions/conf/jobs_template/jobs/__init__.py.tmpl +django_extensions/conf/jobs_template/jobs/sample.py.tmpl +django_extensions/conf/jobs_template/jobs/daily/__init__.py.tmpl +django_extensions/conf/jobs_template/jobs/hourly/__init__.py.tmpl +django_extensions/conf/jobs_template/jobs/monthly/__init__.py.tmpl +django_extensions/conf/jobs_template/jobs/weekly/__init__.py.tmpl +django_extensions/conf/jobs_template/jobs/yearly/__init__.py.tmpl +django_extensions/db/__init__.py +django_extensions/db/models.py +django_extensions/db/fields/__init__.py +django_extensions/db/fields/encrypted.py +django_extensions/db/fields/json.py +django_extensions/jobs/__init__.py +django_extensions/jobs/daily/__init__.py +django_extensions/jobs/daily/cache_cleanup.py +django_extensions/jobs/daily/daily_cleanup.py +django_extensions/jobs/hourly/__init__.py +django_extensions/jobs/minutely/__init__.py +django_extensions/jobs/monthly/__init__.py +django_extensions/jobs/weekly/__init__.py +django_extensions/jobs/yearly/__init__.py +django_extensions/management/__init__.py +django_extensions/management/base.py +django_extensions/management/color.py +django_extensions/management/jobs.py +django_extensions/management/modelviz.py +django_extensions/management/notebook_extension.py +django_extensions/management/shells.py +django_extensions/management/signals.py +django_extensions/management/technical_response.py +django_extensions/management/utils.py +django_extensions/management/commands/__init__.py +django_extensions/management/commands/clean_pyc.py +django_extensions/management/commands/compile_pyc.py +django_extensions/management/commands/create_app.py +django_extensions/management/commands/create_command.py +django_extensions/management/commands/create_jobs.py +django_extensions/management/commands/describe_form.py +django_extensions/management/commands/dumpscript.py +django_extensions/management/commands/export_emails.py +django_extensions/management/commands/find_template.py +django_extensions/management/commands/generate_secret_key.py +django_extensions/management/commands/graph_models.py +django_extensions/management/commands/mail_debug.py +django_extensions/management/commands/notes.py +django_extensions/management/commands/passwd.py +django_extensions/management/commands/pipchecker.py +django_extensions/management/commands/print_settings.py +django_extensions/management/commands/print_user_for_session.py +django_extensions/management/commands/reset_db.py +django_extensions/management/commands/runjob.py +django_extensions/management/commands/runjobs.py +django_extensions/management/commands/runprofileserver.py +django_extensions/management/commands/runscript.py +django_extensions/management/commands/runserver_plus.py +django_extensions/management/commands/set_fake_emails.py +django_extensions/management/commands/set_fake_passwords.py +django_extensions/management/commands/shell_plus.py +django_extensions/management/commands/show_templatetags.py +django_extensions/management/commands/show_urls.py +django_extensions/management/commands/sqlcreate.py +django_extensions/management/commands/sqldiff.py +django_extensions/management/commands/sync_media_s3.py +django_extensions/management/commands/sync_s3.py +django_extensions/management/commands/syncdata.py +django_extensions/management/commands/unreferenced_files.py +django_extensions/management/commands/update_permissions.py +django_extensions/management/commands/validate_templates.py +django_extensions/migrations/0001_empty.py +django_extensions/migrations/__init__.py +django_extensions/mongodb/__init__.py +django_extensions/mongodb/models.py +django_extensions/mongodb/fields/__init__.py +django_extensions/mongodb/fields/encrypted.py +django_extensions/mongodb/fields/json.py +django_extensions/static/django_extensions/css/jquery.autocomplete.css +django_extensions/static/django_extensions/img/indicator.gif +django_extensions/static/django_extensions/js/jquery.ajaxQueue.js +django_extensions/static/django_extensions/js/jquery.autocomplete.js +django_extensions/static/django_extensions/js/jquery.bgiframe.min.js +django_extensions/templates/django_extensions/widgets/foreignkey_searchinput.html +django_extensions/templatetags/__init__.py +django_extensions/templatetags/highlighting.py +django_extensions/templatetags/indent_text.py +django_extensions/templatetags/syntax_color.py +django_extensions/templatetags/truncate_letters.py +django_extensions/templatetags/widont.py +django_extensions/tests/__init__.py +django_extensions/tests/encrypted_fields.py +django_extensions/tests/fields.py +django_extensions/tests/json_field.py +django_extensions/tests/management_command.py +django_extensions/tests/models.py +django_extensions/tests/test_dumpscript.py +django_extensions/tests/urls.py +django_extensions/tests/utils.py +django_extensions/tests/uuid_field.py +django_extensions/tests/management/__init__.py +django_extensions/tests/management/commands/__init__.py +django_extensions/tests/management/commands/error_raising_command.py +django_extensions/utils/__init__.py +django_extensions/utils/dia2django.py +django_extensions/utils/text.py +django_extensions/utils/validatingtemplatetags.py +docs/AUTHORS +docs/Makefile +docs/admin_extensions.rst +docs/command_extension_ideas.rst +docs/command_extensions.rst +docs/conf.py +docs/create_app.rst +docs/creating_release.txt +docs/dumpscript.rst +docs/export_emails.rst +docs/field_extensions.rst +docs/graph_models.rst +docs/index.rst +docs/installation_instructions.rst +docs/jobs_scheduling.rst +docs/model_extensions.rst +docs/namespace_proposal.rst +docs/print_settings.rst +docs/runprofileserver.rst +docs/runscript.rst +docs/runserver_plus.rst +docs/shell_plus.rst +docs/sqlcreate.rst +docs/sqldiff.rst +docs/sync_s3.rst +docs/validate_templates.rst \ No newline at end of file diff --git a/contrib/django_extensions/django_extensions.egg-info/dependency_links.txt b/contrib/django_extensions/django_extensions.egg-info/dependency_links.txt new file mode 100644 index 00000000..8b137891 --- /dev/null +++ b/contrib/django_extensions/django_extensions.egg-info/dependency_links.txt @@ -0,0 +1 @@ + diff --git a/contrib/django_extensions/django_extensions.egg-info/requires.txt b/contrib/django_extensions/django_extensions.egg-info/requires.txt new file mode 100644 index 00000000..64c56a36 --- /dev/null +++ b/contrib/django_extensions/django_extensions.egg-info/requires.txt @@ -0,0 +1 @@ +six \ No newline at end of file diff --git a/contrib/django_extensions/django_extensions.egg-info/top_level.txt b/contrib/django_extensions/django_extensions.egg-info/top_level.txt new file mode 100644 index 00000000..ee493865 --- /dev/null +++ b/contrib/django_extensions/django_extensions.egg-info/top_level.txt @@ -0,0 +1 @@ +django_extensions diff --git a/contrib/django_extensions/django_extensions/__init__.py b/contrib/django_extensions/django_extensions/__init__.py new file mode 100644 index 00000000..321eae2c --- /dev/null +++ b/contrib/django_extensions/django_extensions/__init__.py @@ -0,0 +1,13 @@ + +VERSION = (1, 2, 3, 'DEV') + +# Dynamically calculate the version based on VERSION tuple +if len(VERSION) > 2 and VERSION[2] is not None: + if isinstance(VERSION[2], int): + str_version = "%s.%s.%s" % VERSION[:3] + else: + str_version = "%s.%s_%s" % VERSION[:3] +else: + str_version = "%s.%s" % VERSION[:2] + +__version__ = str_version diff --git a/contrib/django_extensions/django_extensions/admin/__init__.py b/contrib/django_extensions/django_extensions/admin/__init__.py new file mode 100644 index 00000000..7564ea65 --- /dev/null +++ b/contrib/django_extensions/django_extensions/admin/__init__.py @@ -0,0 +1,145 @@ +# +# Autocomplete feature for admin panel +# +# Most of the code has been written by Jannis Leidel and was updated a bit +# for django_extensions. +# http://jannisleidel.com/2008/11/autocomplete-form-widget-foreignkey-model-fields/ +# +# to_string_function, Satchmo adaptation and some comments added by emes +# (Michal Salaban) +# + +import six +import operator +from six.moves import reduce +from django.http import HttpResponse, HttpResponseNotFound +from django.db import models +from django.db.models.query import QuerySet +from django.utils.encoding import smart_str +from django.utils.translation import ugettext as _ +from django.utils.text import get_text_list +try: + from functools import update_wrapper + assert update_wrapper +except ImportError: + from django.utils.functional import update_wrapper + +from django_extensions.admin.widgets import ForeignKeySearchInput + +from django.conf import settings + +if 'reversion' in settings.INSTALLED_APPS: + from reversion.admin import VersionAdmin as ModelAdmin + assert ModelAdmin +else: + from django.contrib.admin import ModelAdmin + + +class ForeignKeyAutocompleteAdmin(ModelAdmin): + """Admin class for models using the autocomplete feature. + + There are two additional fields: + - related_search_fields: defines fields of managed model that + have to be represented by autocomplete input, together with + a list of target model fields that are searched for + input string, e.g.: + + related_search_fields = { + 'author': ('first_name', 'email'), + } + + - related_string_functions: contains optional functions which + take target model instance as only argument and return string + representation. By default __unicode__() method of target + object is used. + """ + + related_search_fields = {} + related_string_functions = {} + + def get_urls(self): + try: + from django.conf.urls import patterns, url + except ImportError: # django < 1.4 + from django.conf.urls.defaults import patterns, url + + def wrap(view): + def wrapper(*args, **kwargs): + return self.admin_site.admin_view(view)(*args, **kwargs) + return update_wrapper(wrapper, view) + + info = self.model._meta.app_label, self.model._meta.module_name + + urlpatterns = patterns('', url(r'foreignkey_autocomplete/$', wrap(self.foreignkey_autocomplete), name='%s_%s_autocomplete' % info)) + urlpatterns += super(ForeignKeyAutocompleteAdmin, self).get_urls() + return urlpatterns + + def foreignkey_autocomplete(self, request): + """ + Searches in the fields of the given related model and returns the + result as a simple string to be used by the jQuery Autocomplete plugin + """ + query = request.GET.get('q', None) + app_label = request.GET.get('app_label', None) + model_name = request.GET.get('model_name', None) + search_fields = request.GET.get('search_fields', None) + object_pk = request.GET.get('object_pk', None) + try: + to_string_function = self.related_string_functions[model_name] + except KeyError: + to_string_function = lambda x: x.__unicode__() + if search_fields and app_label and model_name and (query or object_pk): + def construct_search(field_name): + # use different lookup methods depending on the notation + if field_name.startswith('^'): + return "%s__istartswith" % field_name[1:] + elif field_name.startswith('='): + return "%s__iexact" % field_name[1:] + elif field_name.startswith('@'): + return "%s__search" % field_name[1:] + else: + return "%s__icontains" % field_name + model = models.get_model(app_label, model_name) + queryset = model._default_manager.all() + data = '' + if query: + for bit in query.split(): + or_queries = [models.Q(**{construct_search(smart_str(field_name)): smart_str(bit)}) for field_name in search_fields.split(',')] + other_qs = QuerySet(model) + other_qs.dup_select_related(queryset) + other_qs = other_qs.filter(reduce(operator.or_, or_queries)) + queryset = queryset & other_qs + data = ''.join([six.u('%s|%s\n' % (to_string_function(f), f.pk)) for f in queryset]) + elif object_pk: + try: + obj = queryset.get(pk=object_pk) + except: + pass + else: + data = to_string_function(obj) + return HttpResponse(data) + return HttpResponseNotFound() + + def get_help_text(self, field_name, model_name): + searchable_fields = self.related_search_fields.get(field_name, None) + if searchable_fields: + help_kwargs = { + 'model_name': model_name, + 'field_list': get_text_list(searchable_fields, _('and')), + } + return _('Use the left field to do %(model_name)s lookups in the fields %(field_list)s.') % help_kwargs + return '' + + def formfield_for_dbfield(self, db_field, **kwargs): + """ + Overrides the default widget for Foreignkey fields if they are + specified in the related_search_fields class attribute. + """ + if (isinstance(db_field, models.ForeignKey) and db_field.name in self.related_search_fields): + model_name = db_field.rel.to._meta.object_name + help_text = self.get_help_text(db_field.name, model_name) + if kwargs.get('help_text'): + help_text = six.u('%s %s' % (kwargs['help_text'], help_text)) + kwargs['widget'] = ForeignKeySearchInput(db_field.rel, self.related_search_fields[db_field.name]) + kwargs['help_text'] = help_text + return super(ForeignKeyAutocompleteAdmin, self).formfield_for_dbfield(db_field, **kwargs) diff --git a/contrib/django_extensions/django_extensions/admin/widgets.py b/contrib/django_extensions/django_extensions/admin/widgets.py new file mode 100644 index 00000000..07fa22e2 --- /dev/null +++ b/contrib/django_extensions/django_extensions/admin/widgets.py @@ -0,0 +1,94 @@ +import six +import django +from django import forms +from django.conf import settings +from django.contrib.admin.sites import site +from django.utils.safestring import mark_safe +if django.get_version() >= "1.4": + from django.utils.text import Truncator +else: + from django.utils.text import truncate_words +from django.template.loader import render_to_string +from django.contrib.admin.widgets import ForeignKeyRawIdWidget + + +class ForeignKeySearchInput(ForeignKeyRawIdWidget): + """ + A Widget for displaying ForeignKeys in an autocomplete search input + instead in a + + {% trans + + diff --git a/contrib/django_extensions/django_extensions/templatetags/__init__.py b/contrib/django_extensions/django_extensions/templatetags/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/contrib/django_extensions/django_extensions/templatetags/highlighting.py b/contrib/django_extensions/django_extensions/templatetags/highlighting.py new file mode 100644 index 00000000..78f6dbcf --- /dev/null +++ b/contrib/django_extensions/django_extensions/templatetags/highlighting.py @@ -0,0 +1,91 @@ +""" +Similar to syntax_color.py but this is intended more for being able to +copy+paste actual code into your Django templates without needing to +escape or anything crazy. + +http://lobstertech.com/2008/aug/30/django_syntax_highlight_template_tag/ + +Example: + + {% load highlighting %} + + + +

check out this code

+ + {% highlight 'python' 'Excerpt: blah.py' %} + def need_food(self): + print("Love is than &death&") + {% endhighlight %} + +""" + +from pygments import highlight as pyghighlight +from pygments.lexers import get_lexer_by_name +from pygments.formatters import HtmlFormatter +from django import template +from django.template import Template, Context, Node, Variable, TemplateSyntaxError +from django.template.defaultfilters import stringfilter +from django.utils.safestring import mark_safe + +register = template.Library() + + +@register.filter +@stringfilter +def parse_template(value): + return mark_safe(Template(value).render(Context())) +parse_template.is_safe = True + + +class CodeNode(Node): + def __init__(self, language, nodelist, name=''): + self.language = Variable(language) + self.nodelist = nodelist + if name: + self.name = Variable(name) + else: + self.name = None + + def render(self, context): + code = self.nodelist.render(context).strip() + lexer = get_lexer_by_name(self.language.resolve(context)) + formatter = HtmlFormatter(linenos=False) + html = "" + if self.name: + name = self.name.resolve(context) + html = '
%s
' % (name) + return html + pyghighlight(code, lexer, formatter) + + +@register.tag +def highlight(parser, token): + """ + Allows you to put a highlighted source code
 block in your code.
+    This takes two arguments, the language and a little explaination message
+    that will be generated before the code.  The second argument is optional.
+
+    Your code will be fed through pygments so you can use any language it
+    supports.
+
+    {% load highlighting %}
+    {% highlight 'python' 'Excerpt: blah.py' %}
+    def need_food(self):
+        print("Love is colder than death")
+    {% endhighlight %}
+    """
+    nodelist = parser.parse(('endhighlight',))
+    parser.delete_first_token()
+    bits = token.split_contents()[1:]
+    if len(bits) < 1:
+        raise TemplateSyntaxError("'highlight' statement requires an argument")
+    return CodeNode(bits[0], nodelist, *bits[1:])
diff --git a/contrib/django_extensions/django_extensions/templatetags/indent_text.py b/contrib/django_extensions/django_extensions/templatetags/indent_text.py
new file mode 100644
index 00000000..00854238
--- /dev/null
+++ b/contrib/django_extensions/django_extensions/templatetags/indent_text.py
@@ -0,0 +1,55 @@
+from django import template
+
+register = template.Library()
+
+
+class IndentByNode(template.Node):
+    def __init__(self, nodelist, indent_level, if_statement):
+        self.nodelist = nodelist
+        self.indent_level = template.Variable(indent_level)
+        if if_statement:
+            self.if_statement = template.Variable(if_statement)
+        else:
+            self.if_statement = None
+
+    def render(self, context):
+        indent_level = self.indent_level.resolve(context)
+        if self.if_statement:
+            try:
+                if_statement = bool(self.if_statement.resolve(context))
+            except template.VariableDoesNotExist:
+                if_statement = False
+        else:
+            if_statement = True
+        output = self.nodelist.render(context)
+        if if_statement:
+            indent = " " * indent_level
+            output = indent + indent.join(output.splitlines(True))
+        return output
+
+
+def indentby(parser, token):
+    """
+    Adds indentation to text between the tags by the given indentation level.
+
+    {% indentby  [if ] %}
+    ...
+    {% endindentby %}
+
+    Arguments:
+      indent_level - Number of spaces to indent text with.
+      statement - Only apply indent_level if the boolean statement evalutates to True.
+    """
+    args = token.split_contents()
+    largs = len(args)
+    if largs not in (2, 4):
+        raise template.TemplateSyntaxError("%r tag requires 1 or 3 arguments")
+    indent_level = args[1]
+    if_statement = None
+    if largs == 4:
+        if_statement = args[3]
+    nodelist = parser.parse(('endindentby', ))
+    parser.delete_first_token()
+    return IndentByNode(nodelist, indent_level, if_statement)
+
+indentby = register.tag(indentby)
diff --git a/contrib/django_extensions/django_extensions/templatetags/syntax_color.py b/contrib/django_extensions/django_extensions/templatetags/syntax_color.py
new file mode 100644
index 00000000..4c4c23dc
--- /dev/null
+++ b/contrib/django_extensions/django_extensions/templatetags/syntax_color.py
@@ -0,0 +1,97 @@
+r"""
+Template filter for rendering a string with syntax highlighting.
+It relies on Pygments to accomplish this.
+
+Some standard usage examples (from within Django templates).
+Coloring a string with the Python lexer:
+
+    {% load syntax_color %}
+    {{ code_string|colorize:"python" }}
+
+You may use any lexer in Pygments. The complete list of which
+can be found [on the Pygments website][1].
+
+[1]: http://pygments.org/docs/lexers/
+
+You may also have Pygments attempt to guess the correct lexer for
+a particular string. However, if may not be able to choose a lexer,
+in which case it will simply return the string unmodified. This is
+less efficient compared to specifying the lexer to use.
+
+    {{ code_string|colorize }}
+
+You may also render the syntax highlighed text with line numbers.
+
+    {% load syntax_color %}
+    {{ some_code|colorize_table:"html+django" }}
+    {{ let_pygments_pick_for_this_code|colorize_table }}
+
+Please note that before you can load the ``syntax_color`` template filters
+you will need to add the ``django_extensions.utils`` application to the
+``INSTALLED_APPS``setting in your project's ``settings.py`` file.
+"""
+
+__author__ = 'Will Larson '
+
+
+from django import template
+from django.template.defaultfilters import stringfilter
+from django.utils.safestring import mark_safe
+from django.core.exceptions import ImproperlyConfigured
+
+try:
+    from pygments import highlight
+    from pygments.formatters import HtmlFormatter
+    from pygments.lexers import get_lexer_by_name, guess_lexer, ClassNotFound
+except ImportError:
+    raise ImproperlyConfigured(
+        "Please install 'pygments' library to use syntax_color.")
+
+register = template.Library()
+
+
+@register.simple_tag
+def pygments_css():
+    return HtmlFormatter().get_style_defs('.highlight')
+
+
+def generate_pygments_css(path=None):
+    if path is None:
+        import os
+        path = os.path.join(os.getcwd(), 'pygments.css')
+    f = open(path, 'w')
+    f.write(pygments_css())
+    f.close()
+
+
+def get_lexer(value, arg):
+    if arg is None:
+        return guess_lexer(value)
+    return get_lexer_by_name(arg)
+
+
+@register.filter(name='colorize')
+@stringfilter
+def colorize(value, arg=None):
+    try:
+        return mark_safe(highlight(value, get_lexer(value, arg), HtmlFormatter()))
+    except ClassNotFound:
+        return value
+
+
+@register.filter(name='colorize_table')
+@stringfilter
+def colorize_table(value, arg=None):
+    try:
+        return mark_safe(highlight(value, get_lexer(value, arg), HtmlFormatter(linenos='table')))
+    except ClassNotFound:
+        return value
+
+
+@register.filter(name='colorize_noclasses')
+@stringfilter
+def colorize_noclasses(value, arg=None):
+    try:
+        return mark_safe(highlight(value, get_lexer(value, arg), HtmlFormatter(noclasses=True)))
+    except ClassNotFound:
+        return value
diff --git a/contrib/django_extensions/django_extensions/templatetags/truncate_letters.py b/contrib/django_extensions/django_extensions/templatetags/truncate_letters.py
new file mode 100644
index 00000000..b77a6b1f
--- /dev/null
+++ b/contrib/django_extensions/django_extensions/templatetags/truncate_letters.py
@@ -0,0 +1,28 @@
+import django
+from django import template
+from django.template.defaultfilters import stringfilter
+
+register = template.Library()
+
+
+def truncateletters(value, arg):
+    """
+    Truncates a string after a certain number of letters
+
+    Argument: Number of letters to truncate after
+    """
+    from django_extensions.utils.text import truncate_letters
+    try:
+        length = int(arg)
+    except ValueError:  # invalid literal for int()
+        return value  # Fail silently
+    return truncate_letters(value, length)
+
+if django.get_version() >= "1.4":
+    truncateletters = stringfilter(truncateletters)
+    register.filter(truncateletters, is_safe=True)
+else:
+    truncateletters.is_safe = True
+    truncateletters = stringfilter(truncateletters)
+    register.filter(truncateletters)
+
diff --git a/contrib/django_extensions/django_extensions/templatetags/widont.py b/contrib/django_extensions/django_extensions/templatetags/widont.py
new file mode 100644
index 00000000..d42833f9
--- /dev/null
+++ b/contrib/django_extensions/django_extensions/templatetags/widont.py
@@ -0,0 +1,62 @@
+from django.template import Library
+from django.utils.encoding import force_unicode
+import re
+import six
+
+register = Library()
+re_widont = re.compile(r'\s+(\S+\s*)$')
+re_widont_html = re.compile(r'([^<>\s])\s+([^<>\s]+\s*)(]*>|$)', re.IGNORECASE)
+
+
+def widont(value, count=1):
+    """
+    Adds an HTML non-breaking space between the final two words of the string to
+    avoid "widowed" words.
+
+    Examples:
+
+    >>> print(widont('Test   me   out'))
+    Test   me out
+
+    >>> widont('It works with trailing spaces too  ')
+    u'It works with trailing spaces too  '
+
+    >>> print(widont('NoEffect'))
+    NoEffect
+    """
+    def replace(matchobj):
+        return six.u(' %s' % matchobj.group(1))
+    for i in range(count):
+        value = re_widont.sub(replace, force_unicode(value))
+    return value
+
+
+def widont_html(value):
+    """
+    Adds an HTML non-breaking space between the final two words at the end of
+    (and in sentences just outside of) block level tags to avoid "widowed"
+    words.
+
+    Examples:
+
+    >>> print(widont_html('

Here is a simple example

Single

')) +

Here is a simple example

Single

+ + >>> print(widont_html('

test me
out

Ok?

Not in a p

and this

')) +

test me
out

Ok?

Not in a p

and this

+ + >>> print(widont_html('leading text

test me out

trailing text')) + leading text

test me out

trailing text + """ + def replace(matchobj): + return six.u('%s %s%s' % matchobj.groups()) + return re_widont_html.sub(replace, force_unicode(value)) + +register.filter(widont) +register.filter(widont_html) + +if __name__ == "__main__": + def _test(): + import doctest + doctest.testmod() + _test() diff --git a/contrib/django_extensions/django_extensions/tests/__init__.py b/contrib/django_extensions/django_extensions/tests/__init__.py new file mode 100644 index 00000000..2da4f14d --- /dev/null +++ b/contrib/django_extensions/django_extensions/tests/__init__.py @@ -0,0 +1,19 @@ +from django.db import models # NOQA +from django_extensions.tests.test_dumpscript import DumpScriptTests +from django_extensions.tests.utils import TruncateLetterTests +from django_extensions.tests.json_field import JsonFieldTest +from django_extensions.tests.uuid_field import UUIDFieldTest +from django_extensions.tests.fields import AutoSlugFieldTest +from django_extensions.tests.management_command import CommandTest, ShowTemplateTagsTests + + +__test_classes__ = [ + DumpScriptTests, JsonFieldTest, UUIDFieldTest, AutoSlugFieldTest, CommandTest, + ShowTemplateTagsTests, TruncateLetterTests +] + +try: + from django_extensions.tests.encrypted_fields import EncryptedFieldsTestCase + __test_classes__.append(EncryptedFieldsTestCase) +except ImportError: + pass diff --git a/contrib/django_extensions/django_extensions/tests/encrypted_fields.py b/contrib/django_extensions/django_extensions/tests/encrypted_fields.py new file mode 100644 index 00000000..ad391e83 --- /dev/null +++ b/contrib/django_extensions/django_extensions/tests/encrypted_fields.py @@ -0,0 +1,270 @@ +from contextlib import contextmanager +import functools + +from django.conf import settings +from django.core.management import call_command +from django.db import connection, models +from django.db.models import loading +from django.utils import unittest + +from django_extensions.tests.models import Secret + +# Only perform encrypted fields tests if keyczar is present. Resolves +# http://github.com/django-extensions/django-extensions/issues/#issue/17 +try: + from django_extensions.db.fields.encrypted import EncryptedTextField, EncryptedCharField # NOQA + from keyczar import keyczar, keyczart, keyinfo # NOQA + keyczar_active = True +except ImportError: + keyczar_active = False + + +def run_if_active(func): + "Method decorator that only runs a test if KeyCzar is available." + + @functools.wraps(func) + def inner(self): + if not keyczar_active: + return + return func(self) + return inner + + +# Locations of both private and public keys. +KEY_LOCS = getattr(settings, 'ENCRYPTED_FIELD_KEYS_DIR', {}) + + +@contextmanager +def keys(purpose, mode=None): + """ + A context manager that sets up the correct KeyCzar environment for a test. + + Arguments: + purpose: Either keyczar.keyinfo.DECRYPT_AND_ENCRYPT or + keyczar.keyinfo.ENCRYPT. + mode: If truthy, settings.ENCRYPTED_FIELD_MODE will be set to (and then + reverted from) this value. If falsy, settings.ENCRYPTED_FIELD_MODE + will not be changed. Optional. Default: None. + + Yields: + A Keyczar subclass for the stated purpose. This will be keyczar.Crypter + for DECRYPT_AND_ENCRYPT or keyczar.Encrypter for ENCRYPT. In addition, + settings.ENCRYPTED_FIELD_KEYS_DIR will be set correctly, and then + reverted when the manager exits. + """ + # Store the original settings so we can restore when the manager exits. + orig_setting_dir = getattr(settings, 'ENCRYPTED_FIELD_KEYS_DIR', None) + orig_setting_mode = getattr(settings, 'ENCRYPTED_FIELD_MODE', None) + try: + if mode: + settings.ENCRYPTED_FIELD_MODE = mode + + if purpose == keyinfo.DECRYPT_AND_ENCRYPT: + settings.ENCRYPTED_FIELD_KEYS_DIR = KEY_LOCS['DECRYPT_AND_ENCRYPT'] + yield keyczar.Crypter.Read(settings.ENCRYPTED_FIELD_KEYS_DIR) + else: + settings.ENCRYPTED_FIELD_KEYS_DIR = KEY_LOCS['ENCRYPT'] + yield keyczar.Encrypter.Read(settings.ENCRYPTED_FIELD_KEYS_DIR) + + except: + raise # Reraise any exceptions. + + finally: + # Restore settings. + settings.ENCRYPTED_FIELD_KEYS_DIR = orig_setting_dir + if mode: + if orig_setting_mode: + settings.ENCRYPTED_FIELD_MODE = orig_setting_mode + else: + del settings.ENCRYPTED_FIELD_MODE + + +@contextmanager +def secret_model(): + """ + A context manager that yields a Secret model defined at runtime. + + All EncryptedField init logic occurs at model class definition time, not at + object instantiation time. This means that in order to test different keys + and modes, we must generate a new class definition at runtime, after + establishing the correct KeyCzar settings. This context manager handles + that process. + + See http://dynamic-models.readthedocs.org/en/latest/ and + https://docs.djangoproject.com/en/dev/topics/db/models/ + #differences-between-proxy-inheritance-and-unmanaged-models + """ + + # Store Django's cached model, if present, so we can restore when the + # manager exits. + orig_model = None + try: + orig_model = loading.cache.app_models['tests']['secret'] + del loading.cache.app_models['tests']['secret'] + except KeyError: + pass + + try: + # Create a new class that shadows tests.models.Secret. + attrs = { + 'name': EncryptedCharField("Name", max_length=Secret._meta.get_field('name').max_length), + 'text': EncryptedTextField("Text"), + '__module__': 'django_extensions.tests.models', + 'Meta': type('Meta', (object, ), { + 'managed': False, + 'db_table': Secret._meta.db_table + }) + } + yield type('Secret', (models.Model, ), attrs) + + except: + raise # Reraise any exceptions. + + finally: + # Restore Django's model cache. + try: + loading.cache.app_models['tests']['secret'] = orig_model + except KeyError: + pass + + +class EncryptedFieldsTestCase(unittest.TestCase): + + def setUp(self): + self.old_installed_apps = settings.INSTALLED_APPS + settings.INSTALLED_APPS = list(settings.INSTALLED_APPS) + settings.INSTALLED_APPS.append('django_extensions.tests') + loading.cache.loaded = False + call_command('syncdb', verbosity=0) + + def tearDown(self): + settings.INSTALLED_APPS = self.old_installed_apps + + @run_if_active + def testCharFieldCreate(self): + """ + Uses a private key to encrypt data on model creation. + Verifies the data is encrypted in the database and can be decrypted. + """ + with keys(keyinfo.DECRYPT_AND_ENCRYPT) as crypt: + with secret_model() as model: + test_val = "Test Secret" + secret = model.objects.create(name=test_val) + + cursor = connection.cursor() + query = "SELECT name FROM %s WHERE id = %d" % (model._meta.db_table, secret.id) + cursor.execute(query) + db_val, = cursor.fetchone() + decrypted_val = crypt.Decrypt(db_val[len(EncryptedCharField.prefix):]) + self.assertEqual(test_val, decrypted_val) + + @run_if_active + def testCharFieldRead(self): + """ + Uses a private key to encrypt data on model creation. + Verifies the data is decrypted when reading the value back from the + model. + """ + with keys(keyinfo.DECRYPT_AND_ENCRYPT): + with secret_model() as model: + test_val = "Test Secret" + secret = model.objects.create(name=test_val) + retrieved_secret = model.objects.get(id=secret.id) + self.assertEqual(test_val, retrieved_secret.name) + + @run_if_active + def testTextFieldCreate(self): + """ + Uses a private key to encrypt data on model creation. + Verifies the data is encrypted in the database and can be decrypted. + """ + with keys(keyinfo.DECRYPT_AND_ENCRYPT) as crypt: + with secret_model() as model: + test_val = "Test Secret" + secret = model.objects.create(text=test_val) + cursor = connection.cursor() + query = "SELECT text FROM %s WHERE id = %d" % (model._meta.db_table, secret.id) + cursor.execute(query) + db_val, = cursor.fetchone() + decrypted_val = crypt.Decrypt(db_val[len(EncryptedCharField.prefix):]) + self.assertEqual(test_val, decrypted_val) + + @run_if_active + def testTextFieldRead(self): + """ + Uses a private key to encrypt data on model creation. + Verifies the data is decrypted when reading the value back from the + model. + """ + with keys(keyinfo.DECRYPT_AND_ENCRYPT): + with secret_model() as model: + test_val = "Test Secret" + secret = model.objects.create(text=test_val) + retrieved_secret = model.objects.get(id=secret.id) + self.assertEqual(test_val, retrieved_secret.text) + + @run_if_active + def testCannotDecrypt(self): + """ + Uses a public key to encrypt data on model creation. + Verifies that the data cannot be decrypted using the same key. + """ + with keys(keyinfo.ENCRYPT, mode=keyinfo.ENCRYPT.name): + with secret_model() as model: + test_val = "Test Secret" + secret = model.objects.create(name=test_val) + retrieved_secret = model.objects.get(id=secret.id) + self.assertNotEqual(test_val, retrieved_secret.name) + self.assertTrue(retrieved_secret.name.startswith(EncryptedCharField.prefix)) + + @run_if_active + def testUnacceptablePurpose(self): + """ + Tries to create an encrypted field with a mode mismatch. + A purpose of "DECRYPT_AND_ENCRYPT" cannot be used with a public key, + since public keys cannot be used for decryption. This should raise an + exception. + """ + with self.assertRaises(keyczar.errors.KeyczarError): + with keys(keyinfo.ENCRYPT): + with secret_model(): + # A KeyCzar exception should get raised during class + # definition time, so any code in here would never get run. + pass + + @run_if_active + def testDecryptionForbidden(self): + """ + Uses a private key to encrypt data, but decryption is not allowed. + ENCRYPTED_FIELD_MODE is explicitly set to ENCRYPT, meaning data should + not be decrypted, even though the key would allow for it. + """ + with keys(keyinfo.DECRYPT_AND_ENCRYPT, mode=keyinfo.ENCRYPT.name): + with secret_model() as model: + test_val = "Test Secret" + secret = model.objects.create(name=test_val) + retrieved_secret = model.objects.get(id=secret.id) + self.assertNotEqual(test_val, retrieved_secret.name) + self.assertTrue(retrieved_secret.name.startswith(EncryptedCharField.prefix)) + + @run_if_active + def testEncryptPublicDecryptPrivate(self): + """ + Uses a public key to encrypt, and a private key to decrypt data. + """ + test_val = "Test Secret" + + # First, encrypt data with public key and save to db. + with keys(keyinfo.ENCRYPT, mode=keyinfo.ENCRYPT.name): + with secret_model() as model: + secret = model.objects.create(name=test_val) + enc_retrieved_secret = model.objects.get(id=secret.id) + self.assertNotEqual(test_val, enc_retrieved_secret.name) + self.assertTrue(enc_retrieved_secret.name.startswith(EncryptedCharField.prefix)) + + # Next, retrieve data from db, and decrypt with private key. + with keys(keyinfo.DECRYPT_AND_ENCRYPT): + with secret_model() as model: + retrieved_secret = model.objects.get(id=secret.id) + self.assertEqual(test_val, retrieved_secret.name) + diff --git a/contrib/django_extensions/django_extensions/tests/fields.py b/contrib/django_extensions/django_extensions/tests/fields.py new file mode 100644 index 00000000..3edd360e --- /dev/null +++ b/contrib/django_extensions/django_extensions/tests/fields.py @@ -0,0 +1,105 @@ +from django.conf import settings +from django.core.management import call_command +from django.db.models import loading +from django.db import models +from django.utils import unittest + +from django_extensions.db.fields import AutoSlugField + + +class SluggedTestModel(models.Model): + title = models.CharField(max_length=42) + slug = AutoSlugField(populate_from='title') + + +class ChildSluggedTestModel(SluggedTestModel): + pass + + +class AutoSlugFieldTest(unittest.TestCase): + def setUp(self): + self.old_installed_apps = settings.INSTALLED_APPS + settings.INSTALLED_APPS = list(settings.INSTALLED_APPS) + settings.INSTALLED_APPS.append('django_extensions.tests') + loading.cache.loaded = False + call_command('syncdb', verbosity=0) + + def tearDown(self): + SluggedTestModel.objects.all().delete() + settings.INSTALLED_APPS = self.old_installed_apps + + def testAutoCreateSlug(self): + m = SluggedTestModel(title='foo') + m.save() + self.assertEqual(m.slug, 'foo') + + def testAutoCreateNextSlug(self): + m = SluggedTestModel(title='foo') + m.save() + + m = SluggedTestModel(title='foo') + m.save() + self.assertEqual(m.slug, 'foo-2') + + def testAutoCreateSlugWithNumber(self): + m = SluggedTestModel(title='foo 2012') + m.save() + self.assertEqual(m.slug, 'foo-2012') + + def testAutoUpdateSlugWithNumber(self): + m = SluggedTestModel(title='foo 2012') + m.save() + m.save() + self.assertEqual(m.slug, 'foo-2012') + + def testUpdateSlug(self): + m = SluggedTestModel(title='foo') + m.save() + + # update m instance without using `save' + SluggedTestModel.objects.filter(pk=m.pk).update(slug='foo-2012') + # update m instance with new data from the db + m = SluggedTestModel.objects.get(pk=m.pk) + + self.assertEqual(m.slug, 'foo-2012') + + m.save() + self.assertEqual(m.slug, 'foo-2012') + + def testSimpleSlugSource(self): + m = SluggedTestModel(title='-foo') + m.save() + self.assertEqual(m.slug, 'foo') + + n = SluggedTestModel(title='-foo') + n.save() + self.assertEqual(n.slug, 'foo-2') + + n.save() + self.assertEqual(n.slug, 'foo-2') + + def testEmptySlugSource(self): + # regression test + + m = SluggedTestModel(title='') + m.save() + self.assertEqual(m.slug, '-2') + + n = SluggedTestModel(title='') + n.save() + self.assertEqual(n.slug, '-3') + + n.save() + self.assertEqual(n.slug, '-3') + + def testInheritanceCreatesNextSlug(self): + m = SluggedTestModel(title='foo') + m.save() + + n = ChildSluggedTestModel(title='foo') + n.save() + self.assertEqual(n.slug, 'foo-2') + + o = SluggedTestModel(title='foo') + o.save() + self.assertEqual(o.slug, 'foo-3') diff --git a/contrib/django_extensions/django_extensions/tests/json_field.py b/contrib/django_extensions/django_extensions/tests/json_field.py new file mode 100644 index 00000000..e9aed0ff --- /dev/null +++ b/contrib/django_extensions/django_extensions/tests/json_field.py @@ -0,0 +1,37 @@ +from django.conf import settings +from django.core.management import call_command +from django.db.models import loading +from django.db import models +from django.utils import unittest + +from django_extensions.db.fields.json import JSONField + + +class TestModel(models.Model): + a = models.IntegerField() + j_field = JSONField() + + +class JsonFieldTest(unittest.TestCase): + def setUp(self): + self.old_installed_apps = settings.INSTALLED_APPS + settings.INSTALLED_APPS = list(settings.INSTALLED_APPS) + settings.INSTALLED_APPS.append('django_extensions.tests') + loading.cache.loaded = False + call_command('syncdb', verbosity=0) + + def tearDown(self): + settings.INSTALLED_APPS = self.old_installed_apps + + def testCharFieldCreate(self): + j = TestModel.objects.create(a=6, j_field=dict(foo='bar')) + self.assertEqual(j.a, 6) + + def testDefault(self): + j = TestModel.objects.create(a=1) + self.assertEqual(j.j_field, {}) + + def testEmptyList(self): + j = TestModel.objects.create(a=6, j_field=[]) + self.assertTrue(isinstance(j.j_field, list)) + self.assertEqual(j.j_field, []) diff --git a/contrib/django_extensions/django_extensions/tests/management/__init__.py b/contrib/django_extensions/django_extensions/tests/management/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/contrib/django_extensions/django_extensions/tests/management/commands/__init__.py b/contrib/django_extensions/django_extensions/tests/management/commands/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/contrib/django_extensions/django_extensions/tests/management/commands/error_raising_command.py b/contrib/django_extensions/django_extensions/tests/management/commands/error_raising_command.py new file mode 100644 index 00000000..3bc646cf --- /dev/null +++ b/contrib/django_extensions/django_extensions/tests/management/commands/error_raising_command.py @@ -0,0 +1,10 @@ + +from django_extensions.management.base import LoggingBaseCommand + + +class Command(LoggingBaseCommand): + help = 'Test error' + + def handle(self, *args, **options): + raise Exception("Test Error") + diff --git a/contrib/django_extensions/django_extensions/tests/management_command.py b/contrib/django_extensions/django_extensions/tests/management_command.py new file mode 100644 index 00000000..86c6d381 --- /dev/null +++ b/contrib/django_extensions/django_extensions/tests/management_command.py @@ -0,0 +1,60 @@ +# -*- coding: utf-8 -*- +import logging + +try: + from cStringIO import StringIO # NOQA +except ImportError: + from io import StringIO # NOQA + +try: + import importlib # NOQA +except ImportError: + from django.utils import importlib # NOQA + +from django.core.management import call_command +from django.test import TestCase + + +class MockLoggingHandler(logging.Handler): + """ Mock logging handler to check for expected logs. """ + + def __init__(self, *args, **kwargs): + self.reset() + logging.Handler.__init__(self, *args, **kwargs) + + def emit(self, record): + self.messages[record.levelname.lower()].append(record.getMessage()) + + def reset(self): + self.messages = { + 'debug': [], + 'info': [], + 'warning': [], + 'error': [], + 'critical': [], + } + + +class CommandTest(TestCase): + def test_error_logging(self): + # Ensure command errors are properly logged and reraised + from django_extensions.management.base import logger + logger.addHandler(MockLoggingHandler()) + module_path = "django_extensions.tests.management.commands.error_raising_command" + module = importlib.import_module(module_path) + error_raising_command = module.Command() + self.assertRaises(Exception, error_raising_command.execute) + handler = logger.handlers[0] + self.assertEqual(len(handler.messages['error']), 1) + + +class ShowTemplateTagsTests(TestCase): + def test_some_output(self): + out = StringIO() + call_command('show_templatetags', stdout=out) + output = out.getvalue() + # Once django_extension is installed during tests it should appear with + # its templatetags + self.assertIn('django_extensions', output) + # let's check at least one + self.assertIn('truncate_letters', output) diff --git a/contrib/django_extensions/django_extensions/tests/models.py b/contrib/django_extensions/django_extensions/tests/models.py new file mode 100644 index 00000000..b3a0aadb --- /dev/null +++ b/contrib/django_extensions/django_extensions/tests/models.py @@ -0,0 +1,21 @@ +from django.db import models + + +class Secret(models.Model): + name = models.CharField(blank=True, max_length=255, null=True) + text = models.TextField(blank=True, null=True) + + +class Name(models.Model): + name = models.CharField(max_length=50) + + +class Note(models.Model): + note = models.TextField() + + +class Person(models.Model): + name = models.ForeignKey(Name) + age = models.PositiveIntegerField() + children = models.ManyToManyField('self') + notes = models.ManyToManyField(Note) diff --git a/contrib/django_extensions/django_extensions/tests/test_dumpscript.py b/contrib/django_extensions/django_extensions/tests/test_dumpscript.py new file mode 100644 index 00000000..90aa2898 --- /dev/null +++ b/contrib/django_extensions/django_extensions/tests/test_dumpscript.py @@ -0,0 +1,94 @@ +import sys +import six + +if sys.version_info[:2] >= (2, 6): + import ast as compiler # NOQA +else: + import compiler # NOQA + +from django.test import TestCase + +from django.core.management import call_command +from django_extensions.tests.models import Name, Note, Person + +from django.conf import settings +from django.db.models import loading + + +class DumpScriptTests(TestCase): + def setUp(self): + self.real_stdout = sys.stdout + self.real_stderr = sys.stderr + sys.stdout = six.StringIO() + sys.stderr = six.StringIO() + + self.original_installed_apps = settings.INSTALLED_APPS + settings.INSTALLED_APPS = list(settings.INSTALLED_APPS) + settings.INSTALLED_APPS.append('django_extensions.tests') + loading.cache.loaded = False + call_command('syncdb', verbosity=0) + + def tearDown(self): + sys.stdout = self.real_stdout + sys.stderr = self.real_stderr + settings.INSTALLED_APPS.remove('django_extensions.tests') + settings.INSTALLED_APPS = self.original_installed_apps + loading.cache.loaded = False + + def test_runs(self): + # lame test...does it run? + n = Name(name='Gabriel') + n.save() + call_command('dumpscript', 'tests') + self.assertTrue('Gabriel' in sys.stdout.getvalue()) + + #---------------------------------------------------------------------- + def test_replaced_stdout(self): + # check if stdout can be replaced + sys.stdout = six.StringIO() + n = Name(name='Mike') + n.save() + tmp_out = six.StringIO() + call_command('dumpscript', 'tests', stdout=tmp_out) + self.assertTrue('Mike' in tmp_out.getvalue()) # script should go to tmp_out + self.assertEqual(0, len(sys.stdout.getvalue())) # there should not be any output to sys.stdout + tmp_out.close() + + #---------------------------------------------------------------------- + def test_replaced_stderr(self): + # check if stderr can be replaced, without changing stdout + n = Name(name='Fred') + n.save() + tmp_err = six.StringIO() + sys.stderr = six.StringIO() + call_command('dumpscript', 'tests', stderr=tmp_err) + self.assertTrue('Fred' in sys.stdout.getvalue()) # script should still go to stdout + self.assertTrue('Name' in tmp_err.getvalue()) # error output should go to tmp_err + self.assertEqual(0, len(sys.stderr.getvalue())) # there should not be any output to sys.stderr + tmp_err.close() + + #---------------------------------------------------------------------- + def test_valid_syntax(self): + n1 = Name(name='John') + n1.save() + p1 = Person(name=n1, age=40) + p1.save() + n2 = Name(name='Jane') + n2.save() + p2 = Person(name=n2, age=18) + p2.save() + p2.children.add(p1) + note1 = Note(note="This is the first note.") + note1.save() + note2 = Note(note="This is the second note.") + note2.save() + p2.notes.add(note1, note2) + tmp_out = six.StringIO() + call_command('dumpscript', 'tests', stdout=tmp_out) + ast_syntax_tree = compiler.parse(tmp_out.getvalue()) + if hasattr(ast_syntax_tree, 'body'): + self.assertTrue(len(ast_syntax_tree.body) > 1) + else: + self.assertTrue(len(ast_syntax_tree.asList()) > 1) + tmp_out.close() + diff --git a/contrib/django_extensions/django_extensions/tests/urls.py b/contrib/django_extensions/django_extensions/tests/urls.py new file mode 100644 index 00000000..e69de29b diff --git a/contrib/django_extensions/django_extensions/tests/utils.py b/contrib/django_extensions/django_extensions/tests/utils.py new file mode 100644 index 00000000..23935b66 --- /dev/null +++ b/contrib/django_extensions/django_extensions/tests/utils.py @@ -0,0 +1,76 @@ +# -*- coding: utf-8 -*- +import sys +import six + +from django.test import TestCase +from django.utils.unittest import skipIf + +from django_extensions.utils.text import truncate_letters +try: + import uuid + assert uuid +except ImportError: + from django_extensions.utils import uuid + + +class TruncateLetterTests(TestCase): + def test_truncate_more_than_text_length(self): + self.assertEqual(six.u("hello tests"), truncate_letters("hello tests", 100)) + + def test_truncate_text(self): + self.assertEqual(six.u("hello..."), truncate_letters("hello tests", 5)) + + def test_truncate_with_range(self): + for i in range(10, -1, -1): + self.assertEqual( + six.u('hello tests'[:i]) + '...', + truncate_letters("hello tests", i) + ) + + def test_with_non_ascii_characters(self): + self.assertEqual( + six.u('\u5ce0 (\u3068\u3046\u3052 t\u014dg...'), + truncate_letters("峠 (とうげ tōge - mountain pass)", 10) + ) + + +class UUIDTests(TestCase): + @skipIf(sys.version_info >= (2, 5, 0), 'uuid already in stdlib') + def test_uuid3(self): + # make a UUID using an MD5 hash of a namespace UUID and a name + self.assertEqual( + uuid.UUID('6fa459ea-ee8a-3ca4-894e-db77e160355e'), + uuid.uuid3(uuid.NAMESPACE_DNS, 'python.org') + ) + + @skipIf(sys.version_info >= (2, 5, 0), 'uuid already in stdlib') + def test_uuid5(self): + # make a UUID using a SHA-1 hash of a namespace UUID and a name + self.assertEqual( + uuid.UUID('886313e1-3b8a-5372-9b90-0c9aee199e5d'), + uuid.uuid5(uuid.NAMESPACE_DNS, 'python.org') + ) + + @skipIf(sys.version_info >= (2, 5, 0), 'uuid already in stdlib') + def test_uuid_str(self): + # make a UUID from a string of hex digits (braces and hyphens ignored) + x = uuid.UUID('{00010203-0405-0607-0809-0a0b0c0d0e0f}') + # convert a UUID to a string of hex digits in standard form + self.assertEqual('00010203-0405-0607-0809-0a0b0c0d0e0f', str(x)) + + @skipIf(sys.version_info >= (2, 5, 0), 'uuid already in stdlib') + def test_uuid_bytes(self): + # make a UUID from a string of hex digits (braces and hyphens ignored) + x = uuid.UUID('{00010203-0405-0607-0809-0a0b0c0d0e0f}') + # get the raw 16 bytes of the UUID + self.assertEqual( + '\\x00\\x01\\x02\\x03\\x04\\x05\\x06\\x07\\x08\\t\\n\\x0b\\x0c\\r\\x0e\\x0f', + x.bytes + ) + + @skipIf(sys.version_info >= (2, 5, 0), 'uuid already in stdlib') + def test_make_uuid_from_byte_string(self): + self.assertEqual( + uuid.UUID(bytes='\\x00\\x01\\x02\\x03\\x04\\x05\\x06\\x07\\x08\\t\\n\\x0b\\x0c\\r\\x0e\\x0f'), + uuid.UUID('00010203-0405-0607-0809-0a0b0c0d0e0f') + ) diff --git a/contrib/django_extensions/django_extensions/tests/uuid_field.py b/contrib/django_extensions/django_extensions/tests/uuid_field.py new file mode 100644 index 00000000..823ff2e2 --- /dev/null +++ b/contrib/django_extensions/django_extensions/tests/uuid_field.py @@ -0,0 +1,58 @@ +import six +from django.conf import settings +from django.core.management import call_command +from django.db.models import loading +from django.db import models +from django.utils import unittest + +from django_extensions.db.fields import UUIDField + + +class TestModel_field(models.Model): + a = models.IntegerField() + uuid_field = UUIDField() + + +class TestModel_pk(models.Model): + uuid_field = UUIDField(primary_key=True) + + +class TestAgregateModel(TestModel_pk): + a = models.IntegerField() + + +class TestManyToManyModel(TestModel_pk): + many = models.ManyToManyField(TestModel_field) + + +class UUIDFieldTest(unittest.TestCase): + def setUp(self): + self.old_installed_apps = settings.INSTALLED_APPS + settings.INSTALLED_APPS = list(settings.INSTALLED_APPS) + settings.INSTALLED_APPS.append('django_extensions.tests') + loading.cache.loaded = False + call_command('syncdb', verbosity=0) + + def tearDown(self): + settings.INSTALLED_APPS = self.old_installed_apps + + def testUUIDFieldCreate(self): + j = TestModel_field.objects.create(a=6, uuid_field=six.u('550e8400-e29b-41d4-a716-446655440000')) + self.assertEqual(j.uuid_field, six.u('550e8400-e29b-41d4-a716-446655440000')) + + def testUUIDField_pkCreate(self): + j = TestModel_pk.objects.create(uuid_field=six.u('550e8400-e29b-41d4-a716-446655440000')) + self.assertEqual(j.uuid_field, six.u('550e8400-e29b-41d4-a716-446655440000')) + self.assertEqual(j.pk, six.u('550e8400-e29b-41d4-a716-446655440000')) + + def testUUIDField_pkAgregateCreate(self): + j = TestAgregateModel.objects.create(a=6, uuid_field=six.u('550e8400-e29b-41d4-a716-446655440001')) + self.assertEqual(j.a, 6) + self.assertIsInstance(j.pk, six.string_types) + self.assertEqual(len(j.pk), 36) + + def testUUIDFieldManyToManyCreate(self): + j = TestManyToManyModel.objects.create(uuid_field=six.u('550e8400-e29b-41d4-a716-446655440010')) + self.assertEqual(j.uuid_field, six.u('550e8400-e29b-41d4-a716-446655440010')) + self.assertEqual(j.pk, six.u('550e8400-e29b-41d4-a716-446655440010')) + diff --git a/contrib/django_extensions/django_extensions/utils/__init__.py b/contrib/django_extensions/django_extensions/utils/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/contrib/django_extensions/django_extensions/utils/dia2django.py b/contrib/django_extensions/django_extensions/utils/dia2django.py new file mode 100755 index 00000000..91a48631 --- /dev/null +++ b/contrib/django_extensions/django_extensions/utils/dia2django.py @@ -0,0 +1,215 @@ +#!/usr/bin/python +# -*- coding: UTF-8 -*- +##Author Igor Támara igor@tamarapatino.org +##Use this little program as you wish, if you +#include it in your work, let others know you +#are using it preserving this note, you have +#the right to make derivative works, Use it +#at your own risk. +#Tested to work on(etch testing 13-08-2007): +# Python 2.4.4 (#2, Jul 17 2007, 11:56:54) +# [GCC 4.1.3 20070629 (prerelease) (Debian 4.1.2-13)] on linux2 + +dependclasses = ["User", "Group", "Permission", "Message"] + +import codecs +import sys +import gzip +from xml.dom.minidom import * # NOQA +import re +import six + +#Type dictionary translation types SQL -> Django +tsd = { + "text": "TextField", + "date": "DateField", + "varchar": "CharField", + "int": "IntegerField", + "float": "FloatField", + "serial": "AutoField", + "boolean": "BooleanField", + "numeric": "FloatField", + "timestamp": "DateTimeField", + "bigint": "IntegerField", + "datetime": "DateTimeField", + "date": "DateField", + "time": "TimeField", + "bool": "BooleanField", + "int": "IntegerField", +} + +#convert varchar -> CharField +v2c = re.compile('varchar\((\d+)\)') + + +def index(fks, id): + """Looks for the id on fks, fks is an array of arrays, each array has on [1] + the id of the class in a dia diagram. When not present returns None, else + it returns the position of the class with id on fks""" + for i, j in fks.items(): + if fks[i][1] == id: + return i + return None + + +def addparentstofks(rels, fks): + """Gets a list of relations, between parents and sons and a dict of + clases named in dia, and modifies the fks to add the parent as fk to get + order on the output of classes and replaces the base class of the son, to + put the class parent name. + """ + for j in rels: + son = index(fks, j[1]) + parent = index(fks, j[0]) + fks[son][2] = fks[son][2].replace("models.Model", parent) + if parent not in fks[son][0]: + fks[son][0].append(parent) + + +def dia2django(archivo): + models_txt = '' + f = codecs.open(archivo, "rb") + #dia files are gzipped + data = gzip.GzipFile(fileobj=f).read() + ppal = parseString(data) + #diagram -> layer -> object -> UML - Class -> name, (attribs : composite -> name,type) + datos = ppal.getElementsByTagName("dia:diagram")[0].getElementsByTagName("dia:layer")[0].getElementsByTagName("dia:object") + clases = {} + herit = [] + imports = six.u("") + for i in datos: + #Look for the classes + if i.getAttribute("type") == "UML - Class": + myid = i.getAttribute("id") + for j in i.childNodes: + if j.nodeType == Node.ELEMENT_NODE and j.hasAttributes(): + if j.getAttribute("name") == "name": + actclas = j.getElementsByTagName("dia:string")[0].childNodes[0].data[1:-1] + myname = "\nclass %s(models.Model) :\n" % actclas + clases[actclas] = [[], myid, myname, 0] + if j.getAttribute("name") == "attributes": + for l in j.getElementsByTagName("dia:composite"): + if l.getAttribute("type") == "umlattribute": + #Look for the attribute name and type + for k in l.getElementsByTagName("dia:attribute"): + if k.getAttribute("name") == "name": + nc = k.getElementsByTagName("dia:string")[0].childNodes[0].data[1:-1] + elif k.getAttribute("name") == "type": + tc = k.getElementsByTagName("dia:string")[0].childNodes[0].data[1:-1] + elif k.getAttribute("name") == "value": + val = k.getElementsByTagName("dia:string")[0].childNodes[0].data[1:-1] + if val == '##': + val = '' + elif k.getAttribute("name") == "visibility" and k.getElementsByTagName("dia:enum")[0].getAttribute("val") == "2": + if tc.replace(" ", "").lower().startswith("manytomanyfield("): + #If we find a class not in our model that is marked as being to another model + newc = tc.replace(" ", "")[16:-1] + if dependclasses.count(newc) == 0: + dependclasses.append(newc) + if tc.replace(" ", "").lower().startswith("foreignkey("): + #If we find a class not in our model that is marked as being to another model + newc = tc.replace(" ", "")[11:-1] + if dependclasses.count(newc) == 0: + dependclasses.append(newc) + + #Mapping SQL types to Django + varch = v2c.search(tc) + if tc.replace(" ", "").startswith("ManyToManyField("): + myfor = tc.replace(" ", "")[16:-1] + if actclas == myfor: + #In case of a recursive type, we use 'self' + tc = tc.replace(myfor, "'self'") + elif clases[actclas][0].count(myfor) == 0: + #Adding related class + if myfor not in dependclasses: + #In case we are using Auth classes or external via protected dia visibility + clases[actclas][0].append(myfor) + tc = "models." + tc + if len(val) > 0: + tc = tc.replace(")", "," + val + ")") + elif tc.find("Field") != -1: + if tc.count("()") > 0 and len(val) > 0: + tc = "models.%s" % tc.replace(")", "," + val + ")") + else: + tc = "models.%s(%s)" % (tc, val) + elif tc.replace(" ", "").startswith("ForeignKey("): + myfor = tc.replace(" ", "")[11:-1] + if actclas == myfor: + #In case of a recursive type, we use 'self' + tc = tc.replace(myfor, "'self'") + elif clases[actclas][0].count(myfor) == 0: + #Adding foreign classes + if myfor not in dependclasses: + #In case we are using Auth classes + clases[actclas][0].append(myfor) + tc = "models." + tc + if len(val) > 0: + tc = tc.replace(")", "," + val + ")") + elif varch is None: + tc = "models." + tsd[tc.strip().lower()] + "(" + val + ")" + else: + tc = "models.CharField(max_length=" + varch.group(1) + ")" + if len(val) > 0: + tc = tc.replace(")", ", " + val + " )") + if not (nc == "id" and tc == "AutoField()"): + clases[actclas][2] = clases[actclas][2] + (" %s = %s\n" % (nc, tc)) + elif i.getAttribute("type") == "UML - Generalization": + mycons = ['A', 'A'] + a = i.getElementsByTagName("dia:connection") + for j in a: + if len(j.getAttribute("to")): + mycons[int(j.getAttribute("handle"))] = j.getAttribute("to") + print(mycons) + if not 'A' in mycons: + herit.append(mycons) + elif i.getAttribute("type") == "UML - SmallPackage": + a = i.getElementsByTagName("dia:string") + for j in a: + if len(j.childNodes[0].data[1:-1]): + imports += six.u("from %s.models import *" % j.childNodes[0].data[1:-1]) + + addparentstofks(herit, clases) + #Ordering the appearance of classes + #First we make a list of the classes each classs is related to. + ordered = [] + for j, k in clases.iteritems(): + k[2] = k[2] + "\n def __unicode__(self):\n return u\"\"\n" + for fk in k[0]: + if fk not in dependclasses: + clases[fk][3] += 1 + ordered.append([j] + k) + + i = 0 + while i < len(ordered): + mark = i + j = i + 1 + while j < len(ordered): + if ordered[i][0] in ordered[j][1]: + mark = j + j += 1 + if mark == i: + i += 1 + else: + # swap %s in %s" % ( ordered[i] , ordered[mark]) to make ordered[i] to be at the end + if ordered[i][0] in ordered[mark][1] and ordered[mark][0] in ordered[i][1]: + #Resolving simplistic circular ForeignKeys + print("Not able to resolve circular ForeignKeys between %s and %s" % (ordered[i][1], ordered[mark][0])) + break + a = ordered[i] + ordered[i] = ordered[mark] + ordered[mark] = a + if i == len(ordered) - 1: + break + ordered.reverse() + if imports: + models_txt = str(imports) + for i in ordered: + models_txt += '%s\n' % str(i[3]) + + return models_txt + +if __name__ == '__main__': + if len(sys.argv) == 2: + dia2django(sys.argv[1]) + else: + print(" Use:\n \n " + sys.argv[0] + " diagram.dia\n\n") diff --git a/contrib/django_extensions/django_extensions/utils/text.py b/contrib/django_extensions/django_extensions/utils/text.py new file mode 100644 index 00000000..fd650cf4 --- /dev/null +++ b/contrib/django_extensions/django_extensions/utils/text.py @@ -0,0 +1,23 @@ +import six + +from django.utils.functional import allow_lazy + +# conditional import, force_unicode was renamed in Django 1.5 +try: + from django.utils.encoding import force_unicode # NOQA +except ImportError: + from django.utils.encoding import force_text as force_unicode # NOQA + + +def truncate_letters(s, num): + """ + truncates a string to a number of letters, similar to truncate_words + """ + s = force_unicode(s) + length = int(num) + if len(s) > length: + s = s[:length] + if not s.endswith('...'): + s += '...' + return s +truncate_letters = allow_lazy(truncate_letters, six.text_type) diff --git a/contrib/django_extensions/django_extensions/utils/validatingtemplatetags.py b/contrib/django_extensions/django_extensions/utils/validatingtemplatetags.py new file mode 100644 index 00000000..b36ca7e1 --- /dev/null +++ b/contrib/django_extensions/django_extensions/utils/validatingtemplatetags.py @@ -0,0 +1,91 @@ +from django.template.base import Library, Node +from django.template import defaulttags +from django.templatetags import future +register = Library() + +error_on_old_style_url_tag = False +new_style_url_tag = False +errors = [] + + +def before_new_template(force_new_urls): + """Reset state ready for new template""" + global new_style_url_tag, error_on_old_style_url_tag, errors + new_style_url_tag = False + error_on_old_style_url_tag = force_new_urls + errors = [] + + +def get_template_errors(): + return errors + + +# Disable extends and include as they are not needed, slow parsing down, and cause duplicate errors +class NoOpNode(Node): + def render(self, context): + return '' + + +@register.tag +def extends(parser, token): + return NoOpNode() + + +@register.tag +def include(parser, token): + return NoOpNode() + + +# We replace load to determine whether new style urls are in use and re-patch url after +# a future version is loaded +@register.tag +def load(parser, token): + global new_style_url_tag + bits = token.contents.split() + + reloaded_url_tag = False + if len(bits) >= 4 and bits[-2] == "from" and bits[-1] == "future": + for name in bits[1:-2]: + if name == "url": + new_style_url_tag = True + reloaded_url_tag = True + + try: + return defaulttags.load(parser, token) + finally: + if reloaded_url_tag: + parser.tags['url'] = new_style_url + + +@register.tag(name='url') +def old_style_url(parser, token): + global error_on_old_style_url_tag + + bits = token.split_contents() + view = bits[1] + + if error_on_old_style_url_tag: + _error("Old style url tag used (only reported once per file): {%% %s %%}" % (" ".join(bits)), token) + error_on_old_style_url_tag = False + + if view[0] in "\"'" and view[0] == view[-1]: + _error("Old style url tag with quotes around view name: {%% %s %%}" % (" ".join(bits)), token) + + return defaulttags.url(parser, token) + + +def new_style_url(parser, token): + bits = token.split_contents() + view = bits[1] + + if view[0] not in "\"'" or view[0] != view[-1]: + _error("New style url tag without quotes around view name: {%% %s %%}" % (" ".join(bits)), token) + + return future.url(parser, token) + + +def _error(message, token): + origin, (start, upto) = token.source + source = origin.reload() + line = source.count("\n", 0, start) + 1 # 1 based line numbering + errors.append((origin, line, message)) diff --git a/contrib/django_extensions/docs/AUTHORS b/contrib/django_extensions/docs/AUTHORS new file mode 100644 index 00000000..4e4bb249 --- /dev/null +++ b/contrib/django_extensions/docs/AUTHORS @@ -0,0 +1,21 @@ +The following individuals have contributed to this project + +Antonio Cavedoni - For the GraphViz stuff (http://cavedoni.com/) +Ludvig Ericson (toxic) +Collin Grady (magus) +Gabriel Grant (gabrielgrant) +Rob Hudson (robhudson) +Jannis Leidel (leidel) +Brian Rosner (brosner) +Michael Trier (empty) +Doug Napoleone (dougn) +Bas van Oostveen (trbs) +David Krauth (dakrauth) +Will Larson (lethain) - For syntax_color template filters. +Patrick Altman (paltman) - Patched sync_media_s3 +Chris Beaven (smileychris) - widont filter +qMax - various graph_model patches +Tyson Clugg (tclugg) - Patched sqldiff +Domen Kožar (iElectric) - staticfiles patch improvement +quinox - original staticfiles patch +Camilo Nova (camilonova) diff --git a/contrib/django_extensions/docs/Makefile b/contrib/django_extensions/docs/Makefile new file mode 100644 index 00000000..969e5985 --- /dev/null +++ b/contrib/django_extensions/docs/Makefile @@ -0,0 +1,75 @@ +# Makefile for Sphinx documentation +# + +# You can set these variables from the command line. +SPHINXOPTS = +SPHINXBUILD = sphinx-build +PAPER = + +# Internal variables. +PAPEROPT_a4 = -D latex_paper_size=a4 +PAPEROPT_letter = -D latex_paper_size=letter +ALLSPHINXOPTS = -d _build/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . + +.PHONY: help clean html web pickle htmlhelp latex changes linkcheck + +help: + @echo "Please use \`make ' where is one of" + @echo " html to make standalone HTML files" + @echo " pickle to make pickle files" + @echo " json to make JSON files" + @echo " htmlhelp to make HTML files and a HTML help project" + @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter" + @echo " changes to make an overview over all changed/added/deprecated items" + @echo " linkcheck to check all external links for integrity" + +clean: + -rm -rf _build/* + +html: + mkdir -p _build/html _build/doctrees _static + $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) _build/html + @echo + @echo "Build finished. The HTML pages are in _build/html." + +pickle: + mkdir -p _build/pickle _build/doctrees + $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) _build/pickle + @echo + @echo "Build finished; now you can process the pickle files." + +web: pickle + +json: + mkdir -p _build/json _build/doctrees + $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) _build/json + @echo + @echo "Build finished; now you can process the JSON files." + +htmlhelp: + mkdir -p _build/htmlhelp _build/doctrees + $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) _build/htmlhelp + @echo + @echo "Build finished; now you can run HTML Help Workshop with the" \ + ".hhp project file in _build/htmlhelp." + +latex: + mkdir -p _build/latex _build/doctrees + $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) _build/latex + @echo + @echo "Build finished; the LaTeX files are in _build/latex." + @echo "Run \`make all-pdf' or \`make all-ps' in that directory to" \ + "run these through (pdf)latex." + +changes: + mkdir -p _build/changes _build/doctrees + $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) _build/changes + @echo + @echo "The overview file is in _build/changes." + +linkcheck: + mkdir -p _build/linkcheck _build/doctrees + $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) _build/linkcheck + @echo + @echo "Link check complete; look for any errors in the above output " \ + "or in _build/linkcheck/output.txt." diff --git a/contrib/django_extensions/docs/admin_extensions.rst b/contrib/django_extensions/docs/admin_extensions.rst new file mode 100644 index 00000000..a6c62127 --- /dev/null +++ b/contrib/django_extensions/docs/admin_extensions.rst @@ -0,0 +1,10 @@ +Current Admin Extensions +======================== + +:synopsis: Current Field Extensions + + +* *ForeignKeyAutocompleteAdmin* - ForeignKeyAutocompleteAdmin will enable the + admin app to show ForeignKey fields with an search input field. The search + field is rendered by the ForeignKeySearchInput form widget and uses jQuery + to do configureable autocompletion. diff --git a/contrib/django_extensions/docs/command_extension_ideas.rst b/contrib/django_extensions/docs/command_extension_ideas.rst new file mode 100644 index 00000000..c33742b6 --- /dev/null +++ b/contrib/django_extensions/docs/command_extension_ideas.rst @@ -0,0 +1,7 @@ +Ideas for New Command Extensions +================================ + +:synopsis: Here are some ideas for some future command extensions. + +* create form/manager for App +* CSS and JS concatenation and minification scripts diff --git a/contrib/django_extensions/docs/command_extensions.rst b/contrib/django_extensions/docs/command_extensions.rst new file mode 100644 index 00000000..435f10e3 --- /dev/null +++ b/contrib/django_extensions/docs/command_extensions.rst @@ -0,0 +1,95 @@ +Current Command Extensions +========================== + +:synopsis: Current Command Extensions + +* :doc:`shell_plus` - An enhanced version of the Django shell. It will autoload + all your models making it easy to work with the ORM right away. + +* `create_app`_ - Creates an application directory structure for the specified + app name. This command allows you to specify the --template option where you + can indicate a template directory structure to use as your default. + +* *create_command* - Creates a command extension directory structure within the + specified application. This makes it easy to get started with adding a + command extension to your application. + +* *create_jobs* - Creates a Django jobs command directory structure for the + given app name in the current directory. This is part of the impressive jobs + system. + +* *create_superuser* - Makes it easy to create a superuser for the + django.contrib.auth. + +* *describe_form* - Used to display a form definition for a model. Copy and + paste the contents into your forms.py and you're ready to go. + +* :doc:`dumpscript ` - Generates a Python script that will + repopulate the database using objects. The advantage of this approach is that + it is easy to understand, and more flexible than directly populating the + database, or using XML. + +* `export_emails`_ - export the email addresses for your + users in one of many formats. Currently supports Address, Google, Outlook, + LinkedIn, and VCard formats. + +* *generate_secret_key* - Creates a new secret key that you can put in your + settings.py module. + +* `graph_models`_ - Creates a GraphViz_ dot file. You need + to send this output to a file yourself. Great for graphing your models. Pass + multiple application names to combine all the models into a single dot file. + +* *mail_debug* - Starts a mail server which echos out the contents of the email + instead of sending it. + +* *passwd* - Makes it easy to reset a user's password. + +* `print_settings`_ - Similar to ``diffsettings`` but shows *all* active Django settings. + +* *print_user_for_session* - Print the user information for the provided + session key. this is very helpful when trying to track down the person who + experienced a site crash. + +* *reset_db* - Resets a database (currently sqlite3, mysql, postgres). + +* *runjob* - Run a single maintenance job. Part of the jobs system. + +* *runjobs* - Runs scheduled maintenance jobs. Specify hourly, daily, weekly, + monthly. Part of the jobs system. + +* :doc:`runprofileserver ` - Starts *runserver* with hotshot/profiling tools enabled. + I haven't had a chance to check this one out, but it looks really cool. + +* `runscript`_ - Runs a script in the django context. + +* `runserver_plus`_ - The standard runserver stuff but with + the Werkzeug debugger baked in. Requires Werkzeug_. This one kicks ass. + +* *set_fake_passwords* - Sets all user passwords to a common value (*password* by default). *DEBUG only*. + +* *show_urls* - Displays the url routes that are defined in your project. Very + crude at this point. + +* :doc:`sqldiff` - Prints the (approximated) difference between an apps models and + what is in the database. This is very nice, but also very experimental at + the moment. It can not catch everything but it's a great sanity check. + +* :doc:`sqlcreate` - Generates the SQL to create your database for you, as specified + in settings.py. + +* `sync_s3`_ - Copies files found in settings.MEDIA_ROOT to S3. + Optionally can also gzip CSS and Javascript files and set the + Content-Encoding header, and also set a far future expires header for browser + caching. + + +.. _`create_app`: create_app.html +.. _`export_emails`: export_emails.html +.. _`graph_models`: graph_models.html +.. _`print_settings`: print_settings.html +.. _`runscript`: runscript.html +.. _`runserver_plus`: runserver_plus.html +.. _`sync_s3`: sync_s3.html +.. _GraphViz: http://www.graphviz.org/ +.. _Werkzeug: http://werkzeug.pocoo.org/ diff --git a/contrib/django_extensions/docs/conf.py b/contrib/django_extensions/docs/conf.py new file mode 100644 index 00000000..11f11f22 --- /dev/null +++ b/contrib/django_extensions/docs/conf.py @@ -0,0 +1,194 @@ +# -*- coding: utf-8 -*- +# +# django-extensions documentation build configuration file, created by +# sphinx-quickstart on Wed Apr 1 20:39:40 2009. +# +# This file is execfile()d with the current directory set to its containing dir. +# +# Note that not all possible configuration values are present in this +# autogenerated file. +# +# All configuration values have a default; values that are commented out +# serve to show the default. + +#import sys, os + +# If extensions (or modules to document with autodoc) are in another directory, +# add these directories to sys.path here. If the directory is relative to the +# documentation root, use os.path.abspath to make it absolute, like shown here. +#sys.path.append(os.path.abspath('.')) + +# -- General configuration ----------------------------------------------------- + +# Add any Sphinx extension module names here, as strings. They can be extensions +# coming with Sphinx (named 'sphinx.ext.*') or your custom ones. +extensions = [] + +# Add any paths that contain templates here, relative to this directory. +templates_path = ['_templates'] + +# The suffix of source filenames. +source_suffix = '.rst' + +# The encoding of source files. +#source_encoding = 'utf-8' + +# The master toctree document. +master_doc = 'index' + +# General information about the project. +project = u'django-extensions' +copyright = u'Copyright (C) 2008, 2009, 2010, 2011, 2012, 2013 Michael Trier, Bas van Oostveen and contributors' + +# The version info for the project you're documenting, acts as replacement for +# |version| and |release|, also used in various other places throughout the +# built documents. +# +# The short X.Y version. +version = '1.2' +# The full version, including alpha/beta/rc tags. +release = '1.2.2' + +# The language for content autogenerated by Sphinx. Refer to documentation +# for a list of supported languages. +#language = None + +# There are two options for replacing |today|: either, you set today to some +# non-false value, then it is used: +#today = '' +# Else, today_fmt is used as the format for a strftime call. +#today_fmt = '%B %d, %Y' + +# List of documents that shouldn't be included in the build. +#unused_docs = [] + +# List of directories, relative to source directory, that shouldn't be searched +# for source files. +exclude_trees = ['_build'] + +# The reST default role (used for this markup: `text`) to use for all documents. +#default_role = None + +# If true, '()' will be appended to :func: etc. cross-reference text. +#add_function_parentheses = True + +# If true, the current module name will be prepended to all description +# unit titles (such as .. function::). +#add_module_names = True + +# If true, sectionauthor and moduleauthor directives will be shown in the +# output. They are ignored by default. +#show_authors = False + +# The name of the Pygments (syntax highlighting) style to use. +pygments_style = 'sphinx' + +# A list of ignored prefixes for module index sorting. +#modindex_common_prefix = [] + + +# -- Options for HTML output --------------------------------------------------- + +# The theme to use for HTML and HTML Help pages. Major themes that come with +# Sphinx are currently 'default' and 'sphinxdoc'. +html_theme = 'default' + +# Theme options are theme-specific and customize the look and feel of a theme +# further. For a list of options available for each theme, see the +# documentation. +#html_theme_options = {} + +# Add any paths that contain custom themes here, relative to this directory. +#html_theme_path = [] + +# The name for this set of Sphinx documents. If None, it defaults to +# " v documentation". +#html_title = None + +# A shorter title for the navigation bar. Default is the same as html_title. +#html_short_title = None + +# The name of an image file (relative to this directory) to place at the top +# of the sidebar. +#html_logo = None + +# The name of an image file (within the static path) to use as favicon of the +# docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 +# pixels large. +#html_favicon = None + +# Add any paths that contain custom static files (such as style sheets) here, +# relative to this directory. They are copied after the builtin static files, +# so a file named "default.css" will overwrite the builtin "default.css". +html_static_path = ['_static'] + +# If not '', a 'Last updated on:' timestamp is inserted at every page bottom, +# using the given strftime format. +#html_last_updated_fmt = '%b %d, %Y' + +# If true, SmartyPants will be used to convert quotes and dashes to +# typographically correct entities. +#html_use_smartypants = True + +# Custom sidebar templates, maps document names to template names. +#html_sidebars = {} + +# Additional templates that should be rendered to pages, maps page names to +# template names. +#html_additional_pages = {} + +# If false, no module index is generated. +#html_use_modindex = True + +# If false, no index is generated. +#html_use_index = True + +# If true, the index is split into individual pages for each letter. +#html_split_index = False + +# If true, links to the reST sources are added to the pages. +#html_show_sourcelink = True + +# If true, an OpenSearch description file will be output, and all pages will +# contain a tag referring to it. The value of this option must be the +# base URL from which the finished HTML is served. +#html_use_opensearch = '' + +# If nonempty, this is the file name suffix for HTML files (e.g. ".xhtml"). +#html_file_suffix = '' + +# Output file base name for HTML help builder. +htmlhelp_basename = 'django-extensionsdoc' + + +# -- Options for LaTeX output -------------------------------------------------- + +# The paper size ('letter' or 'a4'). +#latex_paper_size = 'letter' + +# The font size ('10pt', '11pt' or '12pt'). +#latex_font_size = '10pt' + +# Grouping the document tree into LaTeX files. List of tuples +# (source start file, target name, title, author, documentclass [howto/manual]). +latex_documents = [( + 'index', 'django-extensions.tex', u'django-extensions Documentation', + u'Michael Trier, Bas van Oostveen, and contributors', 'manual' +), ] + +# The name of an image file (relative to this directory) to place at the top of +# the title page. +#latex_logo = None + +# For "manual" documents, if this is true, then toplevel headings are parts, +# not chapters. +#latex_use_parts = False + +# Additional stuff for the LaTeX preamble. +#latex_preamble = '' + +# Documents to append as an appendix to all manuals. +#latex_appendices = [] + +# If false, no module index is generated. +#latex_use_modindex = True diff --git a/contrib/django_extensions/docs/create_app.rst b/contrib/django_extensions/docs/create_app.rst new file mode 100644 index 00000000..83fbff43 --- /dev/null +++ b/contrib/django_extensions/docs/create_app.rst @@ -0,0 +1,41 @@ +create_app +========== + +:synopsis: Creates an application directory structure for the specified application name. + +This command allows you to specify the --template option where you can indicate +a template directory structure to use as your default. + +The --diagram option generates the models.py and admin.py from a .dia file. + + +Example Usage +------------- + +All examples assume your current directory is the project directory and +settings.py is under it. + +:: + + # Get command help + ./manage.py create_app --help + +:: + + # Generate models.py and admin.py from [APP_NAME].dia file. This file should + # be placed in the settings.py directory. + ./manage.py create_app -d APP_NAME + + +Example generated from sample.dia +--------------------------------- + +:: + + ./manage.py create_app --diagram=sample.dia webdata + +-d switch or --diagram option use dia2django_ to generate models.py and is +better documented in `django wiki`_. + +.. _dia2django: https://svn.devnull.li/main/pythonware/dia2django/trunk/doc/ +.. _`django wiki`: http://code.djangoproject.com/wiki/Dia2Django diff --git a/contrib/django_extensions/docs/creating_release.txt b/contrib/django_extensions/docs/creating_release.txt new file mode 100644 index 00000000..18b37c12 --- /dev/null +++ b/contrib/django_extensions/docs/creating_release.txt @@ -0,0 +1,58 @@ +Creating a release +================== + +:synopsis: Creating a django-extensions release + + +How to make a new release +------------------------- + +Get a fresh copy:: + + $ git clone git@github.com:django-extensions/django-extensions.git + $ cd django-extensions + +Run tests:: + + $ python run_tests.py + +Change version numbers in django_extensions/__init__.py and docs/conf.py:: + + $ vi django_extensions/__init__.py (1 occurance) + $ vi docs/conf.py (2 occurances) + $ git commit -a -m v0.4.1 + +Tag it:: + + $ git tag 0.4.1 + +Prepare the release tarball:: + + $ python ./setup.py sdist + +Upload release to pypi:: + + $ python ./setup.py register + $ python ./setup.py sdist upload -s + +Upload new documentation to pypi:: + + $ cd docs + $ make html + $ cd _build/html + $ zip -r9v ../django_extensions_docs.zip * + +Now goto http://pypi.python.org, login and edit the django_extensions package. +At the bottom of the page your can upload documentation. Click browse, select +the zip file and hit "Upload Documentation". + +Bumb version number to new in-development pre version:: + + $ vi django_extensions/__init__.py + $ git commit -a -m 'bumped version number' + +Push changes back to github:: + + $ git push --tags + $ git push + diff --git a/contrib/django_extensions/docs/dumpscript.rst b/contrib/django_extensions/docs/dumpscript.rst new file mode 100644 index 00000000..37e09a41 --- /dev/null +++ b/contrib/django_extensions/docs/dumpscript.rst @@ -0,0 +1,101 @@ +dumpscript +========== + +:synopsis: Generates a standalone Python script that will repopulate the database using objects. + +The `dumpscript` command generates a standalone Python script that will +repopulate the database using objects. The advantage of this approach is that +it is easy to understand, and more flexible than directly populating the +database, or using XML. + +Why? +---- + +There are a few benefits to this: + +* less drama with model evolution: foreign keys handled naturally without IDs, + new and removed columns are ignored +* edit script to create 1,000s of generated entries using for loops, generated + names, python modules etc. + +For example, an edited script can populate the database with test data:: + + for i in xrange(2000): + poll = Poll() + poll.question = "Question #%d" % i + poll.pub_date = date(2001,01,01) + timedelta(days=i) + poll.save() + +Real databases will probably be bigger and more complicated, and so it is useful +to enter some values using the admin interface and then edit the generated +scripts. + + +Features +-------- + +* *ForeignKey* and *ManyToManyFields* (using python variables, not object IDs) +* Self-referencing *ForeignKey* (and M2M) fields +* Sub-classed models +* *ContentType* fields and generic relationships (but see issue 43) +* Recursive references +* *AutoFields* are excluded +* Parent models are only included when no other child model links to it +* Individual models can be referenced + + +What it can't do (yet!) +----------------------- + +* Ideal handling of generic relationships (ie no *AutoField* references): + issue 43 +* Intermediate join tables: issue 48 +* GIS fields: issue 72 + + +How? +---- + +To dump the data from all the models in a given Django app (`appname`):: + + $ ./manage.py dumpscript appname > scripts/testdata.py + +To dump the data from just a single model (`appname.ModelName`):: + + $ ./manage.py dumpscript appname.ModelName > scripts/testdata.py + +To reset a given app, and reload with the saved data:: + + $ ./manage.py reset appname + $ ./manage.py runscript testdata + +Note: Runscript needs *scripts* to be a module, so create the directory and a +*__init__.py* file. + + +Caveats +------- + +Naming conflicts +~~~~~~~~~~~~~~~~ + +Please take care that when naming the output files these filenames do not +clash with other names in your import path. If for example the appname is +the same as the script name then this can cause an importerror. + +Since instead of importing the application modules it tries to load the +modules from the dumpscript file itself. + +Examples:: + + # Wrong + $ ./manage.py dumpscript appname > dumps/appname.py + + # Right + $ ./manage.py dumpscript appname > dumps/appname_all.py + + # Right + $ ./manage.py dumpscript appname.Somemodel > dumps/appname_somemodel.py + + + diff --git a/contrib/django_extensions/docs/export_emails.rst b/contrib/django_extensions/docs/export_emails.rst new file mode 100644 index 00000000..c092ec85 --- /dev/null +++ b/contrib/django_extensions/docs/export_emails.rst @@ -0,0 +1,81 @@ +export_emails +============= + +:synopsis: export the email addresses for your users in one of many formats + +Most Django sites include a registered user base. There are times when you +would like to import these e-mail addresses into other systems (generic mail +program, GMail, google docs invites, give edit permissions, LinkedLn Group +pre-approved listing). The export_emails command extension gives you this +ability. The users exported can be filtered by Group name association. + + +Example Usage +------------- + +:: + + # Export all the addresses in the '"First Last" ;' format. + $ ./manage.py export_emails > addresses.txt + +:: + + # Export users from the group 'Attendees' in the linked in pre-approve Group csv format. + $ ./manage.py export_emails -g Attendees -f linkedin pycon08.csv + +:: + + # Create a csv file importable by GMail or Google Docs + $ ./manage.py export_emails --format=google google.csv + + +Current Supported Formats +------------------------- + +address +^^^^^^^ + +This is the default basic text format. Each entry is on its own line in the +format:: + + "First Last" ; + +This can be used with all known mail programs (that I know about anyway). + + +google +^^^^^^ + +A CSV (comma separated value) format which google applications can import. +This can be used to import directly into GMail, a GMail mailing group, Google +Docs invite (to read), Google Docs grant edit permissions, Google Calendar +invites, etc, etc, etc. + +Only two columns are supplied. One for the persons name and the e-mail address. +This is also nice for importing into spreadsheets. + + +outlook +^^^^^^^ + +A CSV (comma separated value) format which outlook can parse and import. +Supplies all the columns that Outlook 'requires', but only the name and e-mail +address are supplied. + + +linkedin +^^^^^^^^ + +A CSV (comma separated value) format which can be imported by `LinkedIn Groups`_ +to pre-approve a list of people for joining the group. + +This supplies 3 columns: First name, last name, and e-mail address. This is the +best generic csv file for importing into spreadsheets as well. + + +vcard +^^^^^ + +A vCard format which Apple Address Book can parse and import. + +.. _`LinkedIn Groups`: http://www.linkedin.com/static?key=groups_info diff --git a/contrib/django_extensions/docs/field_extensions.rst b/contrib/django_extensions/docs/field_extensions.rst new file mode 100644 index 00000000..054f8e59 --- /dev/null +++ b/contrib/django_extensions/docs/field_extensions.rst @@ -0,0 +1,27 @@ +Field Extensions +================ + +:synopsis: Current Field Extensions + + +Current Database Model Field Extensions +--------------------------------------- + +* *AutoSlugField* - AutoSlugfield will automatically create a unique slug + increasing an appended number on the slug until it is unique. Inspired by + SmileyChris' Unique Slugify snippet. + +* *CreationDateTimeField* - DateTimeField that will automatically set it's date + when the object is first saved to the database. Works in the same way as the + deprecated auto_now_add keyword. + +* *ModificationDateTimeField* - DateTimeField that will automatically set it's + date when an object is saved to the database. Works in the same way as the + deprecated auto_now keyword. + +* *UUIDField* - UUIDField for Django, supports all uuid versions which are + natively supported by the uuid python module. + +* *EncryptedCharField* - CharField which transparently encrypts its value as it goes in and out of the database. Encryption is handled by `Keyczar `_. To use this field you must have Keyczar installed, have generated a primary encryption key, and have ``settings.KEYS_DIR`` set to the full path of your keys directory. + +* *EncryptedTextField* - CharField which transparently encrypts its value as it goes in and out of the database. Encryption is handled by `Keyczar `_. To use this field you must have Keyczar installed, have generated a primary encryption key, and have ``settings.KEYS_DIR`` set to the full path of your keys directory. \ No newline at end of file diff --git a/contrib/django_extensions/docs/graph_models.rst b/contrib/django_extensions/docs/graph_models.rst new file mode 100644 index 00000000..76ca4415 --- /dev/null +++ b/contrib/django_extensions/docs/graph_models.rst @@ -0,0 +1,88 @@ +Graph models +============ + +:synopsis: Renders a graphical overview of your project or specified apps. + +Creates a GraphViz_ dot file for the specified app names based on their models.py. +You can pass multiple app names and they will all be combined into a single model. +Output is usually directed to a dot file. + +There several options available like: grouping models, including inheritance, +excluding models and columns and changing the layout when rendering to an output +image. + +With the latest revisions it's also possible to specify an output file if +pygraphviz_ is installed and render directly to an image or other supported +file-type. + + +Selecting a library +------------------- + +You need to select the library to generate the image, you can do so by passing +the --pygraphviz or --pydot parameters depending on the library you want to use. + +When neither of the command line parameters are given the default is to try and load +pygraphviz or pydot (in that order) to generate the image. + +To install pygraphviz you usually need to run this command: + +:: + $ pip install pygraphviz + +It is possible you can't install it because it needs some C extensions to build, you +can try other methods to install or you can use PyDot. + +To install pydot you need to run this command: + +:: + $ pip install pyparsing==1.5.7 + $ pip install pydot + +Installation should be fast and easy, remember to install this exact version of +pyparsing, otherwise it's possible you get this error: + + Couldn't import dot_parser, loading of dot files will not be possible. + + +Default Settings +---------------- + +The option GRAPH_MODELS = {} can be used in the settings file to specify default options:: + + GRAPH_MODELS = { + 'all_applications': True, + 'group_models': True, + } + +It uses the same names as on the command line only with the leading two dashes removed and +the other dashes replaced by underscores. + + +Example Usage +------------- + +With *django-extensions* installed you can create a dot-file or an +image by using the *graph_models* command. Like used in the following examples:: + + # Create a dot file + $ ./manage.py graph_models -a > my_project.dot + +:: + + # Create a PNG image file called my_project_visualized.png with application grouping + $ ./manage.py graph_models -a -g -o my_project_visualized.png + + # Same example but with explicitly selecting pygraphviz or pydot + $ ./manage.py graph_models --pygraphviz -a -g -o my_project_visualized.png + $ ./manage.py graph_models --pydot -a -g -o my_project_visualized.png + +:: + + # Create a dot file for only the 'foo' and 'bar' applications of your project + $ ./manage.py graph_models foo bar > my_project.dot + + +.. _GraphViz: http://www.graphviz.org/ +.. _pygraphviz: https://networkx.lanl.gov/wiki/pygraphviz +.. _pydot: https://pypi.python.org/pypi/pydot diff --git a/contrib/django_extensions/docs/index.rst b/contrib/django_extensions/docs/index.rst new file mode 100644 index 00000000..6a2aa826 --- /dev/null +++ b/contrib/django_extensions/docs/index.rst @@ -0,0 +1,79 @@ +.. django-extensions documentation master file, created by + sphinx-quickstart on Wed Apr 1 20:39:40 2009. + You can adapt this file completely to your liking, but it should at least + contain the root `toctree` directive. + +Welcome to django-extensions's documentation! +============================================= + +Django Extensions is a collection of custom extensions for the Django Framework. + +These include management commands, additional database field, admin extensions and +much more. + +Getting Started +=============== + +The easiest way to figure out what Django Extensions are all about is to watch the `excellent screencast by Eric Holscher`__. In a couple minutes Eric walks you through a half a dozen command extensions. + +Getting it +========== + +You can get Django Extensions by using pip or easy_install:: + + $ pip install django-extensions + or + $ easy_install django-extensions + +If you want to install it from source, grab the git repository and run setup.py:: + + $ git clone git://github.com/django-extensions/django-extensions.git + $ cd django-extensions + $ python setup.py install + +For more detailed instructions check out our :doc:`installation_instructions`. Enjoy. + +Compatibility with versions of Python and Django +================================================= + +We try to follow the Django guidelines for supported Python and Django versions. + +This might mean the django-extensions may work with older or unsupported versions but we do not garantee it and most likely will not fix bugs related to incompatibilities with older versions. + +At the time of writing we require at least Python 2.5. + +Contents +======== + +.. toctree:: + :maxdepth: 3 + + installation_instructions + command_extensions + command_extension_ideas + admin_extensions + shell_plus + create_app + dumpscript + runscript + export_emails + field_extensions + graph_models + jobs_scheduling + model_extensions + namespace_proposal + print_settings + runprofileserver + runserver_plus + sync_s3 + sqldiff + sqlcreate + validate_templates + + +Indices and tables +================== + +* :ref:`search` + +__ http://ericholscher.com/blog/2008/sep/12/screencast-django-command-extensions/ diff --git a/contrib/django_extensions/docs/installation_instructions.rst b/contrib/django_extensions/docs/installation_instructions.rst new file mode 100644 index 00000000..a8b460c4 --- /dev/null +++ b/contrib/django_extensions/docs/installation_instructions.rst @@ -0,0 +1,100 @@ +Installation instructions +========================= + +:synopsis: Installing django-extensions + + +Download and installation +------------------------- + +Pip and easy_install +^^^^^^^^^^^^^^^^^^^^ + +You can use pip or easy_install to install django-extensions:: + + $ pip install django-extensions + +or:: + + $ easy_install django-extensions + +Download +^^^^^^^^ + +Download the latest packaged version from +http://pypi.python.org/pypi/django-extensions/ and unpack it. Inside is a +script called setup.py. Enter this command:: + + python setup.py install + +...and the package will install automatically. + +Installation +^^^^^^^^^^^^ + +You will need to add the *django_extensions* application to the INSTALLED_APPS +setting of your Django project *settings.py* file.:: + + INSTALLED_APPS = ( + ... + 'django_extensions', + ) + +This will make sure that Django finds the additional management commands +provided by *django-extensions*. + +The next time you invoke *./manage.py help* you should be able to see all the +newly available commands. + +Some command's or option's require additional applications or python libraries, +for example: + + * 'export_emails' will require the *python vobject* module to create vcard + files. + * 'graph_models' requires *pygraphviz* to render directly to image file. + +If the given application or python library is not installed on your system (or +not in the python path) the executed command will raise an exception and inform +you of the missing dependency. + +Version Control +--------------- + +Django-extensions is hosted on github:: + + https://github.com/django-extensions/django-extensions + +Source code can be accessed by performing a Git clone. + +Tracking the development version of *django command extensions* should be +pretty stable and will keep you up-to-date with the latests fixes. + +The following command will check the application's source code out to a +directory called *django-extensions*: + +Git:: + + git clone git://github.com/django-extensions/django-extensions.git + +You should either install the resulting project with *python setup.py install* +or put it the *extensions* directory into your PYTHONPATH. The most common way +is to symlink (junction, if you're on Windows) the extensions directory inside +a directory which is on your PYTHONPATH, such as your Python installation's +site-packages directory. + +:: + + ln -sf /full/path/to/django-extensions/django_extensions /usr/lib/python2.7/site-packages/django_extensions + +You can verify that the application is available on your PYTHONPATH by opening a Python interpreter and entering the following commands: + +:: + + >>> import django_extensions + >>> django_extensions.VERSION + (0, 8) + +Keep in mind that the current code in the git repository may be different from the +packaged release. It may contain bugs and backwards-incompatible changes but most +likely also new goodies to play with. + diff --git a/contrib/django_extensions/docs/jobs_scheduling.rst b/contrib/django_extensions/docs/jobs_scheduling.rst new file mode 100644 index 00000000..b50c5bcf --- /dev/null +++ b/contrib/django_extensions/docs/jobs_scheduling.rst @@ -0,0 +1,44 @@ +Jobs scheduling +=============== + +:synopsis: Documentation on creating/using jobs in Django-extensions + + +JobsScheduling +-------------- + + +This page is very much; Work in Progress +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Creating jobs works much like management commands work in Django. +Use create_jobs to make a 'jobs' directory inside of an application. +After that create one python file per job. + +Some simple examples are provided by the django_extensions.jobs package. + +A job is a python script with a mandatory Job class which extends from +HourlyJob, DailyJob, WeeklyJob or MonthlyJob. It has one method that must be +implemented called 'execute', which is called when the job is ran. + +The following commands are related to jobs: + +* create_jobs, created the directory structure for jobs +* runjob, runs a single job +* runjobs, run all hourly/daily/weekly/monthly jobs + +Use "runjob(s) -l" to list all jobs recognized. + +Jobs do not run automatically ! + +You must either run a job by hand, with which you can specify the exact time on +which the command is ran, or put something like the following lines in your +crontab file. + +@hourly /path/to/my/project/manage.py runjobs hourly + +@daily /path/to/my/project/manage.py runjobs daily + +@weekly /path/to/my/project/manage.py runjobs weekly + +@monthly /path/to/my/project/manage.py runjobs monthly \ No newline at end of file diff --git a/contrib/django_extensions/docs/model_extensions.rst b/contrib/django_extensions/docs/model_extensions.rst new file mode 100644 index 00000000..4e9025dd --- /dev/null +++ b/contrib/django_extensions/docs/model_extensions.rst @@ -0,0 +1,11 @@ +Model extensions +================ + +:synopsis: Current Model Extensions + + +Current Database Model Extensions +--------------------------------- + +* *TimeStampedModel* - TimeStampedModel An abstract base class model that + provides self-managed "created" and "modified" fields. \ No newline at end of file diff --git a/contrib/django_extensions/docs/namespace_proposal.rst b/contrib/django_extensions/docs/namespace_proposal.rst new file mode 100644 index 00000000..2c602f95 --- /dev/null +++ b/contrib/django_extensions/docs/namespace_proposal.rst @@ -0,0 +1,29 @@ +Namespace proposal +================== + +:synopsis: Namespace Proposal + + +Introduction +------------ + +Please change / write your proposal for splitting django_extensions into +namespaces here. + + +Proposal of a Namespace +----------------------- + +Rough proposal for splitting into functional parts: + +* django_extensions.commands (20% that everbody uses / production) +* django_extensions.commands.development (everything development) +* django_extensions.commands.extra (not fitting about category's?) +* django_extensions.db +* django_extensions.templates +* django_extensions.jobs + +The db part should be okey where it is right now. Cause it's only used when +somebody explicitly imports:: + + from django_extensions.db.models import something diff --git a/contrib/django_extensions/docs/print_settings.rst b/contrib/django_extensions/docs/print_settings.rst new file mode 100644 index 00000000..b3ad00af --- /dev/null +++ b/contrib/django_extensions/docs/print_settings.rst @@ -0,0 +1,55 @@ +print_settings +============== + +:synopsis: Django managment command similar to ``diffsettings`` but shows *all* active Django settings. + + +Introduction +------------ + +Django comes with a ``diffsettings`` command that shows how your project's +settings differ from the Django defaults. Sometimes it is useful to just see +*all* the settings that are in effect for your project. This is particularly +true if you have a more complex system for settings than just a single +:file:`settings.py` file. For example, you might have settings files that +import other settings file, such as dev, test, and production settings files +that source a base settings file. + +This command also supports dumping the data in a few different formats. + +More Info +--------------- + +The simplest way to run it is with no arguments:: + + $ python manage.py print_settings + +Some other variations:: + + $ python manage.py print_settings --format=json + $ python manage.py print_settings --format=yaml # Requires PyYAML + +For more info, take a look at the built-in help:: + + $ python manage.py print_settings --help + Usage: manage.py print_settings [options] + + Print the active Django settings. + + Options: + -v VERBOSITY, --verbosity=VERBOSITY + Verbosity level; 0=minimal output, 1=normal output, + 2=verbose output, 3=very verbose output + --settings=SETTINGS The Python path to a settings module, e.g. + "myproject.settings.main". If this isn't provided, the + DJANGO_SETTINGS_MODULE environment variable will be + used. + --pythonpath=PYTHONPATH + A directory to add to the Python path, e.g. + "/home/djangoprojects/myproject". + --traceback Print traceback on exception + --format=FORMAT Specifies output format. + --indent=INDENT Specifies indent level for JSON and YAML + --version show program's version number and exit + -h, --help show this help message and exit + diff --git a/contrib/django_extensions/docs/runprofileserver.rst b/contrib/django_extensions/docs/runprofileserver.rst new file mode 100644 index 00000000..f507d933 --- /dev/null +++ b/contrib/django_extensions/docs/runprofileserver.rst @@ -0,0 +1,84 @@ +RunProfileServer +================ + +*First of all; we recommend that before you start profiling any language or +framework you learn enough about it so that you feel comfortable with digging +into it's internals. Without sufficient knowledge it will not only be (very) +hard but your likely to make wrong assumptions (and fixes). As a rule of thumb, +clean well written code will help you a lot more then overzealous +micro-optimizations will.* + +*This document is work in progress. If you feel you can help with +better/clearer or additional information about profiling Django please leave a +comment.* + + +Introduction +------------ + +*runprofileserver* starts Django's runserver command with hotshot/profiling +tools enabled. It will save .prof files containing the profiling information +into the --prof-path directory. Note that for each request made one profile +data file is saved. + +Per default the profile-data-files are saved in /tmp use the --prof-path option +to specify your own target directory. Saving the data in a meaningful directory +structure helps to keep your profile data organized and /tmp uncluttered. (Yes +this is probably a bug on windows and other systems where /tmp does not exist) + +To define profile filenames use --prof-file option. Default format +is "{path}.{duration:06d}ms.{time}" (Python +`Format Specification `_ +is used). + +Examples: + + * "{time}-{path}-{duration}ms" - to order profile-data-files by request time + * "{duration:06d}ms.{path}.{time}" - to order by request duration + +gather_profile_stats.py +----------------------- + +Django comes packed with a tool to aggregate these different prof files into +one aggregated profile file. This tool is called *gather_profile_stats.py* and +is located inside the *bin* directory of your Django distribution. + + +KCacheGrind +----------- + +Recent versions of *runprofileserver* have an option to save the profile data +into a KCacheGrind compatible format. So you can use the excellent KCacheGrind +tool for analyzing the profile data. + +Example:: + + $ mkdir /tmp/my-profile-data + $ ./manage.py runprofileserver --kcachegrind --prof-path=/tmp/my-profile-data + Validating models... + 0 errors found + + Django version 1.0-post-release-SVN-SVN-unknown, using settings 'complete_project.settings' + Development server is running at http://127.0.0.1:8000/ + Quit the server with CONTROL-C. + [13/Nov/2008 06:29:38] "GET / HTTP/1.1" 200 41107 + [13/Nov/2008 06:29:39] "GET /site_media/base.css?743 HTTP/1.1" 200 17227 + [13/Nov/2008 06:29:39] "GET /site_media/logo.png HTTP/1.1" 200 3474 + [13/Nov/2008 06:29:39] "GET /site_media/jquery.js HTTP/1.1" 200 31033 + [13/Nov/2008 06:29:39] "GET /site_media/heading.png HTTP/1.1" 200 247 + [13/Nov/2008 06:29:39] "GET /site_media/base.js HTTP/1.1" 200 751 + + $ kcachegrind /tmp/my-profile-data/root.12574391.592.prof + +Here is a screenshot of how the above commands might look in KCacheGrind: + + http://trbs.net/media/misc/django-runprofileserver-kcachegrind-full.jpg + +Links +----- + +* http://code.djangoproject.com/wiki/ProfilingDjango +* http://www.rkblog.rk.edu.pl/w/p/django-profiling-hotshot-and-kcachegrind/ +* http://code.djangoproject.com/browser/django/trunk/django/bin/profiling/gather_profile_stats.py +* http://www.oluyede.org/blog/2007/03/07/profiling-django/ +* http://simonwillison.net/2008/May/22/debugging/ diff --git a/contrib/django_extensions/docs/runscript.rst b/contrib/django_extensions/docs/runscript.rst new file mode 100644 index 00000000..b61caf95 --- /dev/null +++ b/contrib/django_extensions/docs/runscript.rst @@ -0,0 +1,65 @@ +RunScript +============= + +:synopsis: Runs a script in the django context. + + +Introduction +------------ + +The runscript command lets you run any arbritrary set of python commands within +the django context. It offers the same usability and functionality as running a +set of command in shell accessed by:: + + $ python manage.py shell + + +Getting Started +--------------- + +To get started create a scripts directory in your project root, next to +manage.py:: + + $ mkdir scripts + $ touch scripts/__init__.py + +Note: The *__init__.py* file is necessary so that the folder is picked up as a +python package. + +Next, create a python file with the name of the script you want to run within +the scripts directory:: + + $ touch scripts/delete_all_polls.py + +This file must implement a *run()* function. This is what gets called when you +run the script. You can import any models or other parts of your django project +to use in these scripts. + +For example:: + + # scripts/delete_all_polls.py + + from Polls.models import Poll + + def run(): + # Get all polls + all_polls = Poll.objects.all() + # Delete polls + all_polls.delete() + +Note: You can put a script inside a *scripts* folder in any of your apps too. + +Using +----- + +To run any script you use the command *runscript* with the name of the script +that you want to run. + +For example:: + + $ python manage.py runscript delete_all_polls + +Note: The command first checks for scripts in your apps i.e. *app_name/scripts* +folder and runs them before checking for and running scripts in the +*project_root/scripts* folder. You can have multiple scripts with the same name +and they will all be run sequentially. diff --git a/contrib/django_extensions/docs/runserver_plus.rst b/contrib/django_extensions/docs/runserver_plus.rst new file mode 100644 index 00000000..acdbdeb2 --- /dev/null +++ b/contrib/django_extensions/docs/runserver_plus.rst @@ -0,0 +1,125 @@ +RunServerPlus +============= + +:synopsis: RunServerPlus-typical runserver with Werkzeug debugger baked in + + +Introduction +------------ + +This item requires that you have the `Werkzeug WSGI utilities` (version 0.3) +installed. Included with Werkzeug is a kick ass debugger that renders nice +debugging tracebacks and adds an AJAX based debugger (which allows to execute +code in the context of the traceback’s frames). Additionally it provides a +nice access view to the source code. + + +Getting Started +--------------- + +To get started we just use the *runserver_plus* command instead of the normal +*runserver* command:: + + $ python manage.py runserver_plus + + * Running on http://127.0.0.1:8000/ + * Restarting with reloader... + + Validating models... + 0 errors found + + Django version 0.97-newforms-admin-SVN-unknown, using settings 'screencasts.settings' + Development server is running at http://127.0.0.1:8000/ + Using the Werkzeug debugger (http://werkzeug.pocoo.org/) + Quit the server with CONTROL-C. + +Note: all normal runserver options apply. In other words, if you need to change +the port number or the host information, you can do so like you would normally. + + +Using +----- + +Whenever we hit an exception in our code, instead of the normal Django +traceback page appearing, we see the Werkzeug traceback page instead. + +.. image:: https://f.cloud.github.com/assets/202559/1261027/2637f826-2c22-11e3-83c6-646acc87808b.png + :alt: werkzeug-traceback + +Along with the typical traceback information we have a couple of options. These +options appear when you hover over a particular traceback line. Notice that +two buttons appear to the right: + +.. image:: https://f.cloud.github.com/assets/202559/1261035/558ad0ee-2c22-11e3-8ddd-6678d84d77e7.png + :alt: werkzeug-options + +The options are: + + +View Source +^^^^^^^^^^^ + +This displays the source below the traceback: + +.. image:: https://f.cloud.github.com/assets/202559/1261036/583c8c42-2c22-11e3-9eb9-5c16b8732512.png + :alt: werkzeug-source + +Being able to view the source file is handy because you are able to get more +context information around where the error occurred. The actual traceback +areas are highlighted so they are easy to spot. + +One awkward piece about this is that the page is not scrolled to the bottom. +At first I thought nothing was happening because of this. + + +Interactive Debugging Console +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +When you click on this button a new pane will open up below the traceback line +you're on. This is the money shot: + +.. image:: https://f.cloud.github.com/assets/202559/1261037/5d12eda6-2c22-11e3-802a-2639ff8813fa.png + :alt: werkzeug-debugger + +An ajax based console appears in the pane and you can begin debugging away. +Notice in the screenshot above I did a `print environ` to see what was in the +environment parameter coming into the function. + +*WARNING*: This should *never* be used in any kind of production environment. +Not even for a quick check into a problem. I cannot emphasize this enough. The +interactive debugger allows you to evaluate python code right against the +server. You've been warned. + +.. _`Werkzeug WSGI utilities`: http://werkzeug.pocoo.org/ + + +SSL +^^^ + +runserver_plus also supports SSL, so that you can easily debug bugs which seem to popup when https is used. To use SSL simply provide a file name for certificates, a key and certificate file will be automatically generated:: + + $ python manage.py runserver_plus --cert cert + Validating models... + 0 errors found + + Django version 1.6.dev20130122125534, using settings 'mysite.settings' + Development server is running at http://127.0.0.1:8000/ + Using the Werkzeug debugger (http://werkzeug.pocoo.org/) + Quit the server with CONTROL-C. + * Running on https://127.0.0.1:8000/ + * Restarting with reloader + Validating models... + 0 errors found + + Django version 1.6.dev20130122125534, using settings 'mysite.settings' + Development server is running at http://127.0.0.1:8000/ + Using the Werkzeug debugger (http://werkzeug.pocoo.org/) + Quit the server with CONTROL-C. + +After you run above command, you can access your web application through https://127.0.0.1:8000. You will also find that two files are created in the current working directory, which are key file and a certificate file. If you run the above command again, these certificate files will be reused so that you do not have to keep accepting the self generated certificates from your browser every time. You can also provide absolute file for the certificate to be used if you already have one:: + + $ python manage.py runserver_plus --cert /tmp/cert + +Note that you need OpenSSL library to use SSL, and Werkzeug 0.9 or later if you would like to reuse existing certificates. To install OpenSSL:: + + $ pip install pyOpenSSL diff --git a/contrib/django_extensions/docs/shell_plus.rst b/contrib/django_extensions/docs/shell_plus.rst new file mode 100644 index 00000000..5f3d64f1 --- /dev/null +++ b/contrib/django_extensions/docs/shell_plus.rst @@ -0,0 +1,127 @@ +shell_plus +========== + +:synopsis: Django shell with autoloading of the apps database models + + +Interactive Python Shells +------------------------- + +There is support for three different types of interactive python shells. + +IPython:: + + $ ./manage.py shell_plus --use-ipython + + +BPython:: + + $ ./manage.py shell_plus --use-bpython + + +Python:: + + $ ./manage.py shell_plus --use-plain + + +The default resolution order is: bpython, ipython, python. + +You can also set the configuration option SHELL_PLUS to explicitly specify which version you want. + +:: + + # Always use IPython for shell_plus + SHELL_PLUS = "ipython" + + + +Configuration +------------- + +Sometimes, models from your own apps and other peoples apps have colliding names, +or you may want to completly skip loading an apps models. Here are some examples of how to do that. + +Note: This settings are just used inside shell_plus and will not affect your envirnoment. + +:: + + # Rename the automatic loaded module Messages in the app blog to blog_messages. + SHELL_PLUS_MODEL_ALIASES = {'blog': {'Messages': 'blog_messages'},} + } + +:: + + # Dont load the 'sites' app, and skip the model 'pictures' in the app 'blog' + SHELL_PLUS_DONT_LOAD = ['sites', 'blog.pictures'] + } + + +You can also combine model_aliases and dont_load. + +It is also possible to ignore autoloaded modules when using manage.py, like + + $ ./manage.py shell_plus --dont-load app1 --dont-load app2.module1 + +And, commandline parameters and settings in the configuration file is merged, so you can +safely append modules to ignore from the commandline for one time usage. + +It is possible to use `IPython Notebook`_, an interactive Python shell which +uses a web browser as its user interface, as an alternative shell:: + + $ ./manage.py shell_plus --notebook + +The Django settings module and database models are auto-loaded into the +interactive shell's global namespace also for IPython Notebook. + +Auto-loading is done by a custom IPython extension which is activated by +default by passing the +``--ext django_extensions.management.notebook_extension`` +argument to the Notebook. If you need to pass custom options to the IPython +Notebook, you can override the default options in your Django settings using +the ``IPYTHON_ARGUMENTS`` setting. For example:: + + IPYTHON_ARGUMENTS = [ + '--ext', 'django_extensions.management.notebook_extension', + '--ext', 'myproject.notebook_extension', + '--debug', + ] + +To activate auto-loading, remember to either include django-extensions' default +notebook extension or copy the auto-loading code from it into your own +extension. + +Note that the IPython Notebook feature doesn't currently honor the +``--dont-load`` option. + +.. _`IPython Notebook`: http://ipython.org/ipython-doc/dev/interactive/htmlnotebook.html + + + +Additional Imports +------------------ + +In addition to importing the models you can also specify other items to import by default. +These are specified in SHELL_PLUS_PRE_IMPORTS and SHELL_PLUS_POST_IMPORTS. The former is imported +before any other imports (such as the default models import) and the latter is imported after any +other imports. Both have similar syntax. So in you settings.py file: + +:: + + SHELL_PLUS_PRE_IMPORTS = ( + ('module.submodule1', ('class1', 'function2')), + ('module.submodule2', 'function3'), + ('module.submodule3', '*'), + 'module.submodule4' + ) + +The above example would directly translate to the following python code which would be executed before +the automatic imports: + +:: + + from module.submodule1 import class1, function2 + from module.submodule2 import function3 + from module.submodule3 import * + import module.submodule4 + +These symbols will be available as soon as the shell starts. diff --git a/contrib/django_extensions/docs/sqlcreate.rst b/contrib/django_extensions/docs/sqlcreate.rst new file mode 100644 index 00000000..13b03132 --- /dev/null +++ b/contrib/django_extensions/docs/sqlcreate.rst @@ -0,0 +1,45 @@ +sqlcreate +========== + +:synopsis: Helps you setup your database(s) more easily + + +Introduction +------------- + +Stop creating databases by hand. Your settings.py file already contains the correct +information, so DRY. + +Usage +------------- + + $ python manage.py sqlcreate [--router=] | + +It will spit out SQL which you can review (if you want) but ultimately you want to +pipe it into the database shell command of your choice. + +If there was a good way to ensure that the user in the database settings had the +proper permissions, we could submit the commands straight to the database. +But due to the nature of this portion of the project setup, that will never happen. + +Example +------------- + +PostgreSQL +~~~~~~~~~~ + $ ./manage.py sqlcreate [--router=] | psql -U -W + + +MySQL +~~~~~ + $ ./manage.py sqlcreate [--router=] | mysql -u -p + + +Known Issues +------------ + + * CREATE DATABASE is not SQL standard so might not work everywhere. + * When using fallback user is not created and password is not set. + But it does try to do a GRANT to the database user. + * Missing options for tablespaces, etc. + diff --git a/contrib/django_extensions/docs/sqldiff.rst b/contrib/django_extensions/docs/sqldiff.rst new file mode 100644 index 00000000..f8a34dc4 --- /dev/null +++ b/contrib/django_extensions/docs/sqldiff.rst @@ -0,0 +1,39 @@ +sqldiff +======= + +:synopsis: Prints the ALTER TABLE statemens for the given appnames. + +Django command that scans all models for the given appnames and compares +their database schema with the real database tables. + +It indicates how columns in the database are different from the SQL that would +be generated by Django. This command is not a database migration tool. Though +it might certainly be of help during migrations. It's purpose is to show the +current differences as a way to checking or debugging your models compared to +the real database tables and columns. + +Supported Databases +------------------- + +Currently the following databases are supported: + +* PostgreSQL +* Sqlite3 +* MySQL +* Oracle + +Patches to support other databases are welcome ! :-) + + +Example Usage +------------- + +:: + + # View SQL differences for all installed application + $ ./manage.py sqldiff -a + +:: + + # View SQL differences for all installed application using text instead of SQL + $ ./manage.py sqldiff -a -t diff --git a/contrib/django_extensions/docs/sync_s3.rst b/contrib/django_extensions/docs/sync_s3.rst new file mode 100644 index 00000000..ced4c0cf --- /dev/null +++ b/contrib/django_extensions/docs/sync_s3.rst @@ -0,0 +1,50 @@ +sync_s3 +======= + +:synopsis: sync your MEDIA_ROOT and STATIC_ROOT folders to S3 + +Django command that scans all files in your settings.MEDIA_ROOT and +settings.STATIC_ROOT folders, then uploads them to S3 with the same +directory structure. + +This command can optionally do the following but it is off by default: + + * gzip compress any CSS and Javascript files it finds and adds the + appropriate 'Content-Encoding' header. + * set a far future 'Expires' header for optimal caching. + * uploads only media or static files. + + +Example Usage +------------- + +:: + + # Upload files to S3 into the bucket 'mybucket' + $ ./manage.py sync_s3 mybucket + +:: + + # Upload files to S3 into the bucket 'mybucket' and enable gzipping CSS/JS files and setting of a far future expires header + $ ./manage.py sync_s3 mybucket --gzip --expires + +:: + + # Upload only media files to S3 into the bucket 'mybucket' + $ ./manage.py sync_s3 mybucket --media-only # or --static-only + + +Required libraries and settings +------------------------------- + +This management command requires the boto library and was tested with version +1.4c: + + http://code.google.com/p/boto/ + +It also requires an account with Amazon Web Services (AWS) and the AWS S3 keys. +The keys are added to your settings.py file, for example:: + + # settings.py + AWS_ACCESS_KEY_ID = '' + AWS_SECRET_ACCESS_KEY = '' diff --git a/contrib/django_extensions/docs/validate_templates.rst b/contrib/django_extensions/docs/validate_templates.rst new file mode 100644 index 00000000..8d5a2ae3 --- /dev/null +++ b/contrib/django_extensions/docs/validate_templates.rst @@ -0,0 +1,35 @@ +validate_templates +================== + +:synopsis: Checks templates on syntax or compile errors. + +Options +------- + +verbosity +~~~~~~~~~ +A higher verbosity level will print out all the files that are processed not just the onces that contain errors. + +break +~~~~~ +Do not continue scanning other templates after the first failure. + +includes +~~~~~~~~ +Use -i (can be used multiple times) to add directories to the TEMPLATE_DIRS. + +Settings +-------- + +VALIDATE_TEMPLATES_EXTRA_TEMPLATE_DIRS +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +You can use `VALIDATE_TEMPLATES_EXTRA_TEMPLATE_DIRS` to include a number of template dirs per default directly from the settings file. +This can be usefull for situations where TEMPLATE_DIRS is dynamically generated or switched in middleware or you have other template +dirs for external applications like celery you want to check as well. + +Usage Example +------------- + + ./manage.py validate_templates + diff --git a/contrib/django_extensions/run_tests.py b/contrib/django_extensions/run_tests.py new file mode 100755 index 00000000..ab6c286d --- /dev/null +++ b/contrib/django_extensions/run_tests.py @@ -0,0 +1,67 @@ +#!/usr/bin/env python + +import sys +import shutil +import tempfile +from django.conf import settings + + +def main(): + # Dynamically configure the Django settings with the minimum necessary to + # get Django running tests. + KEY_LOCS = {} + try: + try: + # If KeyCzar is available, set up the environment. + from keyczar import keyczart, keyinfo + + # Create an RSA private key. + keys_dir = tempfile.mkdtemp("django_extensions_tests_keyzcar_rsa_dir") + keyczart.Create(keys_dir, "test", keyinfo.DECRYPT_AND_ENCRYPT, asymmetric=True) + keyczart.AddKey(keys_dir, "PRIMARY", size=4096) + KEY_LOCS['DECRYPT_AND_ENCRYPT'] = keys_dir + + # Create an RSA public key. + pub_dir = tempfile.mkdtemp("django_extensions_tests_keyzcar_pub_dir") + keyczart.PubKey(keys_dir, pub_dir) + KEY_LOCS['ENCRYPT'] = pub_dir + except ImportError: + pass + + settings.configure( + INSTALLED_APPS=[ + 'django.contrib.auth', + 'django.contrib.contenttypes', + 'django.contrib.admin', + 'django.contrib.sessions', + 'django_extensions', + 'django_extensions.tests', + ], + # Django replaces this, but it still wants it. *shrugs* + DATABASE_ENGINE='django.db.backends.sqlite3', + DATABASES={ + 'default': { + 'ENGINE': 'django.db.backends.sqlite3', + } + }, + MEDIA_ROOT='/tmp/django_extensions_test_media/', + MEDIA_PATH='/media/', + ROOT_URLCONF='django_extensions.tests.urls', + DEBUG=True, + TEMPLATE_DEBUG=True, + ENCRYPTED_FIELD_KEYS_DIR=KEY_LOCS, + ) + + from django.test.utils import get_runner + test_runner = get_runner(settings)(verbosity=2, interactive=True) + failures = test_runner.run_tests(['django_extensions']) + sys.exit(failures) + + finally: + for name, path in KEY_LOCS.items(): + # cleanup crypto key temp dirs + shutil.rmtree(path) + + +if __name__ == '__main__': + main() diff --git a/contrib/django_extensions/setup.py b/contrib/django_extensions/setup.py new file mode 100644 index 00000000..15909b82 --- /dev/null +++ b/contrib/django_extensions/setup.py @@ -0,0 +1,105 @@ +""" +Based entirely on Django's own ``setup.py``. +""" +import os +import sys +from distutils.command.install_data import install_data +from distutils.command.install import INSTALL_SCHEMES +try: + from setuptools import setup +except ImportError: + from distutils.core import setup # NOQA + + +class osx_install_data(install_data): + # On MacOS, the platform-specific lib dir is at: + # /System/Library/Framework/Python/.../ + # which is wrong. Python 2.5 supplied with MacOS 10.5 has an Apple-specific + # fix for this in distutils.command.install_data#306. It fixes install_lib + # but not install_data, which is why we roll our own install_data class. + + def finalize_options(self): + # By the time finalize_options is called, install.install_lib is set to + # the fixed directory, so we set the installdir to install_lib. The + # install_data class uses ('install_data', 'install_dir') instead. + self.set_undefined_options('install', ('install_lib', 'install_dir')) + install_data.finalize_options(self) + +if sys.platform == "darwin": + cmdclasses = {'install_data': osx_install_data} +else: + cmdclasses = {'install_data': install_data} + + +def fullsplit(path, result=None): + """ + Split a pathname into components (the opposite of os.path.join) in a + platform-neutral way. + """ + if result is None: + result = [] + head, tail = os.path.split(path) + if head == '': + return [tail] + result + if head == path: + return result + return fullsplit(head, [tail] + result) + +# Tell distutils to put the data_files in platform-specific installation +# locations. See here for an explanation: +# http://groups.google.com/group/comp.lang.python/browse_thread/thread/35ec7b2fed36eaec/2105ee4d9e8042cb +for scheme in INSTALL_SCHEMES.values(): + scheme['data'] = scheme['purelib'] + +# Compile the list of packages available, because distutils doesn't have +# an easy way to do this. +packages, data_files = [], [] +root_dir = os.path.dirname(__file__) +if root_dir != '': + os.chdir(root_dir) +extensions_dir = 'django_extensions' + +for dirpath, dirnames, filenames in os.walk(extensions_dir): + # Ignore dirnames that start with '.' + if os.path.basename(dirpath).startswith("."): + continue + if '__init__.py' in filenames: + packages.append('.'.join(fullsplit(dirpath))) + elif filenames: + data_files.append([dirpath, [os.path.join(dirpath, f) for f in filenames]]) + +version = __import__('django_extensions').__version__ + +setup( + name='django-extensions', + version=version, + description="Extensions for Django", + long_description="""django-extensions bundles several useful +additions for Django projects. See the project page for more information: + http://github.com/django-extensions/django-extensions""", + author='Michael Trier', + author_email='mtrier@gmail.com', + maintainer='Bas van Oostveen', + maintainer_email='v.oostveen@gmail.com', + url='http://github.com/django-extensions/django-extensions', + license='New BSD License', + platforms=['any'], + packages=packages, + cmdclass=cmdclasses, + data_files=data_files, + install_requires=['six'], + tests_require=['Django'], + test_suite='run_tests.main', + classifiers=[ + 'Development Status :: 4 - Beta', + 'Development Status :: 5 - Production/Stable', + 'Environment :: Web Environment', + 'Framework :: Django', + 'Intended Audience :: Developers', + 'License :: OSI Approved :: BSD License', + 'Operating System :: OS Independent', + 'Programming Language :: Python', + 'Programming Language :: Python :: 3', + 'Topic :: Utilities', + ], +) diff --git a/contrib/django_extensions/tox.ini b/contrib/django_extensions/tox.ini new file mode 100644 index 00000000..a1378094 --- /dev/null +++ b/contrib/django_extensions/tox.ini @@ -0,0 +1,39 @@ +# Tox (http://tox.testrun.org/) is a tool for running tests +# in multiple virtualenvs. This configuration file will run the +# test suite on all supported python versions. To use it, "pip install tox" +# and then run "tox" from this directory. + +[tox] +envlist = py26_django13, py26_django14, py27_django13, py27_django14, + py26_django15, py27_django15, py33_django15 + +[testenv] +commands = {envpython} setup.py test + +[testenv:py26_django13] +basepython = python2.6 +deps = Django==1.3.7 + +[testenv:py26_django14] +basepython = python2.6 +deps = Django==1.4.5 + +[testenv:py27_django13] +basepython = python2.7 +deps = Django==1.3.7 + +[testenv:py27_django14] +basepython = python2.7 +deps = Django==1.4.5 + +[testenv:py26_django15] +basepython = python2.6 +deps = https://www.djangoproject.com/download/1.5c2/tarball/ + +[testenv:py27_django15] +basepython = python2.7 +deps = https://www.djangoproject.com/download/1.5c2/tarball/ + +[testenv:py33_django15] +basepython = python3.3 +deps = https://www.djangoproject.com/download/1.5c2/tarball/ diff --git a/requirements.txt b/requirements.txt index 97466978..0f0eb621 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,4 +1,5 @@ --e git+https://github.com/django/django.git@stable/1.4.x#egg=django +#-e git+https://github.com/django/django.git@stable/1.4.x#egg=django +Django<1.5 -e bzr+http://code.0x2620.org/python-ox/#egg=python-ox -e bzr+http://code.0x2620.org/oxtimelines/#egg=oxtimelines simplejson @@ -6,10 +7,9 @@ chardet celery==3.0.24 django-celery==3.0.23 #django_extensions -#-e git+https://github.com/django-extensions/django-extensions.git#egg=django_extensions #use fork fixing some postgresql issues --e git://github.com/bit/django-extensions.git#egg=django_extensions --e git+git://github.com/dcramer/django-devserver#egg=django_devserver +-e contrib/django_extensions +django_devserver gunicorn>=0.14.3,<0.19 html5lib South