pax_global_header00006660000000000000000000000064131271746570014527gustar00rootroot0000000000000052 comment=b66dad0b4a3525a0f95977d198df87230907a154 django-extensions-1.8.1/000077500000000000000000000000001312717465700151755ustar00rootroot00000000000000django-extensions-1.8.1/.editorconfig000066400000000000000000000004311312717465700176500ustar00rootroot00000000000000# http://editorconfig.org # Source: pydanny cookiecutter-django repo root = true [*] charset = utf-8 end_of_line = lf insert_final_newline = true trim_trailing_whitespace = true [*.{py,rst,ini}] indent_style = space indent_size = 4 [*.yml] indent_style = space indent_size = 2 django-extensions-1.8.1/.gitignore000066400000000000000000000002351312717465700171650ustar00rootroot00000000000000*.py[cod] *.egg-info MANIFEST build dist docs/_build docs/_static venv* .tox *.bak .DS_Store .eggs/ .idea/ .coverage .cache/ django-sample-app*/ *.swp *.swo django-extensions-1.8.1/.pre-commit-config.yaml000066400000000000000000000016361312717465700214640ustar00rootroot00000000000000- repo: git://github.com/pre-commit/pre-commit-hooks sha: v0.7.0 hooks: - id: trailing-whitespace - id: check-added-large-files args: - --maxkb=128 - id: check-ast - id: check-case-conflict - id: check-docstring-first - id: check-json - id: check-merge-conflict - id: check-xml - id: check-yaml - id: detect-private-key - id: end-of-file-fixer - id: fix-encoding-pragma - id: flake8 - id: name-tests-test args: - --django exclude: ^tests/testapp|^tests/management/ - repo: git://github.com/Lucas-C/pre-commit-hooks.git sha: c25201a00e6b0514370501050cf2a8538ac12270 hooks: - id: forbid-crlf - repo: git://github.com/trbs/pre-commit-hooks-trbs.git sha: e233916fb2b4b9019b4a3cc0497994c7926fe36b hooks: - id: forbid-executables exclude: manage.py|setup.py django-extensions-1.8.1/.travis.yml000066400000000000000000000033441312717465700173120ustar00rootroot00000000000000language: python sudo: false matrix: fast_finish: true include: - python: 2.7 env: TOXENV=py27-flake8 - python: 3.4 env: TOXENV=py34-flake8 - python: 3.5 env: TOXENV=py35-flake8 - python: 3.6 env: TOXENV=py36-flake8 - python: 3.6 env: TOXENV=precommit - python: 3.6 env: TOXENV=safety - python: 3.6 env: TOXENV=compile-catalog - python: 2.7 env: TOXENV=py27-dj18 - python: 2.7 env: TOXENV=py27-dj110 - python: 2.7 env: TOXENV=py27-dj111 - python: 3.3 env: TOXENV=py33-dj18 - python: 3.4 env: TOXENV=py34-dj18 - python: 3.4 env: TOXENV=py34-dj110 - python: 3.4 env: TOXENV=py34-dj111 - python: 3.5 env: TOXENV=py35-dj18 - python: 3.5 env: TOXENV=py35-dj110 - python: 3.5 env: TOXENV=py35-dj111 - python: 3.5 env: TOXENV=py35-djmaster - python: 3.6 env: TOXENV=py36-dj18 - python: 3.6 env: TOXENV=py36-dj110 - python: 3.6 env: TOXENV=py36-dj111 - python: 3.6 env: TOXENV=py36-djmaster - python: pypy env: TOXENV=pypy-dj18 - python: pypy env: TOXENV=pypy-dj110 - python: pypy env: TOXENV=pypy-dj111 - python: pypy3.5-5.8.0 env: TOXENV=pypy3-dj18 - python: pypy3.5-5.8.0 env: TOXENV=pypy3-dj110 - python: pypy3.5-5.8.0 env: TOXENV=pypy3-djmaster allow_failures: - python: 3.5 env: TOXENV=py35-djmaster - python: 3.6 env: TOXENV=py36-djmaster - python: pypy3.5-5.8.0 env: TOXENV=pypy3-dj110 - python: pypy3.5-5.8.0 env: TOXENV=pypy3-djmaster install: - pip install virtualenv tox coveralls script: - tox after_success: - coveralls django-extensions-1.8.1/.tx/000077500000000000000000000000001312717465700157065ustar00rootroot00000000000000django-extensions-1.8.1/.tx/config000066400000000000000000000003321312717465700170740ustar00rootroot00000000000000[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.com django-extensions-1.8.1/CHANGELOG.md000066400000000000000000000614641312717465700170210ustar00rootroot00000000000000Changelog ========= 1.8.1 ----- Changes: - Build: use tox's 'TOXENV' environment variable - Fix: resetdb, fix problem that 'utf8_support' option is ignored - Improvement: export_emails, moved custom csv UnicodeWriter (for py2) into compat.py - Translations: pt, removed since it was causing issues with the builds if anybody wants to update and fix it that would be much appreciated ! 1.8.0 ----- UUIDField has been removed after being deprecated. Deprecation schedule for JSONField has been removed after requests from the community. Changes: - Fix: runserver_plus, fixed Python 3 print syntax - Fix: sqldiff, Use 'display_size', not 'precision' to identify MySQL bool field - Fix: export_emails, fix and refactor the command and all its output options - Improvement: tests, added Python 3.6 and PyPy3.5-5.8.0 - Improvement: clear_cache, add --cache option to support multiple caches - Improvement: runserver_plus, limit printing SQL queries to avoid flooding the terminal - Improvement: shell_plus, limit printing SQL queries to avoid flooding the terminal - Docs: graph_models, document including/excluding specific models - Docs: shell_plus, added PTPython 1.7.9 ----- Changes: - Fix: AutoSlugField, foreignkey relationships - Fix: shell_plus, supported backends 'postgresql' for set_application_name - Improvement: various commands, Add syntax highlighting when printing SQL - Improvement: pipchecker, treat rc versions as unstable - Improvement: shell_plus, allow to subclass and overwrite import_objects - Improvement: shell_plus, fix SHELL_PLUS_PRE_IMPORTS example - Improvement: setup.py, fix and unify running tests - Improvement: runserver_plus, add RUNSERVERPLUS_POLLER_RELOADER_TYPE setting - Improvement: generate_secret_key, use algoritme from django - Docs: fix grammer and spelling mistakes 1.7.8 ----- Changes: - Improvement: django 1.11, add testing for Django 1.11 - Improvement: pipchecker, make it possible to parse https github urls - Improvement: unreferenced_files, make command much faster by using set() - Docs: add undocumented commands - Docs: shell_plus, additional documentation for referencing nested modules - Fix: sync_s3, fix exclusion of directories - Fix: runprofileserver, fix ip:port specification - Fix: runprofileserver, support --nothreading 1.7.7 ----- Changes: - Improvement: admin_generator, use decorator style for registring ModelAdmins. - Improvement: sqldiff, quote tablename for PRAGMA in sqlite - Fix: graph_models, Fix `attributes` referenced before assignment - Fix: pipchecker, Fix AttributeError caused by missing method 1.7.6 ----- Changes: - Improvement: sqldiff, ignore proxy models in diff (with cli option to include them if wanted) 1.7.5 ----- Changes: - New: ForeignKeyAutocompleteAdmin, Add autocomplete for inline model admins - Improvement: graph_models, Rewrite modelviz module from method to class based processor - Improvement: db fields, make MAX_UNIQUE_QUERY_ATTEMPTS configurable per field and via settings - Improvement: runserver_plus, Added nopin option to disable pin - Fix: graph_models, Support PyDot 1.2.0 and higher - Fix: shell_plus, Fix that aliases from SHELL_PLUS_MODEL_ALIASES were not applied - Removed: validate_templatetags, remove support for pre django-1.5 style {% url %} tags - Cleanup: removing support for end-of-life Python 3.2 - Docs: simplify installation instructions - Docs: fix example for NOTEBOOK_ARGUMENTS - Docs: Remove extraneous '}' characters from shell_plus docs 1.7.4 ----- Changes: - Improvement: show_urls, support --no-color option - Fix: notes, Fix reading templates setting after django 1.8 - Fix: create_app, Fixed typo in deprecation warning - Fix: shell_plus, Use new location for url reverse import - Docs: some commands where missing from the docs - Docs: runscript, added documentation for --traceback 1.7.3 ----- Changes: - Fix: ForeignKeySearchInput, fix bug with constructing search_path urls - Docs: runscript, fix runscript example - Deprecation: JSONField, Django now includes JSONField our field is now deprecated 1.7.2 ----- Changes: - Fix: passwd, Update passwd command up to Django>=1.8 - Improvement: shell_plus, add settings.SHELL_PLUS_PRINT_SQL config option - Improvement: shell_plus, allow to specifies the connection file to use if using the --kernel option 1.7.1 ----- Changes: - Fix: sqldiff, fix optional app_label arguments - Fix: runscript, remove args from command class - Doc: runscript, remove = from --script-args example 1.7.0 ----- The "Letting go of the past" release. From this moment on Django Extensions requires version 1.8 or higher. A lot of work has been done to remove old backwards compatibility code and make sure that Django Extensions uses the current Django API's. This should result in better and easier to maintain code (and hopefully less bugs :). This release touches a lot of code if you have any issues please report them at https://github.com/django-extensions/django-extensions/issues We still need more tests to make sure we don't break people's projects when refactoring. If you have some spare time please contribute tests ! Changes: - Cleanup: removing backwards compatibility hacks for (now) unsupported versions of Django - Cleanup: use six instead of home grown functions - Fix: AutoSlugField, allow_duplicates didn't set slug value to model instance - Fix: MongoDB fields, verbose_name on mongoengine fields does not seem to be supported - Fix: MongoDB fields, fix relative import problem with json.py - Improvement: Start using pre-commit - Improvement: syncdata, Replace custom transaction logic with transaction.atomic - Improvement: Django 1.10, use from_db_value instead of models.SubfieldBase - Improvement: print_user_session, support for non standard user model - Improvement: widont, tests to work with py2 and py3 - Improvement: runserver_plus, prevent 2nd reload of debugger on runserver_plus - Improvement: runserver_plus, prevent killing the server when request.META values are evaluated - Improvement: reset_db, add argument to make closing sessions optional - Improvement: print_settings, Fix positional arguments - Improvement: runscript, migrate to argparse and add_arguments - Improvement: graph_models, do not rely on .models_module for inclusion in output - Improvement: jsonfield, fix issues with mutable default - Docs: Convert readthedocs links for their .org -> .io migration 1.6.7 ----- Changes: - Fix: describe_form, fix No module named 'django.db.models.loading' error - Improvement: shell_plus, Add a setting to prefix all models in an application #887 - Improvement: pipchecker, check for requirements-{dev,prod}.txt as well - Docs: pipchecker, update documentation 1.6.6 ----- Changes: - Fix: admin_generator, fix for using all apps in Django <1.7 - Fix: dump_script, fix for using all apps in Django <1.7 - Fix: UniqueFieldMixin, resolve get_fields_with_model deprecation Django 1.10 - Fix: runprofileserver, Fix call grind format to enable source code navigation in qcachegrind. - Docs: runserver_plus, add a little note about the debugger PIN. 1.6.5 ----- Bumped version number since PyPi returns 500 errors while uploading packages :( 1.6.4 ----- Changes: - Fix: jobs cache_cleanup, use `caches` instead of deprecated `get_cache` - Fix: ModificationDateTimeField, missing default value for `update_modified` - Fix: modelviz, use get_model_compat and look up missing app_label - Fix: modelviz, use get_models_for_app instead of get_models_compat - Fix: dumpscript, use `list_app_labels` instead of `get_apps` when no app_labels are given - Improvement: compat.py, move code from try to else block for Django 1.7+ - Docstring: get_models_for_app, clearify argument 1.6.3 ----- Bumped version number for incomplete PyPi uplaod 1.6.2 ----- The long over due release :-) Changes: - Fix: JsonFields, do not parse floats as decimals. This fixes bugs that causes them to be returned as strings after multiple saves. Note that this can be backwards incompatible ! - Fix: use add_arguments() instead of option_list (Django 1.10) - Fix: create_command, Django 1.9 fixes - Fix: create_jobs, Django 1.9 fixes - Fix: RandomCharField, when not unique get the first value from the generator - Fix: graph_models, render() must be called with a dict - Fix: graph_models, use force_bytes fixes command for Python 3 - Fix: graph_models, fix django 1.6 compatibility for strings defined relation - Fix: graph_models, fix settings.GRAPH_MODELS breaking the command - Fix: graph_models, add support for lazy relationships - Fix: ForeignKeyAutocompleteAdmin, url_patterns is just a list (Django 1.9+) - Fix: ForeignKeySearchInput, use url reversing instead of hardcoded paths - Fix: find_template, Fix for Django 1.8+ - Fix: admin_generator, incompatible "default" identifier raising TypeError - Improvement: show_urls, add json and pretty-json formatting - Improvement: runserver_plus, add support for whitenoise - Improvement: ModificationDateTimeField, add parameter to preserve timestamps on save - Improvement: runprofileserver, raise command error when hotspot is not available - Improvement: reset_db, better parsing of mysql cnf file - Improvement: restored coverage for Python 3.2 - Improvement: pep8 fixes, remove unused shims & imports & commented code - Improvement: graph_models, JSON output - Improvement: graph_models, add wildcard filters - Docs: removed text on donations, the hope was that we could generate some funds to have more consistent development and outreach. - Docs: runserver_plus, added some documentation about LOGGING - Docs: runscript, update documentation to match Django tutorial for Django 1.8+ - Docs: runprofileserver, add documentation on profiler choices - Docs: update_permissions, add basic documentation for command 1.6.1 ----- Changes: - Revert: JSONField, revert Django 1.9 fix as it breaks the field (ticket #781) 1.6.0 ----- Changes: - Fix: Django 1.9 compatibility - New: runserver_plus, add --startup-messages to control when to show them - New: added support for Python 3.5 - Improvement: show_template_tags, renamed from show_templatetags for consistancy - Removed: jquery library (after dropping support for Django 1.5) 1.5.9 ----- Changes: - Fix: wheel still had the old migrations directory in the package 1.5.8 ----- Changes: - Fix: migrations, fix BadMigrationError with Django 1.8+ - Fix: reset_db, Django 1.8+ compatibility fix - Fix: runserver_plus, fix signature of null_technical_500_response for Django 1.8+ - Fix: graph_models, use force_bytes instead of .decode('utf8') - Improvement: print_settings, add format option to only print values - Improvement: print_esttings, add format option for simple key = value text output - Improvement: email_export, documentation updates - Improvement: shell_plus, auto load conditional db expressions Case and When 1.5.7 ----- Changes: - Fix: CreationDateTimeField, migration error - Fix: ModificationDateTimeField, migration error - Fix: shell_plus, options is not always in db config dictionary - Fix: admin filters, contrib.admin.util fallback code - Fix: graph_models, currectly support parsing lists for cli options - Improvement: sqldsn, support postfix - Improvement: utils, remove get_project_root function 1.5.6 ----- Changes: - New: RandomCharField, prepopulates random character string - New: (Not)NullFieldListFilter, filters for admin - New: runserver_plus, integrate with django-pdb - New: runserver_plus, add check_migrations from Django - Improvement: show_urls, nested namespace support - Improvement: show_urls, allow to specify alternative urlconf - Improvement: show_urls, support i18n_patterns - Improvement: show_urls, use --language to filter on a particular language - Improvement: admin_generator, added docstrings to module - Improvement: shell_plus, allow cli arguments to be passed to ipython - Improvement: shell_plus, fixed PYTHONPATH bug when using django-admin shell_plus --notebook - Improvement: shell_plus, set application_name on PostgreSQL databases - Improvement: shell_plus, load user pypython config file - Improvement: CreationDateTimeField, use auto_now_add instead of default ModificationDateTimeField - Improvement: ModificationDateTimeField, use auto_now instead of pre_save method - Improvement: ForeignKeyAutocompleteAdmin, added ability to filter autocomplete query - Fix: shell_plus, support for pypython>=0.27 - Fix: shell_plus, load apps and models directly through the apps interface when available - Fix: shell_plus, use ipython start_ipython instead of embed - Fix: shell_plus, fix swalling ImportErrors with IPython 3 and higher - Fix: dumpscript, fix missing imports in dumped script - Fix: admin_generator, fix issues with Django 1.9 - Fix: template tags, move exception for import failure to inside of the template tags - Fix: reset_db, fix for Django 1.9 - Fix: runserver_plus, fix for Django 1.9 1.5.5 ----- Changes: - Fix: sqldiff, previous Django 1.8 fix was slightly broken 1.5.4 ----- Changes: - Improvement: syncdata, add skip-remove option - Improvement: logging, report how often mail was ratelimited - Fix: admin, Django 1.8 compatibility module_name is now called model_name - Fix: notes, Python 3.x fix force output of filter into list - Fix: sqldiff, fix for Django 1.8 1.5.3 ----- Changes: - New: ratelimiter, a simple ratelimiter filter for Python logging - Fix: various improvements for Django 1.8 - Fix: sync_s3, use os.walk instead of os.path.walk (py3 fix) - Improvement: pipchecker, use name instead of url_name to fix casing mismatches - Improvement: pipchecker, use https - Improvement: pipchecker, fix issues with new(er) pip versions - Docs: fixed a few typos - Docs: added documentation about NOTEBOOK_ARGUMENTS settings 1.5.2 ----- Changes: - New: sqldsn, prints Data Source Name for defined database(s) - Fix: graph_models, Django 1.8 support - Fix: highlighting tag, fix usage of is_safe - Fix: runscript, fix for runscript with AppConfig apps - Fix: sqldiff, KeyError when index is missing in database - Fix: sqldiff, multi column indexes was also counted as a single colomn index - Improvements: JSONField, Added try/catch for importing json/simplejson for Django 1.7 1.5.1 ----- Changes: - New: runserver_plus, add support for --extra-files parameter - Fix: Django 1.7 defined MIDDLEWARE_CLASSES for tests - Fix: shell_plus, problem when auto-loading modules with empty '__module__' property - Improvement: shell_plus, IPython 3.x support for notebooks - Improvement: tests, move to py.test and lots of other improvements - Improvement: create_app, add migrations folder - Improvement: tox.ini, refactored to be more DRY - Improvement: runserver_plus, also reload on changes to translation files - Improvement: runserver_plus, add reloader_interval support - Improvement: create_template_tags, removed unusued command line option - Docs: print_user_for_session, add note about SESSION_ENGINE - Docs: runserver_plus, added section about IO calls and CPU usage 1.5.0 ----- Changes: - Fix: various fixes for Django 1.8 - Improvement: shell_plus, autodetect vi mode by looking at $EDITOR shell env setting - Improvement: shell_plus, print which shell is being used at verbosity > 1 - Improvement: shell_plus, added --no-browser option for IPython notebooks - Improvement: tox.ini, updated to latest Django versions - Docs: add reference to JSONField in documentation - Docs: fixed various typo's and links in docs and changelog - Docs: added some basic use cases to README - Docs: added information for companies or people wanting to donate towards the project - Fix: graphmodels, fix for python3 - Fix: dumpscript, fix check for missing import_helper module in Python 3 - Fix: runprofileserver, explicitly close file to avoid error on windows - Fix: json field, migration issues when adding new JSONField to existing model - Fix: runjobs, fix python3 issues 1.4.9 ----- Changes: - New: drop_test_database, drops the test database - New: command_signals, git commit -a -m 'bumped version number' (see docs) - Bugfix: runserver_plus, removed empty lines when logging on Python 3 1.4.8 ----- Changes: - Bugfix: validators, fix NoWhitespaceValidator __eq__ check 1.4.7 ----- Changes: - New: validators.py, NoControlCharactersValidator and NoWhitespaceValidator - New: EmailNotificationCommand class, email exceptions from Django Commands - Improvement: runserver_plus, enable threading by default and added --nothreading - Improvement: runscript, better detection when import error occured in script - Improvement: runscript, use EmailNotificationCommand class - Deprecation: deprecated UUIDField since Django 1.8 will have a native version. - Removed: completely remove support for automatically finding project root. 1.4.6 ----- Changes: - Improvement: sqldiff, fix for dbcolumn not used in few places when generating the sqldiff - Fix: sqldiff, backwards compatiblity fix for Django 1.4 - Fix: ForeignKey Field, handling of __str__ instead of __unicode__ in python3 1.4.5 ----- Changes: - New: clear_cache, Clear django cache, useful when testing or deploying - Improvement: AutoSlugField, add the possibility to define a custom slugify function - Improvement: shell_plus --notebook, add a big warning when the notebook extension is not going to be loaded - Improvement: setup.py, add pypy classifier - Improvement: readme, add pypy badges - Fix: admin_generator, Fixed Python 3 __unicode__/__str__ compatibility 1.4.4 ----- Changes: - Fix: admin_generator, fix ImproperlyConfigured exception on Django 1.7 - Improvement: Remove "requires_model_validation" and "requires_system_checks" in commands which set the default value 1.4.1 ----- Changes: - New: shell_plus, Added python-prompt-toolkit integration for shell_plus - New: shell_plus, Added --ptipython (PYPython + IPython) - Improvement: reset_db, output traceback to easy debugging in case of error - Improvement: dumpscript, add --autofield to dumpscript to include autofields in export - Improvement: show_urls, Include namespace in URL name - Improvement: show_urls, Allow multiple decorators on the show_urls command - Improvement: runscript, show script errors with verbosity > 1 - Fix: jobs, daily_cleanup job use clearsessions for Django 1.5 and later - Fix: shell_plus, refactored importing and selecting shells to avoid polluted exception - Fix: shell_plus, Fix model loading for sentry 1.4.0 ----- Changes: - New admin_generator, can generate a admin.py file from models - Improvement: sqldiff, use the same exit codes as diff uses - Improvement: sqldiff, add support for unsigned numeric fields - Improvement: sqldiff, add NOT NULL support for MySQL - Improvement: sqldiff, add proper AUTO_INCREMENT support for MySQL - Improvement: sqldiff, detect tables for which no model exists - Improvement: travis.yml, add pypy to tests - Fix: sqldiff, fix for mysql misreported field lengths - Fix: sqldiff, in PG custom int primary keys would be mistaking for serial - Fix: sqldiff, use Django 1.7 db_parameters() for detect check constraints - Fix: update_permissions, Django 1.7 support - Fix: encrypted fields, fix for Django 1.7 migrations 1.3.11 ------ Changes: - Improvement: sqldiff, show differences for not managed tables - Improvement: show_urls -f aligned, 3 spaces between columns - Improvement: reset_db, support mysql options files in reset_db - Fix: sqldiff, Fixed bug with --output_text option and notnull-differ text - Fix: reset_db, Fix for PostgreSQL databases with dashes, dots, etc in the name - Fix: dumpscript, AttributeError for datefields that are None - Docs: Adding RUNSERVERPLUS_SERVER_ADDRESS_PORT to docs 1.3.10 ------ Changes: - Fix: show_urls, fix bug in new formatter when column is empty 1.3.9 ----- Changes: - Feature: shell_plus, add --kernel option to start as standalone IPython kernel - Feature: reset_db, Programatically determine PostGIS template - Feature: sqldiff, add support for PointField and MultiPolygonField - Test: renamed test app - Fix: runserver_plus, --print-sql for Django 1.7 - Fix: shell_plus, --print-sql for Django 1.7 - Fix: show_urls, add support for functions that use functools.partial - Fix: show_urls, add formatter for aligned output (will most likely become future default) - Fix: shell_plus / notebook, support for Django 1.7 - Docs: various fixes and improvements - Cleanup: Remove work arounds for Django 0.96 and earlier 1.3.8 ----- Changes: - Feature: show_urls, add option to specify dense or verbose output format - Improvement: better support for django 1.7 migrations - Improvement: better support for django's admin docs - BugFix: runjob, job_name and app_name was swapped in error message - Docs: Update link to chinese docs - Python3: unreferenced_files, fix python3 compatibility - Python3: pipchecker, fix python3 compatibility 1.3.7 ----- Changes: - Reinstated: clean_pyc and compile_pyc commands, these now depends on BASE_DIR in settings.py as per Django 1.6. We urge everybody to include a BASE_DIR settings in their project file! auto-detecting the project-root is now deprecated and will be removed in 1.4.0. - I18N: Added russian locale - Docs: runscript, Add section about passing arguments to scripts - Python3: show_url, Fixed to AttributeError 'func_globals' - Deprecated: clean_pyc, compile_pyc, Auto-detecting project root 1.3.6 ----- Changes: - Additional version bump because we mistakenly already uploaded version 1.3.5 of the wheel package with the code of 1.3.4 1.3.5 ----- Changes: - Feature: Django-Extensions is now also distributed as a Wheel package - Improvement: dumpscript, improved the readability of comments in generated script - Improvement: sqldiff, backported get_constraints() for PostgreSQL - Improvement: shell_plus, consistent colorization - BugFix: encrypted fields, there is no decoding to unicode in Python 3 - BugFix: shell_plus, importing modules failed in some edge cases - Django 1.7: included Django 1.7 in test suite - Python 3.4: included Python 3.4 in test suite 1.3.4 ----- Changes: - Feature: Start maintaining a CHANGELOG file in the repository - Feature: ActivatorModelManager now has an ActivatorQuerySet - Feature: Add a deconstruct() method for future Django 1.7 migration compatibility - Feature: show_urls, now support --language for i18n_patterns - Feature: show_urls, now shows the decoraters set on a view function - Feature: graph_models, now support --include-models to restrict the graph to specified models - Feature: print_settings, allow to specify the settings you want to see - Improvement: graph_models, use '//' instead of '#' as comment character in dot files - Improvement: graph_models, added error message for abstract models without explicit relations - Improvement: JSONField, use python's buildin json support by default with fallback on django.utils.simplejson - Improvement: PostgreSQLUUIDField, parse value into UUID type before sending it to the database - Improvement: Use django.JQuery in autocomplete.js if available - Improvement: use "a not in b" instead of "not a in b" in the entire codebase - Removed: clean_pyc command since it does not work correctly in many cases - Removed: sync_media_s3 command in favor of sync_s3 - BugFix: syncdata, use pk instead of id for identifying primary key of objects - BugFix: sync_s3, use safer content type per default - BugFix: export_emails, filtering on groups - BugFix: print_user_for_session, use USERNAME_FIELD if defined - BugFix: update_permission, fixed TypeError issue - BugFix: JSONField, do not coerse a json string into a python list - BugFix: import json issue by using absolute imports - BugFix: add minimal version number to six (>=1.2) - Docs: graph_models, Added some documentation about using dot templates - Docs: reset_db, short description on SQL DDL used - Docs: Added specific list of supported Python and Django versions - Docs: Add link to GoDjango screencast - Docs: Add ShortUUIDField to docs - Python3: fixes to graph_models and export_emails for Python3 compatibility 1.3.3 ----- Changes: - Docs: Made it clearer that Django Extensions requires Django 1.4 or higher - Translations: FR Updated - Python3: Fix for shell_plus 1.3.0 ----- Changes: - Feature: SQLDiff much better notnull detection - Feature: reset_db add option to explicit set the PostGreSQL owner of the newly created DB - Feature: shell_plus added support for MongoEngine - Feature: sync_s3 enable syncing to other cloud providers compatible with s3 - Improvement: ForeignKeyAutocompleteAdmin add option to limit queryset - BugFix: graph_models fix issue with models without primary key - BugFix: graph_models fix UnicodeDecodeError using --verbose-names - BugFix: dumpscript fix problems with date/datetimes by saving them now as ISO8601 - Docs: many improvements - Docs: Chinese translation !!! - Python3: various improvements - Tests: add Django 1.6 django-extensions-1.8.1/LICENSE000066400000000000000000000020401312717465700161760ustar00rootroot00000000000000Copyright (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.django-extensions-1.8.1/MANIFEST.in000066400000000000000000000003631312717465700167350ustar00rootroot00000000000000recursive-include django_extensions/conf *.tmpl recursive-include django_extensions/templates * recursive-include django_extensions/static * recursive-include django_extensions/locale * recursive-include docs * include LICENSE include tox.ini django-extensions-1.8.1/Makefile000066400000000000000000000024161312717465700166400ustar00rootroot00000000000000help: @echo "clean - remove all build, test, coverage and Python artifacts" @echo "clean-build - remove build artifacts" @echo "clean-pyc - remove Python file artifacts" @echo "clean-test - remove test and coverage artifacts" @echo "compile-catalog - compile translation catalogs" @echo "test - run tests quickly with the default Python" @echo "coverage - check code coverage quickly with the default Python" @echo "install - install the package to the active Python's site-packages" clean: clean-test clean-build clean-pyc clean-build: rm -fr build/ rm -fr dist/ rm -fr .eggs/ find . -name '*.egg-info' -exec rm -fr {} + find . -name '*.egg' -exec rm -fr {} + clean-pyc: find . -name '*.pyc' -exec rm -f {} + find . -name '*.pyo' -exec rm -f {} + find . -name '*~' -exec rm -f {} + find . -name '__pycache__' -exec rm -fr {} + clean-test: rm -fr .cache/ rm -fr .tox/ rm -f .coverage rm -fr htmlcov/ compile-catalog: for loc in django_extensions/locale/*; do \ python setup.py compile_catalog --directory django_extensions/locale/ --locale $$(basename $$loc) --domain django || exit 1; \ done test: python setup.py test coverage: coverage run --source django_extensions setup.py test coverage report -m coverage html install: clean python setup.py install django-extensions-1.8.1/README.rst000066400000000000000000000072771312717465700167010ustar00rootroot00000000000000=================== Django Extensions =================== .. image:: https://img.shields.io/pypi/l/django-extensions.svg :target: https://raw.githubusercontent.com/django-extensions/django-extensions/master/LICENSE .. image:: https://secure.travis-ci.org/django-extensions/django-extensions.svg?branch=master :alt: Build Status :target: http://travis-ci.org/django-extensions/django-extensions .. image:: https://img.shields.io/pypi/v/django-extensions.svg :target: https://pypi.python.org/pypi/django-extensions/ :alt: Latest PyPI version .. image:: https://img.shields.io/pypi/wheel/django-extensions.svg :target: https://pypi.python.org/pypi/django-extensions/ :alt: Supports Wheel format .. image:: https://coveralls.io/repos/django-extensions/django-extensions/badge.svg?branch=master :target: https://coveralls.io/r/django-extensions/django-extensions?branch=master :alt: Coverage 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`__ (`watch the video on vimeo`__). In a couple minutes Eric walks you through a half a dozen command extensions. There is also a `short screencast on GoDjango`__ to help show you even more. Requirements ============ Django Extensions requires Django 1.8 or later. Getting It ========== You can get Django Extensions by using pip:: $ pip 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 Check templates for rendering errors:: $ python manage.py validate_templates Run the enhanced django shell:: $ python manage.py shell_plus Run the enhanced django runserver, (requires Werkzeug install):: $ python manage.py runserver_plus 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: - https://django-extensions.readthedocs.io Or you can look at the docs/ directory in the repository. Support ======= Django Extensions is free and always will be. It is development and maintained by developers in an Open Source manner. Any support is welcome. You could help by writing documentation, pull-requests, report issues and/or translations. Please remember that nobody is payed directly to develop or maintain Django Extensions so we do have to divide our time between putting food on the table, family, this project and the rest of life :-) __ http://ericholscher.com/blog/2008/sep/12/screencast-django-command-extensions/ __ http://vimeo.com/1720508 __ https://godjango.com/39-be-more-productive-with-django_extensions/ django-extensions-1.8.1/django_extensions/000077500000000000000000000000001312717465700207165ustar00rootroot00000000000000django-extensions-1.8.1/django_extensions/__init__.py000066400000000000000000000005551312717465700230340ustar00rootroot00000000000000# -*- coding: utf-8 -*- VERSION = (1, 8, 1) # 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 django-extensions-1.8.1/django_extensions/admin/000077500000000000000000000000001312717465700220065ustar00rootroot00000000000000django-extensions-1.8.1/django_extensions/admin/__init__.py000066400000000000000000000153331312717465700241240ustar00rootroot00000000000000# -*- coding: utf-8 -*- # # Autocomplete feature for admin panel # import six import operator from functools import update_wrapper from six.moves import reduce from django.apps import apps from django.http import HttpResponse, HttpResponseNotFound from django.conf import settings 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 from django.contrib import admin from django_extensions.admin.widgets import ForeignKeySearchInput class ForeignKeyAutocompleteAdminMixin(object): """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. And also an optional additional field to set the limit on the results returned by the autocomplete query. You can set this integer value in your settings file using FOREIGNKEY_AUTOCOMPLETE_LIMIT or you can set this per ForeignKeyAutocompleteAdmin basis. If any value is set the results will not be limited. """ related_search_fields = {} related_string_functions = {} autocomplete_limit = getattr(settings, 'FOREIGNKEY_AUTOCOMPLETE_LIMIT', None) def get_urls(self): from django.conf.urls import url def wrap(view): def wrapper(*args, **kwargs): return self.admin_site.admin_view(view)(*args, **kwargs) return update_wrapper(wrapper, view) return [ url(r'foreignkey_autocomplete/$', wrap(self.foreignkey_autocomplete), name='%s_%s_autocomplete' % (self.model._meta.app_label, self.model._meta.model_name)) ] + super(ForeignKeyAutocompleteAdminMixin, self).get_urls() 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: if six.PY3: to_string_function = lambda x: x.__str__() else: 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 = apps.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.query.select_related = queryset.query.select_related other_qs = other_qs.filter(reduce(operator.or_, or_queries)) queryset = queryset & other_qs additional_filter = self.get_related_filter(model, request) if additional_filter: queryset = queryset.filter(additional_filter) if self.autocomplete_limit: queryset = queryset[:self.autocomplete_limit] 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_related_filter(self, model, request): """Given a model class and current request return an optional Q object that should be applied as an additional filter for autocomplete query. If no additional filtering is needed, this method should return None.""" 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(ForeignKeyAutocompleteAdminMixin, self).formfield_for_dbfield(db_field, **kwargs) class ForeignKeyAutocompleteAdmin(ForeignKeyAutocompleteAdminMixin, admin.ModelAdmin): pass class ForeignKeyAutocompleteTabularInline(ForeignKeyAutocompleteAdminMixin, admin.TabularInline): pass class ForeignKeyAutocompleteStackedInline(ForeignKeyAutocompleteAdminMixin, admin.StackedInline): pass django-extensions-1.8.1/django_extensions/admin/filter.py000066400000000000000000000035361312717465700236540ustar00rootroot00000000000000# -*- coding: utf-8 -*- from __future__ import unicode_literals from django.contrib.admin import FieldListFilter from django.contrib.admin.utils import prepare_lookup_value from django.utils.translation import ugettext_lazy as _ class NullFieldListFilter(FieldListFilter): def __init__(self, field, request, params, model, model_admin, field_path): self.lookup_kwarg = '{0}__isnull'.format(field_path) super(NullFieldListFilter, self).__init__(field, request, params, model, model_admin, field_path) lookup_choices = self.lookups(request, model_admin) self.lookup_choices = () if lookup_choices is None else list(lookup_choices) def expected_parameters(self): return [self.lookup_kwarg] def value(self): return self.used_parameters.get(self.lookup_kwarg, None) def lookups(self, request, model_admin): return ( ('1', _('Yes')), ('0', _('No')), ) def choices(self, cl): yield { 'selected': self.value() is None, 'query_string': cl.get_query_string({}, [self.lookup_kwarg]), 'display': _('All'), } for lookup, title in self.lookup_choices: yield { 'selected': self.value() == prepare_lookup_value(self.lookup_kwarg, lookup), 'query_string': cl.get_query_string({ self.lookup_kwarg: lookup, }, []), 'display': title, } def queryset(self, request, queryset): if self.value() is not None: kwargs = {self.lookup_kwarg: self.value()} return queryset.filter(**kwargs) return queryset class NotNullFieldListFilter(NullFieldListFilter): def lookups(self, request, model_admin): return ( ('0', _('Yes')), ('1', _('No')), ) django-extensions-1.8.1/django_extensions/admin/widgets.py000066400000000000000000000062261312717465700240340ustar00rootroot00000000000000# -*- coding: utf-8 -*- import six from six.moves import urllib from django import forms from django.contrib.admin.sites import site from django.contrib.admin.widgets import ForeignKeyRawIdWidget try: from django.urls import reverse except ImportError: from django.core.urlresolvers import reverse from django.template.loader import render_to_string from django.utils.safestring import mark_safe from django.utils.text import Truncator class ForeignKeySearchInput(ForeignKeyRawIdWidget): """ A Widget for displaying ForeignKeys in an autocomplete search input instead in a {% trans django-extensions-1.8.1/django_extensions/templatetags/000077500000000000000000000000001312717465700234105ustar00rootroot00000000000000django-extensions-1.8.1/django_extensions/templatetags/__init__.py000066400000000000000000000000001312717465700255070ustar00rootroot00000000000000django-extensions-1.8.1/django_extensions/templatetags/highlighting.py000066400000000000000000000061701312717465700264330ustar00rootroot00000000000000# -*- coding: utf-8 -*- """ 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 django import template from django.template import ( Context, Node, Template, TemplateSyntaxError, Variable, ) from django.template.defaultfilters import stringfilter from django.utils.safestring import mark_safe try: from pygments import highlight as pyghighlight from pygments.lexers import get_lexer_by_name from pygments.formatters import HtmlFormatter HAS_PYGMENTS = True except ImportError: HAS_PYGMENTS = False register = template.Library() @register.filter(is_safe=True) @stringfilter def parse_template(value): return mark_safe(Template(value).render(Context())) 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.

    Usage::

      {% load highlighting %}
      {% highlight 'python' 'Excerpt: blah.py' %}
      def need_food(self):
          print("Love is colder than death")
      {% endhighlight %}

    """
    if not HAS_PYGMENTS:
        raise ImportError("Please install 'pygments' library to use highlighting.")
    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:])
django-extensions-1.8.1/django_extensions/templatetags/indent_text.py000066400000000000000000000033221312717465700263070ustar00rootroot00000000000000# -*- coding: utf-8 -*-
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


@register.tag
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)
django-extensions-1.8.1/django_extensions/templatetags/syntax_color.py000066400000000000000000000061631312717465700265140ustar00rootroot00000000000000# -*- coding: utf-8 -*-
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.
"""

from django import template
from django.template.defaultfilters import stringfilter
from django.utils.safestring import mark_safe

try:
    from pygments import highlight
    from pygments.formatters import HtmlFormatter
    from pygments.lexers import get_lexer_by_name, guess_lexer, ClassNotFound
    HAS_PYGMENTS = True
except ImportError:
    HAS_PYGMENTS = False

__author__ = 'Will Larson '


register = template.Library()


@register.simple_tag
def pygments_css():
    if not HAS_PYGMENTS:
        raise ImportError("Please install 'pygments' library to use syntax_color.")
    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):
    if not HAS_PYGMENTS:
        raise ImportError("Please install 'pygments' library to use syntax_color.")
    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):
    if not HAS_PYGMENTS:
        raise ImportError("Please install 'pygments' library to use syntax_color.")
    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):
    if not HAS_PYGMENTS:
        raise ImportError("Please install 'pygments' library to use syntax_color.")
    try:
        return mark_safe(highlight(value, get_lexer(value, arg), HtmlFormatter(noclasses=True)))
    except ClassNotFound:
        return value
django-extensions-1.8.1/django_extensions/templatetags/truncate_letters.py000066400000000000000000000010751312717465700273540ustar00rootroot00000000000000# -*- coding: utf-8 -*-
from django import template
from django.template.defaultfilters import stringfilter

register = template.Library()


@register.filter(is_safe=True)
@stringfilter
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)
django-extensions-1.8.1/django_extensions/templatetags/widont.py000066400000000000000000000036621312717465700252750ustar00rootroot00000000000000# -*- coding: utf-8 -*-
from __future__ import print_function
import re

from django.template import Library
from django.utils.encoding import force_text


register = Library()
re_widont = re.compile(r'\s+(\S+\s*)$')
re_widont_html = re.compile(r'([^<>\s])\s+([^<>\s]+\s*)(]*>|$)', re.IGNORECASE)


@register.filter
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

    >>> print("'",widont('It works with trailing spaces too  '), "'")
    ' It works with trailing spaces too   '

    >>> print(widont('NoEffect'))
    NoEffect
    """
    def replace(matchobj):
        return force_text(' %s' % matchobj.group(1))
    for i in range(count):
        value = re_widont.sub(replace, force_text(value))
    return value


@register.filter
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 force_text('%s %s%s' % matchobj.groups()) return re_widont_html.sub(replace, force_text(value)) if __name__ == "__main__": def _test(): import doctest doctest.testmod() _test() django-extensions-1.8.1/django_extensions/utils/000077500000000000000000000000001312717465700220565ustar00rootroot00000000000000django-extensions-1.8.1/django_extensions/utils/__init__.py000066400000000000000000000000001312717465700241550ustar00rootroot00000000000000django-extensions-1.8.1/django_extensions/utils/deprecation.py000066400000000000000000000003241312717465700247240ustar00rootroot00000000000000# -*- coding: utf-8 -*- from __future__ import absolute_import from django.utils.deprecation import RemovedInNextVersionWarning class MarkedForDeprecationWarning(RemovedInNextVersionWarning): pass django-extensions-1.8.1/django_extensions/utils/dia2django.py000066400000000000000000000237501312717465700244410ustar00rootroot00000000000000# -*- 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 import codecs import gzip import re import sys from xml.dom.minidom import Node, parseString import six dependclasses = ["User", "Group", "Permission", "Message"] # 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", "time": "TimeField", "bool": "BooleanField", } # 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] += " %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 'A' not 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 six.iteritems(clases): k[2] += "\n def %s(self):\n return u\"\"\n" % (("__str__" if six.PY3 else "__unicode__"),) 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") django-extensions-1.8.1/django_extensions/utils/text.py000066400000000000000000000012561312717465700234200ustar00rootroot00000000000000# -*- coding: utf-8 -*- import six from django.utils.encoding import force_text try: from django.utils.functional import keep_lazy KEEP_LAZY = True except ImportError: from django.utils.functional import allow_lazy KEEP_LAZY = False def truncate_letters(s, num): """ truncates a string to a number of letters, similar to truncate_words """ s = force_text(s) length = int(num) if len(s) > length: s = s[:length] if not s.endswith('...'): s += '...' return s if KEEP_LAZY: truncate_letters = keep_lazy(six.text_type)(truncate_letters) else: truncate_letters = allow_lazy(truncate_letters, six.text_type) django-extensions-1.8.1/django_extensions/validators.py000066400000000000000000000041021312717465700234350ustar00rootroot00000000000000# -*- coding: utf-8 -*- import unicodedata from django.core.exceptions import ValidationError from django.utils.deconstruct import deconstructible from django.utils.encoding import force_text from django.utils.translation import ugettext_lazy as _ @deconstructible class NoControlCharactersValidator(object): message = _("Control Characters like new lines or tabs are not allowed.") code = "no_control_characters" whitelist = None def __init__(self, message=None, code=None, whitelist=None): if message: self.message = message if code: self.code = code if whitelist: self.whitelist = whitelist def __call__(self, value): value = force_text(value) whitelist = self.whitelist category = unicodedata.category for character in value: if whitelist and character in whitelist: continue if category(character)[0] == "C": params = {'value': value, 'whitelist': whitelist} raise ValidationError(self.message, code=self.code, params=params) def __eq__(self, other): return ( isinstance(other, NoControlCharactersValidator) and (self.whitelist == other.whitelist) and (self.message == other.message) and (self.code == other.code) ) @deconstructible class NoWhitespaceValidator(object): message = _("Leading and Trailing whitespace is not allowed.") code = "no_whitespace" def __init__(self, message=None, code=None, whitelist=None): if message: self.message = message if code: self.code = code def __call__(self, value): value = force_text(value) if value != value.strip(): params = {'value': value} raise ValidationError(self.message, code=self.code, params=params) def __eq__(self, other): return ( isinstance(other, NoWhitespaceValidator) and (self.message == other.message) and (self.code == other.code) ) django-extensions-1.8.1/docs/000077500000000000000000000000001312717465700161255ustar00rootroot00000000000000django-extensions-1.8.1/docs/AUTHORS000066400000000000000000000015401312717465700171750ustar00rootroot00000000000000The 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) - 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 Ceesjan Luiten (quinox) - original staticfiles patch Camilo Nova (camilonova) Wiktor Kołodziej (viciu) Marc Tudurí (marctc) Rick van Hattem (WoLpH) Rodolphe Quiédeville (rodo) Nik Nyby (nikolas) Joshua Miller (thecardcheat)django-extensions-1.8.1/docs/Makefile000066400000000000000000000045071312717465700175730ustar00rootroot00000000000000# 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." django-extensions-1.8.1/docs/admin_extensions.rst000066400000000000000000000042021312717465700222240ustar00rootroot00000000000000Current 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 configurable autocompletion. Example Usage ------------- To enable the Admin Autocomplete you can follow this code example in your admin.py file: :: from django.contrib import admin from foo.models import Permission from django_extensions.admin import ForeignKeyAutocompleteAdmin class PermissionAdmin(ForeignKeyAutocompleteAdmin): # User is your FK attribute in your model # first_name and email are attributes to search for in the FK model related_search_fields = { 'user': ('first_name', 'email'), } fields = ('user', 'avatar', 'is_active') ... admin.site.register(Permission, PermissionAdmin) If you are using django-reversion you should follow this code example: :: from django.contrib import admin from foo.models import MyVersionModel from reversion.admin import VersionAdmin from django_extensions.admin import ForeignKeyAutocompleteAdmin class MyVersionModelAdmin(VersionAdmin, ForeignKeyAutocompleteAdmin): ... admin.site.register(MyVersionModel, MyVersionModelAdmin) If you need to limit the autocomplete search, you can override the ``get_related_filter`` method of the admin. For example if you want to allow non-superusers to attach attachments only to articles they own you can use:: class AttachmentAdmin(ForeignKeyAutocompleteAdmin): ... def get_related_filter(self, model, request): user = request.user if not issubclass(model, Article) or user.is_superuser(): return super(AttachmentAdmin, self).get_related_filter( model, request ) return Q(owner=user) Note that this does not protect your application from malicious attempts to circumvent it (e.g. sending fabricated requests via cURL). django-extensions-1.8.1/docs/command_extension_ideas.rst000066400000000000000000000003311312717465700235330ustar00rootroot00000000000000Ideas 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 django-extensions-1.8.1/docs/command_extensions.rst000066400000000000000000000140411312717465700225540ustar00rootroot00000000000000Current 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. * *admin_generator* - Generate automatic Django Admin classes by providing an app name. Outputs source code at STDOUT. * *clean_pyc* - Remove all python bytecode compiled files from the project * `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_template_tags* - Creates a template tag directory structure within the specified 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. * *clear_cache* - Clear django cache, useful when testing or deploying. * *compile_pyc* - Compile python bytecode files for the project. * *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:`delete_squashed_migrations` - Deletes leftover migrations after squashing and converts squashed migration to a normal one. * :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. * *find_template* - Finds the location of the given template by resolving its path * *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. * *notes* - Show all annotations like TODO, FIXME, BUG, HACK, WARNING, NOTE or XXX in your py and HTML files. * *passwd* - Makes it easy to reset a user's password. * *pipchecker* - Scan pip requirement file(s)s for out-of-date packages. Similar to ``pip list -o`` which used installed packages (in virtualenv) instead of requirements file(s). * `print_settings`_ - Similar to ``diffsettings`` but shows *selected* active Django settings or *all* if no args passed. * *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. It seems this works only if setting ``SESSION_ENGINE`` is ``'django.contrib.sessions.backends.db'`` (default value). * *drop_test_database* - Drops the test database. Usefull when running Django test via some automated system (BuildBot, Jenkins, etc) and making sure that the test database is always dropped at the end. * *reset_db* - Resets a database (currently sqlite3, mysql, postgres). Uses "DROP DATABASE" and "CREATE DATABASE". * *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_emails* - Give all users a new email based on their account data ("%(username)s@example.com" by default). Possible parameters are: username, first_name, last_name. *DEBUG only* * *set_fake_passwords* - Sets all user passwords to a common value (*password* by default). *DEBUG only*. * *show_template_tags* - Displays template tags and filters available in the current project. * *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 app's 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. * :doc:`sqldsn` - Reads the Django settings and extracts the parameters needed to connect to databases using other programs. * `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. * *syncdata* - Makes the current database have the same data as the fixture(s), no more, no less. * *unreferenced_files* - Prints a list of all files in MEDIA_ROOT that are not referenced in the database. * *update_permissions* - Reloads permissions for specified apps, or all apps if no args are specified. * *validate_templates* - Validate templates on syntax and compile errors. * *set_default_site* - Set parameters of the default `django.contrib.sites` Site using `name` and `domain` or `system-fqdn`. .. _`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/ django-extensions-1.8.1/docs/command_signals.rst000066400000000000000000000057521312717465700220260ustar00rootroot00000000000000Command Signals =============== :synopsis: Signals fired before and after a command is executed. A signal is thrown pre/post each management command allowing your application to hook into each commands execution. Basic Example ------------- An example hooking into show_template_tags: :: from django_extensions.management.signals import pre_command, post_command from django_extensions.management.commands.show_template_tags import Command def pre_receiver(sender, args, kwargs): # I'm executed prior to the management command def post_receiver(sender, args, kwargs, outcome): # I'm executed after the management command pre_command.connect(pre_receiver, Command) post_command.connect(post_receiver, Command) Custom Permissions For All Models --------------------------------- You can use the post signal to hook into the ``update_permissions`` command so that you can add your own permissions to each model. For instance, lets say you want to add ``list`` and ``view`` permissions to each model. You could do this by adding them to the ``permissions`` tuple inside your models ``Meta`` class but this gets pretty tedious. An easier solution is to hook into the ``update_permissions`` call, as follows; :: from django.db.models.signals import post_syncdb from django.contrib.contenttypes.models import ContentType from django.contrib.auth.models import Permission from django_extensions.management.signals import post_command from django_extensions.management.commands.update_permissions import Command as UpdatePermissionsCommand def add_permissions(sender, **kwargs): """ Add view and list permissions to all content types. """ # for each of our content types for content_type in ContentType.objects.all(): for action in ['view', 'list']: # build our permission slug codename = "%s_%s" % (action, content_type.model) try: Permission.objects.get(content_type=content_type, codename=codename) # Already exists, ignore except Permission.DoesNotExist: # Doesn't exist, add it Permission.objects.create(content_type=content_type, codename=codename, name="Can %s %s" % (action, content_type.name)) print "Added %s permission for %s" % (action, content_type.name) post_command.connect(add_permissions, UpdatePermissionsCommand) Each time ``update_permissions`` is called ``add_permissions`` will be called which ensures there are view and list permissions to all content types. Using pre/post signals on your own commands ------------------------------------------- The signals are implemented using a decorator on the handle method of a management command, thus using this functionality in your own application is trivial: :: from django_extensions.management.utils import signalcommand class Command(BaseCommand): @signalcommand def handle(self, *args, **kwargs): ... ... django-extensions-1.8.1/docs/conf.py000066400000000000000000000145121312717465700174270ustar00rootroot00000000000000# -*- 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-2015 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.8' # The full version, including alpha/beta/rc tags. release = '1.8.1' # 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 django-extensions-1.8.1/docs/create_app.rst000066400000000000000000000020461312717465700207640ustar00rootroot00000000000000create_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 django-extensions-1.8.1/docs/creating_release.txt000066400000000000000000000017171312717465700221700ustar00rootroot00000000000000Creating 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:: $ flake8 django_extensions $ tox --recreate 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 Remove old build directory (if exists):: $ rm -r build dist Prepare the release tarball:: $ python ./setup.py sdist bdist_wheel Upload release to pypi:: $ twine upload -s dist/* 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 django-extensions-1.8.1/docs/delete_squashed_migrations.rst000066400000000000000000000020661312717465700242560ustar00rootroot00000000000000delete_squashed_migrations ========================== :synopsis: Deletes leftover migrations after squashing and converts squashed migration to a normal one. Deletes leftover migrations after squashing and converts squashed migration to a normal one by removing the replaces attribute. This automates the clean up procedure outlined at the end of the `Django migration squashing documentation`__. Modifies your source tree! Use with care! __ MigrationSquashingDocs_ Example Usage ------------- With *django-extensions* installed you cleanup squashed migrations using the *delete_squashed_migrations* command:: # Delete leftover migrations from the first squashed migration found in myapp $ ./manage.py delete_squashed_migrations myapp # As above but non-interactive $ ./manage.py --noinput delete_squashed_migrations myapp # Explicitly specify the squashed migration to clean up $ ./manage.py delete_squashed_migrations myapp 0001_squashed .. _MigrationSquashingDocs: https://docs.djangoproject.com/en/dev/topics/migrations/#migration-squashing django-extensions-1.8.1/docs/dumpscript.rst000066400000000000000000000047441312717465700210620ustar00rootroot00000000000000dumpscript ========== :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 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 * Recursive references * *AutoFields* are excluded * Parent models are only included when no other child model links to it * Individual models can be referenced 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. For instance, if the appname is the same as the script name, an importerror can occur because rather than 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 django-extensions-1.8.1/docs/export_emails.rst000066400000000000000000000042351312717465700215360ustar00rootroot00000000000000export_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, LinkedIn Group pre-approved listing, etc.). The export_emails command extension gives you this ability. Exported users 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 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. Only two columns are supplied. One for the person's name and one for the email 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 email 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 email 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 django-extensions-1.8.1/docs/field_extensions.rst000066400000000000000000000107641312717465700222310ustar00rootroot00000000000000Field Extensions ================ :synopsis: Current Field Extensions Current Database Model Field Extensions --------------------------------------- * *AutoSlugField* - AutoSlugfield will automatically create a unique slug incrementing an appended number on the slug until it is unique. Inspired by SmileyChris' Unique Slugify snippet. AutoSlugField takes a `populate_from` argument that specifies which field, list of fields, or model method the slug will be populated from, for instance:: slug = AutoSlugField(populate_from=['title', 'description', 'get_author_name']) `populate_from` can traverse a ForeignKey relationship by using Django ORM syntax:: slug = AutoSlugField(populate_from=['related_model__title', 'related_model__get_readable_name']) * *RandomCharField* - AutoRandomCharField will automatically create a unique random character field with the specified length. By default upper/lower case and digits are included as possible characters. Given a length of 8 thats yields 3.4 million possible combinations. A 12 character field would yield about 2 billion. Below are some examples:: >>> RandomCharField(length=8, unique=True) BVm9GEaE >>> RandomCharField(length=4, include_alpha=False) 7097 >>> RandomCharField(length=12, include_punctuation=True) k[ZS.TR,0LHO >>> RandomCharField(length=12, lowercase=True, include_digits=False) pzolbemetmok * *CreationDateTimeField* - DateTimeField that will automatically set its date when the object is first saved to the database. Works in the same way as the auto_now_add keyword. * *ModificationDateTimeField* - DateTimeField that will automatically set its date when an object is saved to the database. Works in the same way as the auto_now keyword. It is possible to preserve the current timestamp by setting update_modified to False:: >>> example = MyTimeStampedModel.objects.get(pk=1) >>> print example.modified datetime.datetime(2016, 3, 18, 10, 3, 39, 740349, tzinfo=) >>> example.save(update_modified=False) >>> print example.modified datetime.datetime(2016, 3, 18, 10, 3, 39, 740349, tzinfo=) >>> example.save() >>> print example.modified datetime.datetime(2016, 4, 8, 14, 25, 43, 123456, tzinfo=) It is also possible to set the attribute directly on the model, for example when you don't use the TimeStampedModel provided in this package, or when you are in a migration:: >>> example = MyCustomModel.objects.get(pk=1) >>> print example.modified datetime.datetime(2016, 3, 18, 10, 3, 39, 740349, tzinfo=) >>> example.update_modified=False >>> example.save() >>> print example.modified datetime.datetime(2016, 3, 18, 10, 3, 39, 740349, tzinfo=) * *UUIDField* - UUIDField for Django, supports all uuid versions that are natively supported by the uuid python module. .. deprecated:: 1.4.7 Django 1.8 features a native UUIDField. Django-Extensions will support *UUIDField* at the very least until Django 1.7 becomes unsupported. * *PostgreSQLUUIDField* - UUIDField for Django, uses PostgreSQL uuid type. .. deprecated:: 1.4.7 Django 1.8 features a native UUIDField. Django-Extensions will support *UUIDField* at the very least until Django 1.7 becomes unsupported. * *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.ENCRYPTED_FIELD_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.ENCRYPTED_FIELD_KEYS_DIR`` set to the full path of your keys directory. * *ShortUUIDField* - CharField which transparently generates a UUID and pass it to base57. It result in shorter 22 characters values useful e.g. for concise, unambiguous URLS. It's possible to get shorter values with length parameter: they are not Universal Unique any more but probability of collision is still low * *JSONField* - a generic TextField that neatly serializes/unserializes JSON objects seamlessly. Django 1.9 introduces a native JSONField for PostgreSQL, which is preferred for PostgreSQL users on Django 1.9 and above. django-extensions-1.8.1/docs/graph_models.rst000066400000000000000000000074751312717465700213400ustar00rootroot00000000000000Graph 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. Several options are available: 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 parameter, depending on which 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. In that case 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. Templates --------- Django templates are used to generate the dot code. This in turn can be drawn into a image by libraries like *pygraphviz* or *pydot*. You can extend or override the templates if needed. Templates used: - django_extensions/graph_models/digraph.dot - django_extensions/graph_models/label.dot - django_extensions/graph_models/relation.dot Documentation on how to create dot files can be found here: http://www.graphviz.org/Documentation.php .. warning:: Modifying Django's default templates behaviour might break *graph_models* Please be aware that if you use any *template_loaders* or extensions that change the way templates are rendered that this can cause *graph_models* to fail. An example of this is the Django app *django-template-minifier* this automatically removed the newlines before/after template tags even for none-html templates which leads to a mallformed file. Example Usage ------------- With *django-extensions* installed you can create a dot-file or an image by using the *graph_models* command:: # 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 explicit selection of 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 :: # Create a graph for only certain models $ ./manage.py graph_models -a -I Foo,Bar -o my_project_subsystem.png :: # Create a excluding certain models $ ./manage.py graph_models -a -X Foo,Bar -o my_project_sans_foo_bar.png .. _GraphViz: http://www.graphviz.org/ .. _pygraphviz: https://pygraphviz.github.io/ .. _pydot: https://pypi.python.org/pypi/pydot django-extensions-1.8.1/docs/index.rst000066400000000000000000000047021312717465700177710ustar00rootroot00000000000000.. 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 the django-extensions documentation! =============================================== Django Extensions is a collection of custom extensions for the Django Framework. These include management commands, additional database fields, 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:: $ pip 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 follow the Django guidelines for supported Python and Django versions. See more at `Django Supported Versions `_ This might mean the django-extensions may work with older or unsupported versions but we do not guarantee it and most likely will not fix bugs related to incompatibilities with older versions. At this time we test on and thrive to support valid combinations of Python 2.7, 3.4, 3.5, pypy and pypy3 with Django versions 1.8 and 1.9. Contents ======== .. toctree:: :maxdepth: 3 installation_instructions command_extensions command_extension_ideas command_signals admin_extensions shell_plus create_app delete_squashed_migrations dumpscript runscript export_emails field_extensions graph_models jobs_scheduling model_extensions namespace_proposal print_settings runprofileserver runserver_plus sync_s3 sqldiff sqlcreate sqldsn validate_templates Indices and tables ================== * :ref:`search` __ http://ericholscher.com/blog/2008/sep/12/screencast-django-command-extensions/ django-extensions-1.8.1/docs/installation_instructions.rst000066400000000000000000000037541312717465700242150ustar00rootroot00000000000000Installation instructions ========================= :synopsis: Installing django-extensions Installation ------------ For usage ^^^^^^^^^ You can use pip to install django-extensions for usage:: $ pip install django-extensions For development ^^^^^^^^^^^^^^^ 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. $ pip install -e git+https://github.com/django-extensions/django-extensions.git#egg=django-extensions You find the sources in src/django-extensions now. 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. Configuration ^^^^^^^^^^^^^ 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 commands or options 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. django-extensions-1.8.1/docs/jobs_scheduling.rst000066400000000000000000000024221312717465700220210ustar00rootroot00000000000000Jobs scheduling =============== :synopsis: Documentation on creating/using jobs in Django-extensions JobsScheduling -------------- This page is very much a 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 run. The following commands are related to jobs: * create_jobs, create the directory structure for jobs * runjob, run 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 manually specifying the exact time on which the command is to be run, or use crontab: :: @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 django-extensions-1.8.1/docs/model_extensions.rst000066400000000000000000000010531312717465700222350ustar00rootroot00000000000000Model extensions ================ :synopsis: Current Model Extensions Current Database Model Extensions --------------------------------- * *TimeStampedModel* - TimeStampedModel An abstract base class model that provides self-managed "created" and "modified" fields. * *TitleDescriptionModel* - An abstract base class model that provides "title" and "description" fields. * *TitleSlugDescriptionModel* - An abstract base class model that provides "title" and "description" fields and a self-managed "slug" field that populates from the title. django-extensions-1.8.1/docs/namespace_proposal.rst000066400000000000000000000013241312717465700225320ustar00rootroot00000000000000Namespace 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 okay where it is right now. It's only used when somebody explicitly imports:: from django_extensions.db.models import something django-extensions-1.8.1/docs/print_settings.rst000066400000000000000000000047401312717465700217400ustar00rootroot00000000000000print_settings ============== :synopsis: Django managment command similar to ``diffsettings`` but shows *selected* active Django settings or *all* if no args passed. 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 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 variations:: $ python manage.py print_settings --format=json $ python manage.py print_settings --format=yaml # Requires PyYAML $ python manage.py print_settings --format=pprint $ python manage.py print_settings --format=text $ python manage.py print_settings --format=value Show just selected settings:: $ python manage.py print_settings DEBUG INSTALLED_APPS $ python manage.py print_settings DEBUG INSTALLED_APPS --format=pprint $ python manage.py print_settings INSTALLED_APPS --format=value 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 django-extensions-1.8.1/docs/runprofileserver.rst000066400000000000000000000102371312717465700222760ustar00rootroot00000000000000RunProfileServer ================ *We recommend that before you start profiling any language or framework you learn enough about it so that you feel comfortable with digging into its internals.* *Without sufficient knowledge it will not only be (very) hard but you're likely to make wrong assumptions (and fixes). As a rule of thumb, clean, well written code will help you a lot more than 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. By 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 keeps /tmp uncluttered. (Yes this probably malfunctions systems such as Windows 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. Profiler choice --------------- *runprofileserver* supports two profilers: *hotshot* and *cProfile*. Both come with the standard Python library but *cProfile* is more recent and may not be available on all systems. For this reason, *hotshot* is the default profiler. However, *hotshot* `is not maintained anymore `_ and using *cProfile* is usually the recommended way. If it is available on your system, you can use it with the option ``--use-cprofile``. Example:: $ mkdir /tmp/my-profile-data $ ./manage.py runprofileserver --use-cprofile --prof-path=/tmp/my-profile-data If you used the default profiler but are not able to open the profiling results with the ``pstats`` module or with your profiling GUI of choice because of an error "*ValueError: bad marshal data (unknown type code)*", try using *cProfile* instead. 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 X.Y.Z, 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/ django-extensions-1.8.1/docs/runscript.rst000066400000000000000000000055221312717465700207140ustar00rootroot00000000000000RunScript ============= :synopsis: Runs a script in the django context. Introduction ------------ The runscript command lets you run an arbritrary set of python commands within the django context. It offers the same usability and functionality as running a set of commands in shell accessed by:: $ python manage.py shell Getting Started --------------- This example assumes you have followed the tutorial for Django 1.8+, and created a polls app containing a ``Question`` model. We will create a script that deletes all of the questions from the database. 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_questions.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_questions.py from polls.models import Question def run(): # Fetch all questions questions = Question.objects.all() # Delete questions questions.delete() Note: You can put a script inside a *scripts* folder in any of your apps too. Usage ----- 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_questions 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. Passing arguments ----------------- You can pass arguments from the command line to your script by passing a space separated list of values with ``--script-args``. For example:: $ python manage.py runscript delete_all_questions --script-args staleonly The list of argument values gets passed as arguments to your *run()* function. For example:: # scripts/delete_all_questions.py from datetime import timedelta from django.utils import timezone from polls.models import Question def run(*args): # Get all questions questions = Question.objects.all() if 'staleonly' in args: # Only get questions more than 100 days old questions = questions.filter(pub_date__lt=timezone.now() - timedelta(days=100)) # Delete questions questions.delete() Debugging --------- If an exception occurs you will not get a traceback by default. To get a traceback specify ``--traceback``. For example:: $ python manage.py runscript delete_all_questions --traceback django-extensions-1.8.1/docs/runserver_plus.rst000066400000000000000000000174741312717465700217720ustar00rootroot00000000000000RunServerPlus ============= :synopsis: RunServerPlus-typical runserver with Werkzeug debugger baked in Introduction ------------ This item requires that you have the `Werkzeug WSGI utilities` installed. Included with Werkzeug_ is a kick ass debugger that renders nice debugging tracebacks and adds an AJAX based debugger (which allows code execution 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 X.Y.Z, 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. Usage ----- Instead of the default Django traceback page, the Werkzeug traceback page will be shown when an exception occurs. .. 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 hovering 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 underneath 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 it provides more context information around the error. The actual traceback areas are highlighted so they are easy to spot. One awkward aspect of the UI is that the page is not scrolled to the bottom. At first I thought nothing was happening because of this. Interactive Debugging Console ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Clicking on this button opens up a new pane under 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 start debugging. 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 problem check. 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 that pop up 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 X.Y.Z, 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 X.Y.Z, 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 running this command, your web application can be accessed through https://127.0.0.1:8000. You will also find that two files are created in the current working directory: a 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 a specific file for the certificate to be used if you already have one:: $ python manage.py runserver_plus --cert /tmp/cert Note that you need the OpenSSL library to use SSL, and Werkzeug 0.9 or later if you want to reuse existing certificates. To install OpenSSL:: $ pip install pyOpenSSL Configuration ^^^^^^^^^^^^^ The `RUNSERVERPLUS_SERVER_ADDRESS_PORT` setting can be configured to specify which address and port the development server should bind to. If you find yourself frequently starting the server with:: $ python manage.py runserver_plus 0.0.0.0:8000 You can use settings to automatically default your development to an address/port:: RUNSERVERPLUS_SERVER_ADDRESS_PORT = '0.0.0.0:8000' To ensure Werkzeug can log to the console, you may need to add the following to your settings:: LOGGING = { ... 'handlers': { ... 'console': { 'level': 'DEBUG', 'class': 'logging.StreamHandler', }, }, 'loggers': { ... 'werkzeug': { 'handlers': ['console'], 'level': 'DEBUG', 'propagate': True, }, }, } IO Calls and CPU Usage ^^^^^^^^^^^^^^^^^^^^^^ As noted in gh625_ `runserver_plus` can be seen to use a lot of CPU and generate many I/O when idle. This is due to the way Werkzeug_ has implemented the auto reload capability. It supports two ways of doing auto reloading either via `stat polling` or `file system events`. The `stat polling` approach is pretty brute force and continously issues `stat` system calls which causes the CPU and IO load. If possible try to install the Watchdog_ package, this should automatically cause Werkzeug_ to use `file system events` whenever possible. You can read more about this in `Werkzeug documentation `_ You can also increase the poll interval when using `stat polling` from the default of 1 second. This will decrease the CPU load at the expense of file edits taking longer to pick up. This can be set two ways, in the django settings file: RUNSERVERPLUS_POLLER_RELOADER_INTERVAL = 5 or as a commad line argument: $ python manage.py runserver_plus --reloader-interval 5 Debugger PIN ------------ .. epigraph:: The following text about the debugger PIN is taken verbatim from the Werkzeug documentation. -- http://werkzeug.pocoo.org/docs/0.11/debug/#debugger-pin Starting with Werkzeug 0.11 the debugger is additionally protected by a PIN. This is a security helper to make it less likely for the debugger to be exploited in production as it has happened to people to keep the debugger active. The PIN based authentication is enabled by default. When the debugger comes up, on first usage it will prompt for a PIN that is printed to the command line. The PIN is generated in a stable way that is specific to the project. In some situations it might be not possible to generate a stable PIN between restarts in which case an explicit PIN can be provided through the environment variable WERKZEUG_DEBUG_PIN. This can be set to a number and will become the PIN. This variable can also be set to the value off to disable the PIN check entirely. If the PIN is entered too many times incorrectly the server needs to be restarted. This feature is not supposed to entirely secure the debugger. It’s intended to make it harder for an attacker to exploit the debugger. Never enable the debugger in production. .. _gh625: https://github.com/django-extensions/django-extensions/issues/625 .. _Werkzeug: http://werkzeug.pocoo.org/ .. _Watchdog: https://pypi.python.org/pypi/watchdog django-extensions-1.8.1/docs/shell_plus.rst000066400000000000000000000123431312717465700210340ustar00rootroot00000000000000shell_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 --ipython bpython:: $ ./manage.py shell_plus --bpython ptpython:: $ ./manage.py shell_plus --ptpython Python:: $ ./manage.py shell_plus --plain The default resolution order is: ptpython, 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" It is also 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 In addition to being savable, IPython Notebooks can be updated (while running) to reflect changes in a Django application's code with the menu command `Kernel > Restart`. Configuration ------------- Sometimes, models from your own apps and other people's apps have colliding names, or you may want to completely skip loading an app's models. Here are some examples of how to do that. Note: These settings are only used inside shell_plus and will not affect your environment. :: # Rename the automatic loaded module Messages in the app blog to blog_messages. SHELL_PLUS_MODEL_ALIASES = {'blog': {'Messages': 'blog_messages'},} :: # Prefix all automatically loaded models in the app blog with myblog. SHELL_PLUS_APP_PREFIXES = {'blog': 'myblog',} :: # 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. When referencing nested modules, e.g. `somepackage.someapp.models.somemodel`, omit the package name and the reference to `models`. For example: :: SHELL_PLUS_DONT_LOAD = ['someapp.somemodel', ] # This works SHELL_PLUS_DONT_LOAD = ['somepackage.someapp.models.somemodel', ] # This does NOT work It is possible to ignore autoloaded modules when using manage.py, like:: $ ./manage.py shell_plus --dont-load app1 --dont-load app2.module1 Commandline parameters and settings in the configuration file are merged, so you can safely append modules to ignore from the commandline for one-time usage. There are two settings that you can use to pass your custom options to the IPython Notebook in your Django settings. The first one is ``NOTEBOOK_ARGUMENTS`` that can be used to hold those options that available via:: $ ipython notebook -h For example:: NOTEBOOK_ARGUMENTS = [ '--ip', 'x.x.x.x', '--port', 'xx', ] Another one is ``IPYTHON_ARGUMENTS`` that for those options that available via:: $ ipython -h 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 the django-extensions' default notebook extension or copy its auto-loading code 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 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 your 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. Database application signature ------------------------------ If using PostgreSQL the ``application_name`` is set by default to ``django_shell`` to help identify queries made under shell_plus. SQL queries ------------------------- It is possible to print SQL queries as they're executed in shell_plus like:: $ ./manage.py shell_plus --print-sql You can also set the configuration option SHELL_PLUS_PRINT_SQL to omit the above command line option. :: # print SQL queries in shell_plus SHELL_PLUS_PRINT_SQL = True django-extensions-1.8.1/docs/sqlcreate.rst000066400000000000000000000022571312717465700206500ustar00rootroot00000000000000sqlcreate ========== :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). Ultimately you want to pipe it into the database shell command of your choice. If there were 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. However, 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. django-extensions-1.8.1/docs/sqldiff.rst000066400000000000000000000020571312717465700203130ustar00rootroot00000000000000sqldiff ======= :synopsis: Prints the ALTER TABLE statements 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. Its purpose is to show the current differences as a way to check or debug 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! :-) Exit Codes ---------- Exit status is 0 if inputs are the same, 1 if different, 2 if trouble. Example Usage ------------- :: # View SQL differences for all installed applications $ ./manage.py sqldiff -a :: # View SQL differences for all installed applications using text instead of SQL $ ./manage.py sqldiff -a -t django-extensions-1.8.1/docs/sqldsn.rst000066400000000000000000000014501312717465700201630ustar00rootroot00000000000000sqldsn ====== :synopsis: Prints Data Source Name connection string on stdout Supported Databases ------------------- Currently the following databases are supported: * PostgreSQL (psycopg2 or postgis) * Sqlite3 * MySQL Patches to support other databases are welcome! :-) Exit Codes ---------- Exit status is 0 Example Usage ------------- :: # Prints the DSN for the default database $ ./manage.py sqldsn :: # Prints the DSN for all databases $ ./manage.py sqldsn --all :: # Print the DSN for database named 'slave' $ ./manage.py sqldsn --router=slave :: # Print all DSN styles available for the default database $ ./manage.py sqldsn --style=all :: # Create .pgpass file for default database by using the quiet option $ ./manage.py sqldsn -q --style=pgpass > .pgpass django-extensions-1.8.1/docs/sync_s3.rst000066400000000000000000000040331312717465700202400ustar00rootroot00000000000000sync_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. * upload only media or static files. * use any other provider compatible with Amazon S3. * set other than 'public-read' ACL. 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 :: # Upload only media files to a S3 compatible provider into the bucket 'mybucket' and set private file ACLs $ ./manage.py sync_s3 mybucket --media-only --s3host=cs.example.com --acl=private Required libraries and settings ------------------------------- This management command requires the boto library and was tested with version 1.4c: https://github.com/boto/boto It also requires an account with Amazon Web Services (AWS) and the AWS S3 keys. Bucket name is required and cannot be empty. The keys and bucket name are added to your settings.py file, for example:: # settings.py AWS_ACCESS_KEY_ID = '' AWS_SECRET_ACCESS_KEY = '' AWS_BUCKET_NAME = 'bucket' Optional settings ----------------- It is possible to customize sync_s3 directly from django settings file, for example:: # settings.py AWS_S3_HOST = 'cs.example.com' AWS_DEFAULT_ACL = 'private' SYNC_S3_PREFIX = 'some_prefix' FILTER_LIST = 'dir1, dir2' AWS_CLOUDFRONT_DISTRIBUTION = 'E27LVI50CSW06W' SYNC_S3_RENAME_GZIP_EXT = '.gz' django-extensions-1.8.1/docs/validate_templates.rst000066400000000000000000000016571312717465700225370ustar00rootroot00000000000000validate_templates ================== :synopsis: Checks templates on syntax or compile errors. Options ------- verbosity ~~~~~~~~~ A higher verbosity level will print out all the files that are processed instead of only the ones 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 by default directly from the settings file. This can be useful for situations where TEMPLATE DIRS is dynamically generated or switched in middleware, or when you have other template dirs for external applications like celery, and you want to check those as well. Usage Example ------------- ./manage.py validate_templates django-extensions-1.8.1/manage.py000077500000000000000000000004311312717465700170000ustar00rootroot00000000000000#!/usr/bin/env python # -*- coding: utf-8 -*- import os import sys if __name__ == "__main__": os.environ.setdefault("DJANGO_SETTINGS_MODULE", "tests.testapp.settings") from django.core.management import execute_from_command_line execute_from_command_line(sys.argv) django-extensions-1.8.1/setup.cfg000066400000000000000000000006741312717465700170250ustar00rootroot00000000000000[bdist_wheel] universal = 1 [flake8] ignore=E128,E501,E731 exclude=venv/ [tool:pytest] norecursedirs=venv* .tox .eggs build dist django_extensions.egg-info django_extensions/mongodb addopts = --doctest-modules --ignore=django_extensions/db/fields/encrypted.py [isort] combine_as_imports = true default_section = THIRDPARTY include_trailing_comma = true known_third_party = django known_first_party = django_extensions multi_line_output = 5 django-extensions-1.8.1/setup.py000066400000000000000000000121261312717465700167110ustar00rootroot00000000000000# -*- coding: utf-8 -*- """ Based entirely on Django's own ``setup.py``. """ import os import sys from distutils.command.install import INSTALL_SCHEMES from distutils.command.install_data import install_data try: from setuptools import setup except ImportError: from distutils.core import setup # NOQA try: from setuptools.command.test import test as TestCommand class PyTest(TestCommand): user_options = [('pytest-args=', 'a', "Arguments to pass into py.test")] def initialize_options(self): TestCommand.initialize_options(self) self.pytest_args = 'tests django_extensions --ds=tests.testapp.settings --cov=django_extensions' def finalize_options(self): TestCommand.finalize_options(self) self.test_args = [] self.test_suite = True def run_tests(self): import shlex import pytest errno = pytest.main(shlex.split(self.pytest_args)) sys.exit(errno) except ImportError: PyTest = None 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} if PyTest: cmdclasses['test'] = PyTest 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, package_data = [], {} 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 PEP 3147 cache dirs and those whose names start with '.' dirnames[:] = [d for d in dirnames if not d.startswith('.') and d != '__pycache__'] parts = fullsplit(dirpath) package_name = '.'.join(parts) if '__init__.py' in filenames: packages.append(package_name) elif filenames: relative_path = [] while '.'.join(parts) not in packages: relative_path.append(parts.pop()) relative_path.reverse() path = os.path.join(*relative_path) package_files = package_data.setdefault('.'.join(parts), []) package_files.extend([os.path.join(path, 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='MIT License', platforms=['any'], packages=packages, cmdclass=cmdclasses, package_data=package_data, install_requires=['six>=1.2'], tests_require=[ 'Django', 'shortuuid', 'python-dateutil', 'pytest', 'pytest-django', 'pytest-cov', 'tox', 'mock', 'vobject' ], classifiers=[ 'Development Status :: 5 - Production/Stable', 'Environment :: Web Environment', 'Framework :: Django', 'Framework :: Django :: 1.8', 'Framework :: Django :: 1.9', 'Framework :: Django :: 1.10', 'Intended Audience :: Developers', 'License :: OSI Approved :: MIT License', 'Operating System :: OS Independent', 'Programming Language :: Python', 'Programming Language :: Python :: 2', 'Programming Language :: Python :: 3', 'Programming Language :: Python :: Implementation :: PyPy', 'Topic :: Utilities', ], ) django-extensions-1.8.1/tests/000077500000000000000000000000001312717465700163375ustar00rootroot00000000000000django-extensions-1.8.1/tests/__init__.py000066400000000000000000000004031312717465700204450ustar00rootroot00000000000000# -*- coding: utf-8 -*- from __future__ import unicode_literals try: from unittest import mock except ImportError: import mock force_color_support = mock.patch('django.core.management.color.supports_color', autospec=True, side_effect=lambda: True) django-extensions-1.8.1/tests/management/000077500000000000000000000000001312717465700204535ustar00rootroot00000000000000django-extensions-1.8.1/tests/management/__init__.py000066400000000000000000000000001312717465700225520ustar00rootroot00000000000000django-extensions-1.8.1/tests/management/commands/000077500000000000000000000000001312717465700222545ustar00rootroot00000000000000django-extensions-1.8.1/tests/management/commands/__init__.py000066400000000000000000000000001312717465700243530ustar00rootroot00000000000000django-extensions-1.8.1/tests/management/commands/error_raising_command.py000066400000000000000000000003451312717465700271730ustar00rootroot00000000000000# -*- coding: utf-8 -*- from django_extensions.management.base import LoggingBaseCommand class Command(LoggingBaseCommand): help = 'Test error' def handle(self, *args, **options): raise Exception("Test Error") django-extensions-1.8.1/tests/management/commands/test_export_emails.py000066400000000000000000000102711312717465700265410ustar00rootroot00000000000000# -*- coding: utf-8 -*- from __future__ import unicode_literals from django.core.management import call_command from django_extensions.management.commands.export_emails import Command, full_name import pytest from tests.testapp.settings import DATABASES @pytest.fixture(autouse=True) def custom_djsettings(settings): # noqa """Custom django settings to avoid warnings in stdout""" settings.TEMPLATE_DEBUG = False settings.DEBUG = False return settings @pytest.fixture(scope='module') def django_db_setup(): # noqa """Select default database for testing""" settings.DATABASES = DATABASES # noqa @pytest.fixture(scope='module') # noqa def django_db_setup(django_db_setup, django_db_blocker): # noqa """Load to database a set of users, create for export emails command testing""" with django_db_blocker.unblock(): # noqa call_command('loaddata', 'group.json') call_command('loaddata', 'user.json') @pytest.mark.django_db() def test_do_export_emails_stdout_start(capsys): """Testing export_emails command without args.stdout starts.""" export_emails = Command() export_emails.run_from_argv(['manage.py', 'export_emails']) out, err = capsys.readouterr() assert out.startswith('"Mijaíl Bulgakóv') @pytest.mark.django_db() def test_do_export_emails_stdout_end(capsys): """Testing export_emails command without args.stdout end.""" export_emails = Command() export_emails.run_from_argv(['manage.py', 'export_emails']) out, err = capsys.readouterr() assert '"Frédéric Mistral" ;\n\n' in out @pytest.mark.django_db() def test_do_export_emails_format_email(capsys): """Testing python manage.py export_emails -f emails""" export_emails = Command() export_emails.run_from_argv(['manage.py', 'export_emails', '--format=emails']) out, err = capsys.readouterr() assert 'frederic_mistral@gmail.com' in out @pytest.mark.django_db() def test_do_export_emails_address(capsys): """Testing python manage export_emails -f address""" export_emails = Command() export_emails.run_from_argv(['manage.py', 'export_emails', '--format=address']) out, err = capsys.readouterr() assert '"Frédéric Mistral"' in out @pytest.mark.django_db() def test_do_export_emails_format_google(capsys): """Testing python manage export_emails -f google""" export_emails = Command() export_emails.run_from_argv(['manage.py', 'export_emails', '--format=google']) out, err = capsys.readouterr() assert out.startswith('Name,Email') @pytest.mark.django_db() def test_do_export_emails_format_linkedin(capsys): """Testing python manage.py export_emails -f linkedin""" export_emails = Command() export_emails.run_from_argv(['manage.py', 'export_emails', '--format=linkedin']) out, err = capsys.readouterr() assert out.startswith('First Name,') assert 'Gabriel Garcia,Marquéz' in out @pytest.mark.django_db() def test_do_export_emails_format_outlook(capsys): """Testing python manage.py export_emails -f outlook""" export_emails = Command() export_emails.run_from_argv(['manage.py', 'export_emails', '--format=outlook']) out, err = capsys.readouterr() assert out.startswith('Name,E-mail Address') assert 'frederic_mistral@gmail.com' in out @pytest.mark.skipif("sys.version_info < (3, 0)", reason='issues with vobject library on PY2x') @pytest.mark.django_db() def test_do_export_emails_format_vcard_start(capsys): """Testing python manage.py export_emails -f vcard""" export_emails = Command() export_emails.run_from_argv(['manage.py', 'export_emails', '--format=vcard']) out, err = capsys.readouterr() assert 'N:Bulgakóv;Mijaíl;;;' in out assert out.startswith('BEGIN:VCARD') @pytest.mark.django_db() def test_full_name(): """Test, getting full name / username""" fake_user = {'first_name': 'Allan', 'last_name': 'Poe', 'username': 'allan_poe'} name = full_name(**fake_user) assert name == "{u} {l}".format(u=fake_user['first_name'], l=fake_user['last_name']) fake_user2 = {'first_name': '', 'last_name': '', 'username': 'niccolas'} name2 = full_name(**fake_user2) assert name2 == fake_user2['username'] django-extensions-1.8.1/tests/management/commands/test_sync_s3.py000066400000000000000000000063271312717465700252560ustar00rootroot00000000000000# -*- coding: utf-8 -*- import django_extensions.management.commands.sync_s3 import os import shutil from django.test import TestCase from django.test.utils import override_settings from django_extensions.management.commands.sync_s3 import Command class SyncS3Tests(TestCase): def setUp(self): current_dir = os.path.dirname(os.path.abspath(__file__)) media_root = os.path.join(current_dir, "media") os.mkdir(media_root) test_dirs = [os.path.join(media_root, "testdir1"), os.path.join(media_root, "testdir2"), os.path.join(media_root, "testdir3"), os.path.join(media_root, "testdir4"), os.path.join(media_root, "testsamenamedir"), ] for dir in test_dirs: os.mkdir(dir) test_sub_dir = os.path.join(media_root, "testdir2", "testsubdir1") os.mkdir(test_sub_dir) test_sub_dir_base = os.path.join(media_root, "testdir1") test_sub_dirs = [os.path.join(test_sub_dir_base, "testsubdir1"), os.path.join(test_sub_dir_base, "testsubdir2"), os.path.join(test_sub_dir_base, "testsubdir3"), os.path.join(test_sub_dir_base, "testsubdir4"), ] for dir in test_sub_dirs: os.mkdir(dir) test_sub_dir_base2 = os.path.join(media_root, "testdir3") test_sub_dirs_2 = [os.path.join(test_sub_dir_base2, "testsubdir1"), os.path.join(test_sub_dir_base2, "testsubdir2"), os.path.join(test_sub_dir_base2, "testsubdir3"), os.path.join(test_sub_dir_base2, "testsubdir4"), os.path.join(test_sub_dir_base2, "testsamenamedir"), ] for dir in test_sub_dirs_2: os.mkdir(dir) def tearDown(self): current_dir = os.path.dirname(os.path.abspath(__file__)) media_root = os.path.join(current_dir, "media") shutil.rmtree(media_root) @override_settings(MEDIA_ROOT=os.path.join(os.path.dirname(os.path.abspath(__file__)), "media")) @override_settings(STATIC_ROOT=os.path.join(os.path.dirname(os.path.abspath(__file__)), "static")) @override_settings(AWS_ACCESS_KEY_ID="FAKE_KEY") @override_settings(AWS_SECRET_ACCESS_KEY="FAKE_SECRET_KEY") @override_settings(AWS_BUCKET_NAME="FAKE_BUCKET_NAME") def test_sync_s3_dir_exclusions(self): sync_s3_command = Command() django_extensions.management.commands.sync_s3.HAS_BOTO = True try: sync_s3_command.run_from_argv( ["manage.py", "sync_s3", "--acl=authenticated-read", "--filter-list=testsamenamedir,testdir1"]) except: # Exception is expected, we're not actually attempting to connect to S3 self.assertEqual(0, 0) for directory in sync_s3_command.DIRECTORIES: for root, dirs, files in os.walk(directory): sync_s3_command.upload_s3(("FAKE_BUCKET", "FAKE_KEY", "FAKE_BUCKET_NAME", directory), root, files, dirs) dir_name = os.path.basename(root) if dir_name == "testdir1": self.assertEqual(len(dirs), 0) elif dir_name == "testdir2": self.assertEqual(len(dirs), 1) elif dir_name == "testdir3": self.assertEqual(len(dirs), 5) django-extensions-1.8.1/tests/management/test_modelviz.py000066400000000000000000000010411312717465700237110ustar00rootroot00000000000000# -*- coding: utf-8 -*- from django.test import SimpleTestCase from django_extensions.management.modelviz import generate_graph_data class ModelVizTests(SimpleTestCase): def test_generate_graph_data_can_render_label(self): app_labels = ['auth'] data = generate_graph_data(app_labels) models = data['graphs'][0]['models'] user_data = [x for x in models if x['name'] == 'User'][0] relation_labels = [x['label'] for x in user_data['relations']] self.assertIn("groups (user)", relation_labels) django-extensions-1.8.1/tests/test_autoslug_fields.py000066400000000000000000000126451312717465700231510ustar00rootroot00000000000000# -*- coding: utf-8 -*- import pytest import django from django.db import migrations, models from django.db.migrations.writer import MigrationWriter from django.test import TestCase from django.utils import six from django.utils.encoding import force_bytes import django_extensions # noqa from django_extensions.db.fields import AutoSlugField from .testapp.models import ChildSluggedTestModel, SluggedTestModel, \ FKSluggedTestModel, FKSluggedTestModelCallable, \ ModelMethodSluggedTestModel @pytest.mark.usefixtures("admin_user") class AutoSlugFieldTest(TestCase): def tearDown(self): super(AutoSlugFieldTest, self).tearDown() SluggedTestModel.objects.all().delete() def test_auto_create_slug(self): m = SluggedTestModel(title='foo') m.save() self.assertEqual(m.slug, 'foo') def test_auto_create_next_slug(self): m = SluggedTestModel(title='foo') m.save() m = SluggedTestModel(title='foo') m.save() self.assertEqual(m.slug, 'foo-2') def test_auto_create_slug_with_number(self): m = SluggedTestModel(title='foo 2012') m.save() self.assertEqual(m.slug, 'foo-2012') def test_auto_update_slug_with_number(self): m = SluggedTestModel(title='foo 2012') m.save() m.save() self.assertEqual(m.slug, 'foo-2012') def test_update_slug(self): m = SluggedTestModel(title='foo') m.save() self.assertEqual(m.slug, 'foo') # 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.title, 'foo') self.assertEqual(m.slug, 'foo-2012') # Check slug is not overwrite m.title = 'bar' m.save() self.assertEqual(m.title, 'bar') self.assertEqual(m.slug, 'foo-2012') def test_simple_slug_source(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 test_empty_slug_source(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 test_callable_slug_source(self): m = ModelMethodSluggedTestModel(title='-foo') m.save() self.assertEqual(m.slug, 'the-title-is-foo') n = ModelMethodSluggedTestModel(title='-foo') n.save() self.assertEqual(n.slug, 'the-title-is-foo-2') n.save() self.assertEqual(n.slug, 'the-title-is-foo-2') def test_inheritance_creates_next_slug(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') def test_foreign_key_populate_from_field(self): m_fk = SluggedTestModel(title='foo') m_fk.save() m = FKSluggedTestModel(related_field=m_fk) m.save() self.assertEqual(m.slug, 'foo') def test_foreign_key_populate_from_callable(self): m_fk = ModelMethodSluggedTestModel(title='foo') m_fk.save() m = FKSluggedTestModelCallable(related_field=m_fk) m.save() self.assertEqual(m.slug, 'the-title-is-foo') class MigrationTest(TestCase): def safe_exec(self, string, value=None): l = {} try: exec(force_bytes(string), globals(), l) except Exception as e: if value: self.fail("Could not exec %r (from value %r): %s" % (string.strip(), value, e)) else: self.fail("Could not exec %r: %s" % (string.strip(), e)) return l def test_17_migration(self): """ Tests making migrations with Django's migration framework """ fields = { 'autoslugfield': AutoSlugField(populate_from='otherfield'), } migration = type(str("Migration"), (migrations.Migration,), { "operations": [ migrations.CreateModel("MyModel", tuple(fields.items()), {'populate_from': 'otherfield'}, (models.Model,)), ], }) writer = MigrationWriter(migration) output = writer.as_string() # It should NOT be unicode. if django.VERSION < (1, 11): self.assertIsInstance(output, six.binary_type, "Migration as_string returned unicode") else: # As of Django 1.11 MigrationWriter.as_string returns unicode not bytes self.assertIsInstance(output, six.text_type, "Migration as_string returned bytes") # We don't test the output formatting - that's too fragile. # Just make sure it runs for now, and that things look alright. result = self.safe_exec(output) self.assertIn("Migration", result) django-extensions-1.8.1/tests/test_clean_pyc.py000066400000000000000000000045501312717465700217110ustar00rootroot00000000000000# -*- coding: utf-8 -*- import fnmatch import os import shutil import six from django.core.management import call_command from django.test import TestCase class CleanPycTests(TestCase): def setUp(self): self.project_root = os.path.join('tests', 'testapp') self._settings = os.environ.get('DJANGO_SETTINGS_MODULE') os.environ['DJANGO_SETTINGS_MODULE'] = 'django_extensions.settings' def tearDown(self): if self._settings: os.environ['DJANGO_SETTINGS_MODULE'] = self._settings def _find_pyc(self, path): pyc_glob = [] for root, dirnames, filenames in os.walk(path): for filename in fnmatch.filter(filenames, '*.pyc'): pyc_glob.append(os.path.join(root, filename)) return pyc_glob def test_removes_pyc_files(self): with self.settings(BASE_DIR=self.project_root): call_command('compile_pyc') pyc_glob = self._find_pyc(self.project_root) self.assertTrue(len(pyc_glob) > 0) with self.settings(BASE_DIR=self.project_root): call_command('clean_pyc') pyc_glob = self._find_pyc(self.project_root) self.assertEqual(len(pyc_glob), 0) def test_takes_path(self): out = six.StringIO() project_root = os.path.join('tests', 'testapp') call_command('compile_pyc', path=project_root) pyc_glob = self._find_pyc(project_root) self.assertTrue(len(pyc_glob) > 0) call_command('clean_pyc', verbosity=2, path=project_root, stdout=out) output = out.getvalue().splitlines() self.assertEqual(sorted(pyc_glob), sorted(output)) def test_removes_pyo_files(self): out = six.StringIO() project_root = os.path.join('tests', 'testapp') call_command('compile_pyc', path=project_root) pyc_glob = self._find_pyc(project_root) self.assertTrue(len(pyc_glob) > 0) # Create some fake .pyo files since we can't force them to be created. pyo_glob = [] for fn in pyc_glob: pyo = '%s.pyo' % os.path.splitext(fn)[0] shutil.copyfile(fn, pyo) pyo_glob.append(pyo) call_command('clean_pyc', verbosity=2, path=project_root, optimize=True, stdout=out) output = out.getvalue().splitlines() self.assertEqual(sorted(pyc_glob + pyo_glob), sorted(output)) django-extensions-1.8.1/tests/test_color.py000066400000000000000000000013311312717465700210640ustar00rootroot00000000000000# -*- coding: utf-8 -*- from __future__ import unicode_literals from django.test import SimpleTestCase from django_extensions.management import color from . import force_color_support class ColorTest(SimpleTestCase): def test_no_style(self): with force_color_support: style = color.no_style().MODULE_NAME text = 'csv' styled_text = style(text) self.assertEqual(text, styled_text) def test_color_style(self): with force_color_support: style = color.color_style().MODULE_NAME text = 'antigravity' styled_text = style(text) self.assertIn(text, styled_text) self.assertNotEqual(text, styled_text) django-extensions-1.8.1/tests/test_compat.py000066400000000000000000000015511312717465700212350ustar00rootroot00000000000000# -*- coding: utf-8 -*- from django.test import TestCase, override_settings from django_extensions.compat import get_template_setting class GetTemplateSettingTests(TestCase): @override_settings(TEMPLATES=[{'DIRS': ['asdf']}]) def test_new_dir_template(self): setting = get_template_setting('DIRS') self.assertEqual(setting, ['asdf']) @override_settings(TEMPLATES=None, TEMPLATES_DIRS=['asdf']) def test_old_dir_template(self): setting = get_template_setting('DIRS') self.assertEqual(setting, ['asdf']) @override_settings(TEMPLATES=None) def test_old_dir_missing(self): setting = get_template_setting('DIRS', 'default') self.assertEqual(setting, 'default') def test_default_setting(self): setting = get_template_setting('ASDF', 'default') self.assertEqual(setting, 'default') django-extensions-1.8.1/tests/test_compile_pyc.py000066400000000000000000000037201312717465700222550ustar00rootroot00000000000000# -*- coding: utf-8 -*- import fnmatch import os import six from django.core.management import call_command from django.test import TestCase class CompilePycTests(TestCase): def setUp(self): self.project_root = os.path.join('tests', 'testapp') self._settings = os.environ.get('DJANGO_SETTINGS_MODULE') os.environ['DJANGO_SETTINGS_MODULE'] = 'django_extensions.settings' def tearDown(self): if self._settings: os.environ['DJANGO_SETTINGS_MODULE'] = self._settings def _find_pyc(self, path, mask='*.pyc'): pyc_glob = [] for root, dirs, filenames in os.walk(path): for filename in fnmatch.filter(filenames, mask): pyc_glob.append(os.path.join(root, filename)) return pyc_glob def test_compiles_pyc_files(self): with self.settings(BASE_DIR=self.project_root): call_command('clean_pyc') pyc_glob = self._find_pyc(self.project_root) self.assertEqual(len(pyc_glob), 0) with self.settings(BASE_DIR=self.project_root): call_command('compile_pyc') pyc_glob = self._find_pyc(self.project_root) self.assertTrue(len(pyc_glob) > 0) with self.settings(BASE_DIR=self.project_root): call_command('clean_pyc') def test_takes_path(self): out = six.StringIO() with self.settings(BASE_DIR=""): call_command('clean_pyc', path=self.project_root) pyc_glob = self._find_pyc(self.project_root) self.assertEqual(len(pyc_glob), 0) with self.settings(BASE_DIR=""): call_command('compile_pyc', verbosity=2, path=self.project_root, stdout=out) expected = ['Compiling %s...' % fn for fn in sorted(self._find_pyc(self.project_root, mask='*.py'))] output = out.getvalue().splitlines() self.assertEqual(expected, sorted(output)) with self.settings(BASE_DIR=""): call_command('clean_pyc', path=self.project_root) django-extensions-1.8.1/tests/test_create_command.py000066400000000000000000000011331312717465700227070ustar00rootroot00000000000000# -*- coding: utf-8 -*- import os from django.core.management import call_command from django.test import TestCase class CreateCommandTests(TestCase): def setUp(self): self.project_root = os.path.join('tests', 'testapp') self._settings = os.environ.get('DJANGO_SETTINGS_MODULE') os.environ['DJANGO_SETTINGS_MODULE'] = 'django_extensions.settings' def tearDown(self): if self._settings: os.environ['DJANGO_SETTINGS_MODULE'] = self._settings def test_create_command_dry_run(self): call_command('create_command', 'testapp', '--dry-run') django-extensions-1.8.1/tests/test_dumpscript.py000066400000000000000000000046161312717465700221510ustar00rootroot00000000000000# -*- coding: utf-8 -*- import ast import sys import six from django.core.management import call_command from django.test import TestCase from .testapp.models import Name, Note, Person class DumpScriptTests(TestCase): def setUp(self): sys.stdout = six.StringIO() sys.stderr = six.StringIO() def test_runs(self): # lame test...does it run? n = Name(name='Gabriel') n.save() call_command('dumpscript', 'django_extensions') 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', 'django_extensions', 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', 'django_extensions', 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', 'django_extensions', stdout=tmp_out) ast_syntax_tree = ast.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() django-extensions-1.8.1/tests/test_encrypted_fields.py000066400000000000000000000240131312717465700232730ustar00rootroot00000000000000# -*- coding: utf-8 -*- import tempfile from contextlib import contextmanager import pytest from django.conf import settings from django.db import connection, models from django.test import TestCase from .testapp.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 # Locations of both private and public keys. KEY_LOCS = {} @pytest.fixture(scope="class") def keyczar_keys(request): # If KeyCzar is available, set up the environment. if keyczar_active: # 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 # cleanup crypto key temp dirs def cleanup(): import shutil for name, path in KEY_LOCS.items(): shutil.rmtree(path) request.addfinalizer(cleanup) @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 https://dynamic-models.readthedocs.io/en/latest/ and https://docs.djangoproject.com/en/dev/topics/db/models/ #differences-between-proxy-inheritance-and-unmanaged-models """ 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__': 'tests.testapp.models', 'Meta': type('Meta', (object, ), { 'managed': False, 'db_table': Secret._meta.db_table }) } yield type('Secret', (models.Model, ), attrs) except: raise # Reraise any exceptions. @pytest.mark.skipif(keyczar_active is False, reason="Encrypted fields needs that keyczar is installed") @pytest.mark.usefixtures("admin_user", "keyczar_keys") class EncryptedFieldsTestCase(TestCase): def test_char_field_create(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) def test_char_field_read(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) def test_text_field_create(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) def test_text_field_read(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) def test_cannot_decrypt(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)) def test_unacceptable_purpose(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 def test_decryption_forbidden(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)) def test_encrypt_public_decrypt_private(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) django-extensions-1.8.1/tests/test_find_template.py000066400000000000000000000011251312717465700225620ustar00rootroot00000000000000# -*- coding: utf-8 -*- import os from django.core.management import call_command from django.test import TestCase class FindTemplateTests(TestCase): def setUp(self): self.project_root = os.path.join('tests', 'testapp') self._settings = os.environ.get('DJANGO_SETTINGS_MODULE') os.environ['DJANGO_SETTINGS_MODULE'] = 'django_extensions.settings' def tearDown(self): if self._settings: os.environ['DJANGO_SETTINGS_MODULE'] = self._settings def test_finding_template(self): call_command('find_template', 'admin/change_form.html') django-extensions-1.8.1/tests/test_json_field.py000066400000000000000000000073141312717465700220710ustar00rootroot00000000000000# -*- coding: utf-8 -*- import six from django.test import TestCase from .testapp.models import JSONFieldTestModel from django_extensions.db.fields.json import ( dumps, JSONField, JSONDict, JSONList ) class JsonFieldTest(TestCase): def test_char_field_create(self): j = JSONFieldTestModel.objects.create(a=6, j_field=dict(foo='bar')) self.assertEqual(j.a, 6) self.assertEqual(j.j_field, {'foo': 'bar'}) def test_char_field_get_or_create(self): j, created = JSONFieldTestModel.objects.get_or_create( a=6, j_field=dict(foo='bar')) self.assertTrue(created) self.assertEqual(j.a, 6) self.assertEqual(j.j_field, {'foo': 'bar'}) j, created = JSONFieldTestModel.objects.get_or_create( a=6, j_field=dict(foo='bar')) self.assertFalse(created) self.assertEqual(j.a, 6) self.assertEqual(j.j_field, {'foo': 'bar'}) def test_default(self): j = JSONFieldTestModel.objects.create(a=1) self.assertEqual(j.j_field, {}) def test_default_mutable(self): j1 = JSONFieldTestModel.objects.create(a=1) self.assertEqual(j1.j_field, {}) j2 = JSONFieldTestModel.objects.create(a=1) self.assertEqual(j2.j_field, {}) self.assertIsNot(j1.j_field, j2.j_field) def test_get_default(self): j_field = JSONField() value = j_field.get_default() self.assertEqual(value, {}) self.assertIsInstance(value, JSONDict) j_field = JSONField(default={}) value = j_field.get_default() self.assertEqual(value, {}) self.assertIsInstance(value, JSONDict) j_field = JSONField(default='{}') value = j_field.get_default() self.assertEqual(value, {}) self.assertIsInstance(value, JSONDict) j_field = JSONField(default=[{}]) value = j_field.get_default() self.assertEqual(value, [{}]) self.assertIsInstance(value, JSONList) j_field = JSONField(default='[{}]') value = j_field.get_default() self.assertEqual(value, [{}]) self.assertIsInstance(value, JSONList) j_field = JSONField(default=lambda: '{}') value = j_field.get_default() self.assertEqual(value, {}) self.assertIsInstance(value, JSONDict) def test_empty_list(self): j = JSONFieldTestModel.objects.create(a=6, j_field=[]) self.assertIsInstance(j.j_field, list) self.assertEqual(j.j_field, []) def test_float_values(self): """Tests that float values in JSONFields are correctly serialized over repeated saves. Regression test for c382398b, which fixes floats being returned as strings after a second save.""" test_instance = JSONFieldTestModel(a=6, j_field={'test': 0.1}) test_instance.save() test_instance = JSONFieldTestModel.objects.get() test_instance.save() test_instance = JSONFieldTestModel.objects.get() self.assertEqual(test_instance.j_field['test'], 0.1) def test_get_prep_value(self): j_field = JSONField() self.assertEqual( six.u(dumps([{'a': 'a'}])), j_field.get_prep_value(value=[{'a': 'a'}]) ) self.assertEqual( six.u(dumps([{'a': 'a'}])), j_field.get_prep_value(value='[{"a": "a"}]') ) def test_get_db_prep_save(self): j_field = JSONField() self.assertEqual( six.u(dumps([{'a': 'a'}])), j_field.get_db_prep_save(value=[{'a': 'a'}], connection=None) ) self.assertEqual( six.u('[{"a": "a"}]'), j_field.get_db_prep_save(value='[{"a": "a"}]', connection=None) ) django-extensions-1.8.1/tests/test_management_command.py000066400000000000000000000225201312717465700235630ustar00rootroot00000000000000# -*- coding: utf-8 -*- import os import sys import shutil import logging import importlib import django from django.core.management import call_command, find_commands, load_command_class from django.test import TestCase from django.utils.six import StringIO, PY3 from django_extensions.management.modelviz import use_model, generate_graph_data from . import force_color_support 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 = "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_template_tags', 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) class CreateAppTests(TestCase): def test_command(self): if django.VERSION[:2] >= (1, 10): return tmpname = "testapptest" # TODO better temp dir handling tmpdir = "/tmp" tmppath = os.path.join(tmpdir, tmpname) self.assertFalse(os.path.isdir(tmppath)) out = StringIO() try: call_command('create_app', tmpname, parent_path=tmpdir, stdout=out) finally: if os.path.isdir(tmppath): shutil.rmtree(tmppath) output = out.getvalue() self.assertIn("Application '%s' created." % tmpname, output) class AdminGeneratorTests(TestCase): def test_command(self): out = StringIO() call_command('admin_generator', 'django_extensions', stdout=out) output = out.getvalue() self.assertIn("@admin.register(Secret)", output) self.assertIn("class SecretAdmin(admin.ModelAdmin):", output) if PY3: self.assertIn("list_display = ('id', 'name', 'text')", output) self.assertIn("search_fields = ('name',)", output) else: self.assertIn("list_display = (u'id', u'name', u'text')", output) self.assertIn("search_fields = (u'name',)", output) class DescribeFormTests(TestCase): def test_command(self): out = StringIO() call_command('describe_form', 'django_extensions.Secret', stdout=out) output = out.getvalue() self.assertIn("class SecretForm(forms.Form):", output) self.assertRegexpMatches(output, "name = forms.CharField\(.*max_length=255") self.assertRegexpMatches(output, "name = forms.CharField\(.*required=False") self.assertRegexpMatches(output, "name = forms.CharField\(.*label=u?'Name'") self.assertRegexpMatches(output, "text = forms.CharField\(.*required=False") self.assertRegexpMatches(output, "text = forms.CharField\(.*label=u?'Text'") class UpdatePermissionsTests(TestCase): def test_works(self): from django.db import models class PermModel(models.Model): class Meta: app_label = 'django_extensions' permissions = (('test_permission', 'test_permission'),) original_stdout = sys.stdout out = sys.stdout = StringIO() call_command('update_permissions', stdout=out, verbosity=3) sys.stdout = original_stdout self.assertIn("Can change perm model", out.getvalue()) class CommandSignalTests(TestCase): pre = None post = None def test_works(self): from django_extensions.management.signals import post_command, \ pre_command from django_extensions.management.commands.show_template_tags import \ Command def pre(sender, **kwargs): CommandSignalTests.pre = dict(**kwargs) def post(sender, **kwargs): CommandSignalTests.post = dict(**kwargs) pre_command.connect(pre, Command) post_command.connect(post, Command) out = StringIO() call_command('show_template_tags', stdout=out) self.assertIn('args', CommandSignalTests.pre) self.assertIn('kwargs', CommandSignalTests.pre) self.assertIn('args', CommandSignalTests.post) self.assertIn('kwargs', CommandSignalTests.post) self.assertIn('outcome', CommandSignalTests.post) class CommandClassTests(TestCase): def setUp(self): management_dir = os.path.join('django_extensions', 'management') self.commands = find_commands(management_dir) def test_load_commands(self): """Try to load every management command to catch exceptions.""" try: for command in self.commands: load_command_class('django_extensions', command) except Exception as e: self.fail("Can't load command class of {0}\n{1}".format(command, e)) class GraphModelsTests(TestCase): """ Tests for the `graph_models` management command. """ def test_use_model(self): include_models = [ 'NoWildcardInclude', 'Wildcard*InsideInclude', '*WildcardPrefixInclude', 'WildcardSuffixInclude*', '*WildcardBothInclude*' ] exclude_models = [ 'NoWildcardExclude', 'Wildcard*InsideExclude', '*WildcardPrefixExclude', 'WildcardSuffixExclude*', '*WildcardBothExclude*' ] # Any model name should be used if neither include or exclude # are defined. self.assertTrue(use_model( 'SomeModel', None, None )) # Any model name should be allowed if `*` is in `include_models`. self.assertTrue(use_model( 'SomeModel', ['OtherModel', '*', 'Wildcard*Model'], None )) # No model name should be allowed if `*` is in `exclude_models`. self.assertFalse(use_model( 'SomeModel', None, ['OtherModel', '*', 'Wildcard*Model'] )) # Some tests with the `include_models` defined above. self.assertFalse(use_model( 'SomeModel', include_models, None )) self.assertTrue(use_model( 'NoWildcardInclude', include_models, None )) self.assertTrue(use_model( 'WildcardSomewhereInsideInclude', include_models, None )) self.assertTrue(use_model( 'MyWildcardPrefixInclude', include_models, None )) self.assertTrue(use_model( 'WildcardSuffixIncludeModel', include_models, None )) self.assertTrue(use_model( 'MyWildcardBothIncludeModel', include_models, None )) # Some tests with the `exclude_models` defined above. self.assertTrue(use_model( 'SomeModel', None, exclude_models )) self.assertFalse(use_model( 'NoWildcardExclude', None, exclude_models )) self.assertFalse(use_model( 'WildcardSomewhereInsideExclude', None, exclude_models )) self.assertFalse(use_model( 'MyWildcardPrefixExclude', None, exclude_models )) self.assertFalse(use_model( 'WildcardSuffixExcludeModel', None, exclude_models )) self.assertFalse(use_model( 'MyWildcardBothExcludeModel', None, exclude_models )) def test_no_models_dot_py(self): data = generate_graph_data(['testapp_with_no_models_file']) self.assertEqual(len(data['graphs']), 1) model_name = data['graphs'][0]['models'][0]['name'] self.assertEqual(model_name, 'TeslaCar') class ShowUrlsTests(TestCase): """ Tests for the `show_urls` management command. """ def test_color(self): with force_color_support: out = StringIO() call_command('show_urls', stdout=out) self.output = out.getvalue() self.assertIn('\x1b', self.output) def test_no_color(self): with force_color_support: out = StringIO() call_command('show_urls', '--no-color', stdout=out) self.output = out.getvalue() self.assertNotIn('\x1b', self.output) django-extensions-1.8.1/tests/test_models.py000066400000000000000000000030661312717465700212400ustar00rootroot00000000000000# -*- coding: utf-8 -*- from django.test import TestCase from django_extensions.db.models import ActivatorModel from .testapp.models import Post class ActivatorModelTestCase(TestCase): def test_active_includes_active(self): post = Post.objects.create(status=ActivatorModel.ACTIVE_STATUS) active = Post.objects.active() self.assertIn(post, active) post.delete() def test_active_excludes_inactive(self): post = Post.objects.create(status=ActivatorModel.INACTIVE_STATUS) active = Post.objects.active() self.assertNotIn(post, active) post.delete() def test_inactive_includes_inactive(self): post = Post.objects.create(status=ActivatorModel.INACTIVE_STATUS) inactive = Post.objects.inactive() self.assertIn(post, inactive) post.delete() def test_inactive_excludes_active(self): post = Post.objects.create(status=ActivatorModel.ACTIVE_STATUS) inactive = Post.objects.inactive() self.assertNotIn(post, inactive) post.delete() def test_active_is_chainable(self): post = Post.objects.create(title='Foo', status=ActivatorModel.ACTIVE_STATUS) specific_post = Post.objects.filter(title='Foo').active() self.assertIn(post, specific_post) post.delete() def test_inactive_is_chainable(self): post = Post.objects.create(title='Foo', status=ActivatorModel.INACTIVE_STATUS) specific_post = Post.objects.filter(title='Foo').inactive() self.assertIn(post, specific_post) post.delete() django-extensions-1.8.1/tests/test_randomchar_field.py000066400000000000000000000057261312717465700232430ustar00rootroot00000000000000# -*- coding: utf-8 -*- import mock import string import pytest from django.test import TestCase from .testapp.models import ( RandomCharTestModel, RandomCharTestModelUnique, RandomCharTestModelLowercase, RandomCharTestModelUppercase, RandomCharTestModelAlpha, RandomCharTestModelDigits, RandomCharTestModelPunctuation, RandomCharTestModelLowercaseAlphaDigits, RandomCharTestModelUppercaseAlphaDigits, ) class RandomCharFieldTest(TestCase): def testRandomCharField(self): m = RandomCharTestModel() m.save() assert len(m.random_char_field) == 8, m.random_char_field def testRandomCharFieldUnique(self): m = RandomCharTestModelUnique() m.save() assert len(m.random_char_field) == 8, m.random_char_field def testRandomCharFieldLowercase(self): m = RandomCharTestModelLowercase() m.save() for c in m.random_char_field: assert c.islower(), m.random_char_field def testRandomCharFieldUppercase(self): m = RandomCharTestModelUppercase() m.save() for c in m.random_char_field: assert c.isupper(), m.random_char_field def testRandomCharFieldAlpha(self): m = RandomCharTestModelAlpha() m.save() for c in m.random_char_field: assert c.isalpha(), m.random_char_field def testRandomCharFieldDigits(self): m = RandomCharTestModelDigits() m.save() for c in m.random_char_field: assert c.isdigit(), m.random_char_field def testRandomCharFieldPunctuation(self): m = RandomCharTestModelPunctuation() m.save() for c in m.random_char_field: assert c in string.punctuation, m.random_char_field def testRandomCharTestModelLowercaseAlphaDigits(self): m = RandomCharTestModelLowercaseAlphaDigits() m.save() for c in m.random_char_field: assert c.isdigit() or (c.isalpha() and c.islower()), m.random_char_field def testRandomCharTestModelUppercaseAlphaDigits(self): m = RandomCharTestModelUppercaseAlphaDigits() m.save() for c in m.random_char_field: assert c.isdigit() or (c.isalpha() and c.isupper()), m.random_char_field def testRandomCharTestModelDuplicate(self): m = RandomCharTestModelUnique() m.save() with mock.patch('django_extensions.db.fields.RandomCharField.random_char_generator') as func: func.return_value = iter([m.random_char_field, 'aaa']) m = RandomCharTestModelUnique() m.save() assert m.random_char_field == 'aaa' def testRandomCharTestModelAsserts(self): with mock.patch('django_extensions.db.fields.get_random_string') as mock_sample: mock_sample.return_value = 'aaa' m = RandomCharTestModelUnique() m.save() m = RandomCharTestModelUnique() with pytest.raises(RuntimeError): m.save() django-extensions-1.8.1/tests/test_runscript.py000066400000000000000000000020341312717465700220000ustar00rootroot00000000000000# -*- coding: utf-8 -*- from django.core.management import call_command from django.test import TestCase import six import sys class RunScriptTests(TestCase): def setUp(self): sys.stdout = six.StringIO() sys.stderr = six.StringIO() def test_runs(self): # lame test...does it run? call_command('runscript', 'sample_script', verbosity=2) self.assertIn("Found script 'tests.testapp.scripts.sample_script'", sys.stdout.getvalue()) self.assertIn("Running script 'tests.testapp.scripts.sample_script'", sys.stdout.getvalue()) def test_runs_appconfig(self): with self.modify_settings(INSTALLED_APPS={ 'append': 'tests.testapp.apps.TestAppConfig', 'remove': 'tests.testapp', }): call_command('runscript', 'sample_script', verbosity=2) self.assertIn("Found script 'tests.testapp.scripts.sample_script'", sys.stdout.getvalue()) self.assertIn("Running script 'tests.testapp.scripts.sample_script'", sys.stdout.getvalue()) django-extensions-1.8.1/tests/test_shortuuid_field.py000066400000000000000000000023531312717465700231440ustar00rootroot00000000000000# -*- coding: utf-8 -*- import six from django.test import TestCase from .testapp.models import ( ShortUUIDTestAgregateModel, ShortUUIDTestManyToManyModel, ShortUUIDTestModel_field, ShortUUIDTestModel_pk, ) class ShortUUIDFieldTest(TestCase): def test_UUID_field_create(self): j = ShortUUIDTestModel_field.objects.create(a=6, uuid_field=six.u('vytxeTZskVKR7C7WgdSP3d')) self.assertEqual(j.uuid_field, six.u('vytxeTZskVKR7C7WgdSP3d')) def test_UUID_field_pk_create(self): j = ShortUUIDTestModel_pk.objects.create(uuid_field=six.u('vytxeTZskVKR7C7WgdSP3d')) self.assertEqual(j.uuid_field, six.u('vytxeTZskVKR7C7WgdSP3d')) self.assertEqual(j.pk, six.u('vytxeTZskVKR7C7WgdSP3d')) def test_UUID_field_pk_agregate_create(self): j = ShortUUIDTestAgregateModel.objects.create(a=6) self.assertEqual(j.a, 6) self.assertIsInstance(j.pk, six.string_types) self.assertTrue(len(j.pk) < 23) def test_UUID_field_manytomany_create(self): j = ShortUUIDTestManyToManyModel.objects.create(uuid_field=six.u('vytxeTZskVKR7C7WgdSP3e')) self.assertEqual(j.uuid_field, six.u('vytxeTZskVKR7C7WgdSP3e')) self.assertEqual(j.pk, six.u('vytxeTZskVKR7C7WgdSP3e')) django-extensions-1.8.1/tests/test_template_rendering.py000066400000000000000000000007451312717465700236260ustar00rootroot00000000000000# -*- coding: utf-8 -*- from django.test import TestCase from django.template import Context, Template class TemplateRenderingTests(TestCase): def setUp(self): self.ctx = Context({ 'worldvar': 'world', }) def test_simple_template(self): self.assertEqual(Template("hello world").render(self.ctx), "hello world") def test_simple_context(self): self.assertEqual(Template("hello {{ worldvar }}").render(self.ctx), "hello world") django-extensions-1.8.1/tests/test_templatetags.py000066400000000000000000000011271312717465700224430ustar00rootroot00000000000000# -*- coding: utf-8 -*- import six from django.test import TestCase from django_extensions.templatetags.widont import widont, widont_html # TODO: these tests are far from having decent test coverage class TemplateTagsTests(TestCase): def test_widont(self): self.assertEqual(widont('Test Value'), 'Test Value') self.assertEqual(widont(six.u('Test Value')), six.u('Test Value')) def test_widont_html(self): self.assertEqual(widont_html('Test Value'), 'Test Value') self.assertEqual(widont_html(six.u('Test Value')), six.u('Test Value')) django-extensions-1.8.1/tests/test_timestamped_model.py000066400000000000000000000011071312717465700234430ustar00rootroot00000000000000# -*- coding: utf-8 -*- import time from django.test import TestCase from .testapp.models import TimestampedTestModel class ModifiedFieldTest(TestCase): def test_update(self): t = TimestampedTestModel.objects.create() modified = t.modified time.sleep(1) t.save() self.assertNotEqual(modified, t.modified) def test_update_no_modified(self): t = TimestampedTestModel.objects.create() modified = t.modified time.sleep(1) t.save(update_modified=False) self.assertEqual(modified, t.modified) django-extensions-1.8.1/tests/test_utils.py000066400000000000000000000015311312717465700211100ustar00rootroot00000000000000# -*- coding: utf-8 -*- import six from django.test import TestCase from django_extensions.utils.text import truncate_letters 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) ) django-extensions-1.8.1/tests/testapp/000077500000000000000000000000001312717465700200175ustar00rootroot00000000000000django-extensions-1.8.1/tests/testapp/__init__.py000066400000000000000000000001201312717465700221210ustar00rootroot00000000000000# -*- coding: utf-8 -*- default_app_config = 'tests.testapp.apps.TestAppConfig' django-extensions-1.8.1/tests/testapp/apps.py000066400000000000000000000001671312717465700213400ustar00rootroot00000000000000# -*- coding: utf-8 -*- from django.apps import AppConfig class TestAppConfig(AppConfig): name = 'tests.testapp' django-extensions-1.8.1/tests/testapp/fixtures/000077500000000000000000000000001312717465700216705ustar00rootroot00000000000000django-extensions-1.8.1/tests/testapp/fixtures/group.json000066400000000000000000000001551312717465700237200ustar00rootroot00000000000000[ { "model": "auth.group", "pk": 1, "fields": { "name": "Attendees", "permissions": [] } } ] django-extensions-1.8.1/tests/testapp/fixtures/user.json000066400000000000000000000024211312717465700235400ustar00rootroot00000000000000[ { "model": "auth.user", "fields": { "password": "pbkdf2_sha256$36000$90bEBUbbgBEO$L8mP32NfNI8IYyrFFUejJ1J8Mb7nWXAXfc4o40NZcVw=", "last_login": null, "is_superuser": false, "username": "Frederic", "first_name": "Fr\u00e9d\u00e9ric", "last_name": "Mistral", "email": "frederic_mistral@gmail.com", "is_staff": false, "is_active": true, "groups": [ 1 ], "user_permissions": [] } }, { "model": "auth.user", "fields": { "password": "pbkdf2_sha256$36000$DQjI1J5Sb0y7$qcHMl08/2DTYusG4s42c28EBZIzl0inoWmB3znGBLfI=", "last_login": null, "is_superuser": false, "username": "Gabriel", "first_name": "Gabriel Garcia", "last_name": "Marqu\u00e9z", "email": "gabriel_marquez@gmail.com", "is_staff": false, "is_active": true, "groups": [ 1 ], "user_permissions": [] } }, { "model": "auth.user", "fields": { "password": "pbkdf2_sha256$36000$PnIDqW2Atwye$ZKCDEzn+KsGMHrAb4Cq7MDpWVrW4smFl0MK/7Ehxp6g=", "last_login": null, "is_superuser": true, "username": "Mijail", "first_name": "Mija\u00edl", "last_name": "Bulgak\u00f3v", "email": "mijail_bulgakov@gmail.com", "is_staff": true, "is_active": true, "groups": [], "user_permissions": [] } } ] django-extensions-1.8.1/tests/testapp/models.py000066400000000000000000000120371312717465700216570ustar00rootroot00000000000000# -*- coding: utf-8 -*- from django.db import models from django_extensions.db.fields import ( AutoSlugField, RandomCharField, ShortUUIDField, ) from django_extensions.db.fields.json import JSONField from django_extensions.db.models import ActivatorModel, TimeStampedModel class Secret(models.Model): name = models.CharField(blank=True, max_length=255, null=True) text = models.TextField(blank=True, null=True) class Meta: app_label = 'django_extensions' class Name(models.Model): name = models.CharField(max_length=50) class Meta: app_label = 'django_extensions' class Note(models.Model): note = models.TextField() class Meta: app_label = 'django_extensions' class Person(models.Model): name = models.ForeignKey(Name, on_delete=models.CASCADE) age = models.PositiveIntegerField() children = models.ManyToManyField('self') notes = models.ManyToManyField(Note) class Meta: app_label = 'django_extensions' class Post(ActivatorModel): title = models.CharField(max_length=255) class Meta: app_label = 'django_extensions' class SluggedTestModel(models.Model): title = models.CharField(max_length=42) slug = AutoSlugField(populate_from='title') class Meta: app_label = 'django_extensions' class ChildSluggedTestModel(SluggedTestModel): class Meta: app_label = 'django_extensions' class ModelMethodSluggedTestModel(models.Model): title = models.CharField(max_length=42) slug = AutoSlugField(populate_from='get_readable_title') class Meta: app_label = 'django_extensions' def get_readable_title(self): return "The title is {}".format(self.title) class FKSluggedTestModel(models.Model): related_field = models.ForeignKey(SluggedTestModel, on_delete=models.CASCADE) slug = AutoSlugField(populate_from="related_field__title") class Meta: app_label = 'django_extensions' class FKSluggedTestModelCallable(models.Model): related_field = models.ForeignKey(ModelMethodSluggedTestModel, on_delete=models.CASCADE) slug = AutoSlugField(populate_from="related_field__get_readable_title") class Meta: app_label = 'django_extensions' class JSONFieldTestModel(models.Model): a = models.IntegerField() j_field = JSONField() class Meta: app_label = 'django_extensions' class ShortUUIDTestModel_field(models.Model): a = models.IntegerField() uuid_field = ShortUUIDField() class Meta: app_label = 'django_extensions' class ShortUUIDTestModel_pk(models.Model): uuid_field = ShortUUIDField(primary_key=True) class Meta: app_label = 'django_extensions' class ShortUUIDTestAgregateModel(ShortUUIDTestModel_pk): a = models.IntegerField() class Meta: app_label = 'django_extensions' class ShortUUIDTestManyToManyModel(ShortUUIDTestModel_pk): many = models.ManyToManyField(ShortUUIDTestModel_field) class Meta: app_label = 'django_extensions' class RandomCharTestModel(models.Model): random_char_field = RandomCharField(length=8, unique=False) class Meta: app_label = 'django_extensions' class RandomCharTestModelUnique(models.Model): random_char_field = RandomCharField(length=8, unique=True) class Meta: app_label = 'django_extensions' class RandomCharTestModelAlphaDigits(models.Model): random_char_field = RandomCharField(length=8, unique=True) class Meta: app_label = 'django_extensions' class RandomCharTestModelLowercaseAlphaDigits(models.Model): random_char_field = RandomCharField(length=8, lowercase=True) class Meta: app_label = 'django_extensions' verbose_name = 'lowercase alpha digits' class RandomCharTestModelUppercaseAlphaDigits(models.Model): random_char_field = RandomCharField(length=8, uppercase=True) class Meta: app_label = 'django_extensions' verbose_name = 'uppercase alpha digits' class RandomCharTestModelLowercase(models.Model): random_char_field = RandomCharField(length=8, lowercase=True, include_digits=False) class Meta: app_label = 'django_extensions' class RandomCharTestModelUppercase(models.Model): random_char_field = RandomCharField(length=8, uppercase=True, include_digits=False) class Meta: app_label = 'django_extensions' class RandomCharTestModelAlpha(models.Model): random_char_field = RandomCharField(length=8, include_digits=False) class Meta: app_label = 'django_extensions' class RandomCharTestModelDigits(models.Model): random_char_field = RandomCharField(length=8, include_alpha=False) class Meta: app_label = 'django_extensions' class RandomCharTestModelPunctuation(models.Model): random_char_field = RandomCharField( length=8, include_punctuation=True, include_digits=False, include_alpha=False, ) class Meta: app_label = 'django_extensions' class TimestampedTestModel(TimeStampedModel): class Meta: app_label = 'django_extensions' django-extensions-1.8.1/tests/testapp/scripts/000077500000000000000000000000001312717465700215065ustar00rootroot00000000000000django-extensions-1.8.1/tests/testapp/scripts/__init__.py000066400000000000000000000000001312717465700236050ustar00rootroot00000000000000django-extensions-1.8.1/tests/testapp/scripts/sample_script.py000066400000000000000000000000541312717465700247240ustar00rootroot00000000000000# -*- coding: utf-8 -*- def run(): pass django-extensions-1.8.1/tests/testapp/settings.py000066400000000000000000000023701312717465700222330ustar00rootroot00000000000000# -*- coding: utf-8 -*- SECRET_KEY = 'dummy' INSTALLED_APPS = [ 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.admin', 'django.contrib.sessions', 'tests.testapp', 'tests.testapp_with_no_models_file', 'django_extensions', ] MIDDLEWARE_CLASSES = ( 'django.contrib.sessions.middleware.SessionMiddleware', 'django.contrib.auth.middleware.AuthenticationMiddleware', 'django.contrib.messages.middleware.MessageMiddleware', ) DATABASES = { 'default': { 'ENGINE': 'django.db.backends.sqlite3', 'NAME': ':memory:', } } MEDIA_ROOT = '/tmp/django_extensions_test_media/' MEDIA_PATH = '/media/' ROOT_URLCONF = 'tests.testapp.urls' DEBUG = True TEMPLATE_DEBUG = True TEMPLATES = [ { 'BACKEND': 'django.template.backends.django.DjangoTemplates', 'DIRS': [], 'APP_DIRS': True, 'OPTIONS': { 'debug': TEMPLATE_DEBUG, 'context_processors': [ 'django.template.context_processors.debug', 'django.template.context_processors.request', 'django.contrib.auth.context_processors.auth', 'django.contrib.messages.context_processors.messages', ], }, }, ] django-extensions-1.8.1/tests/testapp/urls.py000066400000000000000000000016721312717465700213640ustar00rootroot00000000000000# -*- coding: utf-8 -*- """URL Configuration The `urlpatterns` list routes URLs to views. For more information please see: https://docs.djangoproject.com/en/1.9/topics/http/urls/ Examples: Function views 1. Add an import: from my_app import views 2. Add a URL to urlpatterns: url(r'^$', views.home, name='home') Class-based views 1. Add an import: from other_app.views import Home 2. Add a URL to urlpatterns: url(r'^$', Home.as_view(), name='home') Including another URLconf 1. Import the include() function: from django.conf.urls import url, include 2. Add a URL to urlpatterns: url(r'^blog/', include('blog.urls')) """ from django.conf.urls import url from django.contrib import admin from django.contrib.auth.views import login, logout urlpatterns = [ url(r'^login/$', login, {'template_name': 'login.html'}, name="login"), url(r'^logout/$', logout, name="logout"), url(r'^admin/', admin.site.urls), ] django-extensions-1.8.1/tests/testapp_with_no_models_file/000077500000000000000000000000001312717465700241105ustar00rootroot00000000000000django-extensions-1.8.1/tests/testapp_with_no_models_file/__init__.py000066400000000000000000000000001312717465700262070ustar00rootroot00000000000000django-extensions-1.8.1/tests/testapp_with_no_models_file/admin.py000066400000000000000000000002701312717465700255510ustar00rootroot00000000000000# -*- coding: utf-8 -*- # An app without a models.py file for issue #936 from django.db import models class TeslaCar(models.Model): sentient = models.BooleanField(default=False) django-extensions-1.8.1/tox.ini000066400000000000000000000022351312717465700165120ustar00rootroot00000000000000# 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 = precommit safety {py27,py34,py35,py36}-flake8 {py27,py33,py34,py36,pypy,pypy3}-dj18 {py27,py34,py35,py36,pypy}-dj{dj110,dj111,master} [testenv] commands = make test whitelist_externals = make deps = dj18: Django>=1.8,<1.9 dj110: Django>=1.10,<1.11 dj111: Django>=1.11b1,<1.12 djmaster: https://github.com/django/django/archive/master.tar.gz shortuuid==0.4 python-dateutil pytest-django pytest-cov py27: python-keyczar mock [testenv:precommit] deps = pre-commit commands = pre-commit run -a [testenv:safety] deps = safety commands = safety check --full-report [testenv:compile-catalog] whitelist_externals = make deps = Babel commands = make compile-catalog [testenv:py27-flake8] deps = flake8 commands = flake8 django_extensions tests [testenv:py34-flake8] deps = flake8 commands = flake8 django_extensions tests [testenv:py35-flake8] deps = flake8 commands = flake8 django_extensions tests