diff --git a/.editorconfig b/.editorconfig index 5c99f6582..736c9d3e9 100644 --- a/.editorconfig +++ b/.editorconfig @@ -1,4 +1,4 @@ -# http://editorconfig.org +# https://editorconfig.org # Source: pydanny cookiecutter-django repo root = true diff --git a/.github/workflows/pytest.yml b/.github/workflows/pytest.yml index c2d51f210..f10aeee6e 100644 --- a/.github/workflows/pytest.yml +++ b/.github/workflows/pytest.yml @@ -14,24 +14,42 @@ jobs: max-parallel: 4 matrix: python-version: - - 3.6 - 3.7 - 3.8 - 3.9 - "3.10" - - pypy3 + - "3.11" + # 2023-06-05: disabled pypy3.9 due to asgiref typing error + # - "pypy3.9" tox-django-version: - - "22" - - "30" - - "31" - "32" + - "40" + - "41" + - "42" # GH Actions don't support something like allow-failure ? # - "master" + exclude: + - python-version: "3.7" + tox-django-version: "40" + - python-version: "pypy3.9" + tox-django-version: "40" + - python-version: "3.6" + tox-django-version: "41" + - python-version: "3.7" + tox-django-version: "41" + - python-version: "pypy3.9" + tox-django-version: "41" + - python-version: "3.6" + tox-django-version: "42" + - python-version: "3.7" + tox-django-version: "42" + - python-version: "pypy3.9" + tox-django-version: "42" steps: - name: Checkout - uses: actions/checkout@v2 + uses: actions/checkout@v3 - name: Set up Python - uses: actions/setup-python@v2 + uses: actions/setup-python@v4 with: python-version: ${{ matrix.python-version }} - run: python -m pip install tox @@ -64,9 +82,9 @@ jobs: - "32" steps: - name: Checkout - uses: actions/checkout@v2 + uses: actions/checkout@v3 - name: Set up Python - uses: actions/setup-python@v2 + uses: actions/setup-python@v4 with: python-version: ${{ matrix.python-version }} - run: python -m pip install tox diff --git a/.gitignore b/.gitignore index 122246111..2275dd50c 100644 --- a/.gitignore +++ b/.gitignore @@ -19,3 +19,4 @@ htmlcov/ django-sample-app*/ *.swp *.swo +*.sqlite3 diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index f5983c241..9a6eff1ff 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,5 +1,6 @@ -- repo: git://github.com/pre-commit/pre-commit-hooks - sha: v0.9.1 +repos: +- repo: https://github.com/pre-commit/pre-commit-hooks + rev: v4.3.0 hooks: - id: trailing-whitespace - id: check-added-large-files @@ -14,18 +15,20 @@ - id: check-yaml - id: detect-private-key - id: end-of-file-fixer + - id: mixed-line-ending + args: [ '--fix=lf' ] + description: Forces to replace line ending by the UNIX 'lf' character. - id: fix-encoding-pragma - - id: flake8 - id: name-tests-test args: - --django exclude: ^tests/testapp|^tests/management/|^tests/collisions/|^tests/pythonrc.py|^tests/runner.py -- repo: git://github.com/Lucas-C/pre-commit-hooks.git - sha: v1.0.1 +- repo: https://github.com/pycqa/flake8 + rev: 4.0.1 hooks: - - id: forbid-crlf -- repo: git://github.com/trbs/pre-commit-hooks-trbs.git - sha: e233916fb2b4b9019b4a3cc0497994c7926fe36b + - id: flake8 +- repo: https://github.com/trbs/pre-commit-hooks-trbs.git + rev: 1.2.4 hooks: - id: forbid-executables exclude: manage.py|setup.py diff --git a/CHANGELOG.md b/CHANGELOG.md index cfa5cb942..8debcbae4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,147 +1,201 @@ Changelog ========= +See https://github.com/django-extensions/django-extensions/releases + +3.2.2 +----- + +Changes: + +- Improvement: Add support for psycopg3 (#1814) +- Improvement: runserver_plus, autoreload on template change (#1796) +- Improvement: highlighting, test_should_highlight_bash_syntax_without_name to include whitespace spans (#1797) +- Improvement: tests, add Python 3.11 to tox and actions to formally support python 3.11 (#1786) +- Improvement: runserver_plus, Send the file_changed event when a reload is triggered (#1775) +- Improvement: runserver_plus, Add REMOTE_USER to werkzeug environment (#1708) +- Improvement: pipchecker, force pip to use pkg_resources as backend for resolving distributions (#1782) +- Fix: Fix error with lack of PosixPath support (#1785) +- Fix: Cleanup http: links (#1798) + +3.2.1 +----- + +Changes: + +- Improvement: fix translation interpolation in prospective arabic translations (#1740) +- Improvement: runserver_plus, Add option to ignore files on runserver_plus reload (#1762) +- Improvement: docs: Fix a few typos (#1764) (#1751) +- Improvement: drop python 3.5 as it is EOL (#1735) +- Improvement: sqldiff, Added support for meta indexes and constraints in sqldiff. (#1726) +- Improvement: show_urls, Ensure consistent output in show_urls for django 4.0+ (#1759) +- Fix: dumpscript, make_aware should not be called if aware already (#1745) +- Fix: Use list values for requires_system_checks (#1736) + +3.2.0 +----- + +Changes: + +- Improvement: Django 4 support +- Improvement: Accept both --no-input and --noinput +- Improvement: sqldsn, Added more styles to the sqldsn management command +- Improvement: graph_models, Flag for to color code relations based on on_delete +- Improvement: graph_models, Add --relation-fields-only flag +- Improvement: RandomCharField, allow keeping default values +- Fix: HexValidator, Max length validation +- Fix: runserver_plus, Fix KeyError: 'werkzeug.server.shutdown' +- New: managestate, Saves current applied migrations to a file or applies migrations from file + +3.1.5 +----- + +Changes: + +- Fix: pipchecker, crude way slow down to avoid HTTPTooManyRequests +- Fix: pipchecker, fix for removed get_installed_distributions function + 3.1.4 ----- Changes: - - Fix: set_default_site, improve django.contrib.sites application detection - - Improvement: documentation, Fix name of mixin in docs - - Improvement: mypy, type ignore backwards compatible imports - - Improvement: graph_models, add --rankdir to change graph direction - - Improvement: runserver_plus, Add --sql-truncate cli modifier - - Improvement: shell_plus, Add --sql-truncate cli modifier +- Fix: set_default_site, improve django.contrib.sites application detection +- Improvement: documentation, Fix name of mixin in docs +- Improvement: mypy, type ignore backwards compatible imports +- Improvement: graph_models, add --rankdir to change graph direction +- Improvement: runserver_plus, Add --sql-truncate cli modifier +- Improvement: shell_plus, Add --sql-truncate cli modifier 3.1.3 ----- Changes: - - Fix: Django 3.2, Run tests against Django 3.2 - - Fix: Django 3.2, Handle warnings for default_app_config (#1654) - - Fix: sqldiff, Fix for missing field/index in model case +- Fix: Django 3.2, Run tests against Django 3.2 +- Fix: Django 3.2, Handle warnings for default_app_config (#1654) +- Fix: sqldiff, Fix for missing field/index in model case 3.1.2 ----- Changes: - - Improvement: shell_plus, not save ipython history when using Jupyter - - Improvement: docs, fix spelling mistakes - - Improvement: tests, move to Github Actions instead of Travis - - Improvement: drop_test_database, delete all cloned test databases (#1637) - - Improvement: setup.py, Added minimum Django>=2.2 version to PyPI package - - Improvement: shell_plus, fix --command globals / locals error +- Improvement: shell_plus, not save ipython history when using Jupyter +- Improvement: docs, fix spelling mistakes +- Improvement: tests, move to Github Actions instead of Travis +- Improvement: drop_test_database, delete all cloned test databases (#1637) +- Improvement: setup.py, Added minimum Django>=2.2 version to PyPI package +- Improvement: shell_plus, fix --command globals / locals error 3.1.1.post1 ----------- Changes: - - Improvement: setup.py, Added minimum Django>=2.2 version to PyPI package +- Improvement: setup.py, Added minimum Django>=2.2 version to PyPI package 3.1.1 ----- Changes: - - Improvement: graph_models, add option --app-labels - - Improvement: shell_plus, update shell_plus for jupyterlab 3 - - Improvement: tests, add Python 3.9 +- Improvement: graph_models, add option --app-labels +- Improvement: shell_plus, update shell_plus for jupyterlab 3 +- Improvement: tests, add Python 3.9 3.1.0 ----- Changes: - - Improvement: pipchecker, sleep 60s if pypi raises a fault - - Improvement: add django_zero_downtime_migrations to list of supported postgresql engines - - Improvement: use list of supported database engines from settings for all database commands - - Improvement: reset_db, documentation - - Fix: tests, Python 3.9 fixes for some tests - - Fix: runserver_plus, parsing of RUNSERVER_PLUS_EXTRA_FILES +- Improvement: pipchecker, sleep 60s if pypi raises a fault +- Improvement: add django_zero_downtime_migrations to list of supported postgresql engines +- Improvement: use list of supported database engines from settings for all database commands +- Improvement: reset_db, documentation +- Fix: tests, Python 3.9 fixes for some tests +- Fix: runserver_plus, parsing of RUNSERVER_PLUS_EXTRA_FILES 3.0.9 ----- Changes: - - Improvement: runserver_plus, survive syntax and configuration errors part II - - Improvement: tests, refactor test runner - - Improvement: sqlcreate, support postgresql unix domain socket +- Improvement: runserver_plus, survive syntax and configuration errors part II +- Improvement: tests, refactor test runner +- Improvement: sqlcreate, support postgresql unix domain socket 3.0.8 ----- Changes: - - Improvement: setup.cfg, remove universal flag from wheel, we only support Python 3 and up - - Improvement: sqlcreate, fixed mentioned of old syncdb - - Fix: runserver_plus, stop catching SyntaxError since reload for it was not working properly +- Improvement: setup.cfg, remove universal flag from wheel, we only support Python 3 and up +- Improvement: sqlcreate, fixed mentioned of old syncdb +- Fix: runserver_plus, stop catching SyntaxError since reload for it was not working properly 3.0.7 ----- Changes: - - Improvement: runserver_plus, gh #1575 survive syntax and configuration errors - - Improvement: runscript, use exit-code 1 if script is not found +- Improvement: runserver_plus, gh #1575 survive syntax and configuration errors +- Improvement: runscript, use exit-code 1 if script is not found 3.0.6 ----- Changes: - - Improvement: runscript, add --continue-on-error unless set runscript will exit on errors - - Improvement: runscript, allow to return exit-code - - Improvement: runscript, support raise CommandError(... returncode=...) - - Improvement: runscript, run Django checks() and check_migrations() before executing scripts - - Improvement: shell_plus, set application name on all postgresql backends +- Improvement: runscript, add --continue-on-error unless set runscript will exit on errors +- Improvement: runscript, allow to return exit-code +- Improvement: runscript, support raise CommandError(... returncode=...) +- Improvement: runscript, run Django checks() and check_migrations() before executing scripts +- Improvement: shell_plus, set application name on all postgresql backends 3.0.5 ----- Changes: - - Fix: runserver_plus, exceptions must derive from BaseException error +- Fix: runserver_plus, exceptions must derive from BaseException error 3.0.4 ----- Changes: - - Various cleanups - - Deprecated using `--router` instead use `--database` - - Locales: Indonesian and Polish, updated - - Improvement: show_dsn, fix crash with django-postgres-extra - - Improvement: print_settings, added wildcard support - - Improvement: print_settings, added --fail option - - Improvement: delete_squashed_migrations, add --database option - - Improvement: runserver_plus, added RUNSERVER_PLUS_EXTRA_FILES setting - - Improvement: runserver_plus, added runserver_plus_started signal +- Various cleanups +- Deprecated using `--router` instead use `--database` +- Locales: Indonesian and Polish, updated +- Improvement: show_dsn, fix crash with django-postgres-extra +- Improvement: print_settings, added wildcard support +- Improvement: print_settings, added --fail option +- Improvement: delete_squashed_migrations, add --database option +- Improvement: runserver_plus, added RUNSERVER_PLUS_EXTRA_FILES setting +- Improvement: runserver_plus, added runserver_plus_started signal 3.0.3 ----- Changes: - - New: InternalIPS, allows to specify CIDRs for INTERNAL_IPS - - Docs: restructure toctree +- New: InternalIPS, allows to specify CIDRs for INTERNAL_IPS +- Docs: restructure toctree 3.0.2 ----- Changes: - - Fix: shell_plus, fix honouring SHELL_PLUS in settings.py +- Fix: shell_plus, fix honouring SHELL_PLUS in settings.py 3.0.1 ----- Changes: - - Fix: setup.py, add python_requires and remove legacy trove classifiers +- Fix: setup.py, add python_requires and remove legacy trove classifiers 3.0.0 ----- @@ -149,222 +203,222 @@ Changes: This is the first Django Extensions release which only targets Django 2.2 and above. It drops official support for Python 2.7. - Changes: - - Removal of Python 2 support - - Removal of deprecated keyczar encrypted fields EncryptedTextField and EncryptedCharField - - Removal of deprecated passwd command - - Removal of truncate_letters filter - - Change: TimeStampedModel; Removed default ordering on abstract model - - New: DjangoExtensionsConfig AppConfig - - New: shell_plus, JupyterLab support - - New: list_signals, List all signals by model and signal type - - Improvement: shell_plus, use -- to directly pass additional arguments to Jupyter - - Improvement: shell_plus, improvements to MySQL support - - Improvement: jobs, use logging to record errors - - Improvement: syncdata, added --remove-before flag - - Improvement: graph_models, add field and model to template context - - Fix: syncdata, fix non existent field in fixture data - - Fix: pipchecker, compatibility with pip 20.1 +- Removal of Python 2 support +- Removal of deprecated keyczar encrypted fields EncryptedTextField and EncryptedCharField +- Removal of deprecated passwd command +- Removal of truncate_letters filter +- Change: TimeStampedModel; Removed default ordering on abstract model +- New: DjangoExtensionsConfig AppConfig +- New: shell_plus, JupyterLab support +- New: list_signals, List all signals by model and signal type +- Improvement: shell_plus, use -- to directly pass additional arguments to Jupyter +- Improvement: shell_plus, improvements to MySQL support +- Improvement: jobs, use logging to record errors +- Improvement: syncdata, added --remove-before flag +- Improvement: graph_models, add field and model to template context +- Fix: syncdata, fix non existent field in fixture data +- Fix: pipchecker, compatibility with pip 20.1 2.2.9 ----- Changes: - - Fix: shell_plus, move notebook down the list of preferred shells - - Fix: sqldiff, fix KeyError when detecting missing (unique) indexes - - Improvement: encrypted fields, make it harder to use deprecated keyczar fields - - Locale: Removed empty localizations +- Fix: shell_plus, move notebook down the list of preferred shells +- Fix: sqldiff, fix KeyError when detecting missing (unique) indexes +- Improvement: encrypted fields, make it harder to use deprecated keyczar fields +- Locale: Removed empty localizations 2.2.8 ----- Changes: - - Locale: zh_Hans, removed as it generated UnicodeDecodeError errors (#1478) +- Locale: zh_Hans, removed as it generated UnicodeDecodeError errors (#1478) 2.2.7 ----- Changes: - - Improvement: shell_plus, #865 always add manage.py basedir to path for notebook kernel - - Improvement: docs, add zh-Hans locale - - Improvement: runserver_plus, fix broken import for werkzeug v1.0.0 - - Improvement: runserver_plus, #1461 fix always trying to load StaticFilesHandler - - Improvement: pipchecker, #1471 fix import of PipSession +- Improvement: shell_plus, #865 always add manage.py basedir to path for notebook kernel +- Improvement: docs, add zh-Hans locale +- Improvement: runserver_plus, fix broken import for werkzeug v1.0.0 +- Improvement: runserver_plus, #1461 fix always trying to load StaticFilesHandler +- Improvement: pipchecker, #1471 fix import of PipSession 2.2.6 ----- Changes: - - Improvement: travis, update pypy and pypy3 versions - - Improvement: shell_plus, ability to print location/traceback besides sql - - Improvement: runserver_plus, ability to print location/traceback besides sql - - Improvement: UniqueFieldMixin, Support Django 2.2 UniqueConstraint.condition - - Improvement: DEFAULT_MYSQL_ENGINES, add mysql.connector.django - - Improvement: shell_plus, allow setting SHELL_PLUS="notebook" - - Improvement: shell_plus, add -c/--command to shell_plus mirroring django's shell command - - Fix: shell_plus, fix postgresql debug wrapper on django 3.0 or higher - - Fix: runserver_plus, fix postgresql debug wrapper on django 3.0 or higher +- Improvement: travis, update pypy and pypy3 versions +- Improvement: shell_plus, ability to print location/traceback besides sql +- Improvement: runserver_plus, ability to print location/traceback besides sql +- Improvement: UniqueFieldMixin, Support Django 2.2 UniqueConstraint.condition +- Improvement: DEFAULT_MYSQL_ENGINES, add mysql.connector.django +- Improvement: shell_plus, allow setting SHELL_PLUS="notebook" +- Improvement: shell_plus, add -c/--command to shell_plus mirroring django's shell command +- Fix: shell_plus, fix postgresql debug wrapper on django 3.0 or higher +- Fix: runserver_plus, fix postgresql debug wrapper on django 3.0 or higher 2.2.5 ----- Changes: - - Improvement: travis, add Python 3.8 - - Improvement: setup.py, update classifiers +- Improvement: travis, add Python 3.8 +- Improvement: setup.py, update classifiers 2.2.4 ----- Changes: - - Improvement: RandomCharField, Support unique_together - - Improvement: export_emails, add settings for overriding queryset fields, order_by and the full_name function +- Improvement: RandomCharField, Support unique_together +- Improvement: export_emails, add settings for overriding queryset fields, order_by and the full_name function 2.2.3 ----- Changes: - - Fix: admin widgets, fix import of static template tag (part 2) + +- Fix: admin widgets, fix import of static template tag (part 2) 2.2.2 ----- Changes: - - Fix: autoslugfield, find unique method overrideable - - Fix: notes, do not replace dot in template dirs - - Fix: admin widgets, fix import of static template tag - - Improvement: print_user_for_session, use session backend - - Improvement: sqlcreate, postgis support - - Improvement: graph_models, permit combination of includes and excludes - - Improvement: Adds missing GIS engine to DEFAULT_MYSQL_ENGINES - - Improvement: sqldiff, use lowercase field names in MySQL - - Improvement: sqldiff, mysql code could duplicate AUTO_INCREMENT and UNSIGNED statements +- Fix: autoslugfield, find unique method overrideable +- Fix: notes, do not replace dot in template dirs +- Fix: admin widgets, fix import of static template tag +- Improvement: print_user_for_session, use session backend +- Improvement: sqlcreate, postgis support +- Improvement: graph_models, permit combination of includes and excludes +- Improvement: Adds missing GIS engine to DEFAULT_MYSQL_ENGINES +- Improvement: sqldiff, use lowercase field names in MySQL +- Improvement: sqldiff, mysql code could duplicate AUTO_INCREMENT and UNSIGNED statements 2.2.1 ----- Changes: - - Fix: tests, support for newer versions of pytest - - Fix: tests, disable test with drf dependency for older python versions +- Fix: tests, support for newer versions of pytest +- Fix: tests, disable test with drf dependency for older python versions 2.2.0 ----- Changes: - - Fix: removing wrongly released text_tags template - - Fix: graph_models, support for Python <3.6 - - Improvement: ForeignKeySearchInput, wrap media files in static() - - Improvement: UniqField, added tests - - Improvement: dumpscript, fix orm_item_locator to use dateutil - - Improvement: graph_models, added argument to change arrow_shape +- Fix: removing wrongly released text_tags template +- Fix: graph_models, support for Python <3.6 +- Improvement: ForeignKeySearchInput, wrap media files in static() +- Improvement: UniqField, added tests +- Improvement: dumpscript, fix orm_item_locator to use dateutil +- Improvement: graph_models, added argument to change arrow_shape 2.1.9 ----- Changes: - - Fix: show_urls, fix for traceback on multi language sites - - Improvement: reset_db, fix typo's in help test +- Fix: show_urls, fix for traceback on multi language sites +- Improvement: reset_db, fix typo's in help test 2.1.8 ----- Changes: - - New: HexValidator, validate hex strings - - Improvement: reset_db, move settings to `django_settings.settings` which makes it easier to override. - - Improvement: AutoSlugField, extend support for custom slugify function - - Fix: runprofileserver, fix autoreloader for newer Django versions +- New: HexValidator, validate hex strings +- Improvement: reset_db, move settings to `django_settings.settings` which makes it easier to override. +- Improvement: AutoSlugField, extend support for custom slugify function +- Fix: runprofileserver, fix autoreloader for newer Django versions 2.1.7 ----- Changes: - - New: test, many many more tests :-) thanks everybody - - New: docs, many documentation updates - - New: graph_model, add simple theming support and django2018 theme - - Improvement: ModificationDateTimeField, make modificationfield name modifiable - - Improvement: graph_model, option to not showrelations labels in the graph - - Improvement: reset_db, allow to override list of backends for database engines - - Improvement: reset_db, add psqlextra backend - - Improvement: runserver_plus, idle support - - Improvement: generate_secret_key, removed get_random_string in favour of get_random_secret_key - - Improvement: update_permissions, add create-only and update-only flags - - Improvement: update_permissions, update changed names of permission to match correct permission name - - Improvement: syncdata, add --database option - - Improvement: runscript, allow to override RUNSCRIPT_SCRIPT_DIR - - Fix: create_command, fix mknod error on macos - - Fix: runserver_plus, fix in resolving ssl certificate path - - Fix: sqldiff, fix hstorefield - - Deprecate: truncate_letters, use Django's truncatechars - - Deprecate: passwd, use Django's changepassword - - Deprecate: Keyczar encrypted fields, Keyczar is abandoned / deprecated +- New: test, many many more tests :-) thanks everybody +- New: docs, many documentation updates +- New: graph_model, add simple theming support and django2018 theme +- Improvement: ModificationDateTimeField, make modificationfield name modifiable +- Improvement: graph_model, option to not showrelations labels in the graph +- Improvement: reset_db, allow to override list of backends for database engines +- Improvement: reset_db, add psqlextra backend +- Improvement: runserver_plus, idle support +- Improvement: generate_secret_key, removed get_random_string in favour of get_random_secret_key +- Improvement: update_permissions, add create-only and update-only flags +- Improvement: update_permissions, update changed names of permission to match correct permission name +- Improvement: syncdata, add --database option +- Improvement: runscript, allow to override RUNSCRIPT_SCRIPT_DIR +- Fix: create_command, fix mknod error on macos +- Fix: runserver_plus, fix in resolving ssl certificate path +- Fix: sqldiff, fix hstorefield +- Deprecate: truncate_letters, use Django's truncatechars +- Deprecate: passwd, use Django's changepassword +- Deprecate: Keyczar encrypted fields, Keyczar is abandoned / deprecated 2.1.6 ----- Changes: - - Fix: runserver_plus, auto_reloader fix for compatibility with Django 2.2 - - New: test, many many more tests :-) thanks @kuter +- Fix: runserver_plus, auto_reloader fix for compatibility with Django 2.2 +- New: test, many many more tests :-) thanks @kuter 2.1.5 ----- Changes: - - New: ipdb, pdb and wdb filters - - Fix: ForeignKeySearchInput, error with widget render(...) parameters on Django 2.1 - - Fix: pipchecker, unsupported format string passed to NoneType.format error - - Tests: bunch of new test cases +- New: ipdb, pdb and wdb filters +- Fix: ForeignKeySearchInput, error with widget render(...) parameters on Django 2.1 +- Fix: pipchecker, unsupported format string passed to NoneType.format error +- Tests: bunch of new test cases 2.1.4 ----- Changes: - - Fix: null_technical_500_response, handle function-based middleware - - Fix: shell_plus, fix #1261 check for --notebook-dir=... argument style - - Fix: graph_models, Excluded models displayed as an underscore - - Fix: set_fake_password, requires_model_validation has been replaced with requires_system_checks since 1.9 - - Docs: admin_generator, new documentation and examples - - Improvement: JSONField, use new from_db_value syntax on Django 2 and up - - Improvement: EncryptedTextField, use new from_db_value syntax on Django 2 and up - - Improvement: graph_models, add --dot option - - Improvement: graph_models, allow to redirect (text) output to file - - Improvement: sqldiff, better support for indexes, index_together and unique_together +- Fix: null_technical_500_response, handle function-based middleware +- Fix: shell_plus, fix #1261 check for --notebook-dir=... argument style +- Fix: graph_models, Excluded models displayed as an underscore +- Fix: set_fake_password, requires_model_validation has been replaced with requires_system_checks since 1.9 +- Docs: admin_generator, new documentation and examples +- Improvement: JSONField, use new from_db_value syntax on Django 2 and up +- Improvement: EncryptedTextField, use new from_db_value syntax on Django 2 and up +- Improvement: graph_models, add --dot option +- Improvement: graph_models, allow to redirect (text) output to file +- Improvement: sqldiff, better support for indexes, index_together and unique_together 2.1.3 ----- Changes: - - Fix: Readme, add direct link to screencast video - - Fix: graph_models, regression under Python 2 - - Fix: ForeignKeyAutocompleteAdmin, 2.0.8 breaks ForeignKeyAutocompleteAdmin - - Fix: AutoSlugField, fix regression when copying an autoslug model require the explicit clearing of the slug if it needs to be recalculated - - Fix: technical_response, check for AttributeError - - Improvement: graph_models, Add feature disable_abstract_fields - - Improvement: AutoSlugField, Add overwrite_on_add - - Improvement: runscript, Improve module existence test in runscript +- Fix: Readme, add direct link to screencast video +- Fix: graph_models, regression under Python 2 +- Fix: ForeignKeyAutocompleteAdmin, 2.0.8 breaks ForeignKeyAutocompleteAdmin +- Fix: AutoSlugField, fix regression when copying an autoslug model require the explicit clearing of the slug if it needs to be recalculated +- Fix: technical_response, check for AttributeError +- Improvement: graph_models, Add feature disable_abstract_fields +- Improvement: AutoSlugField, Add overwrite_on_add +- Improvement: runscript, Improve module existence test in runscript 2.1.2 ----- Changes: - - Fix: AutoSlugField, fix check on list or tuple type +- Fix: AutoSlugField, fix check on list or tuple type 2.1.1 ----- @@ -372,29 +426,29 @@ Changes: Removed support for Django versions before 1.11 Changes: - - Fix: foreignkey_searchinput, remove unnecessary img tag - - Fix: sqldiff, fix deprecated get_indexes call - - Fix: AutoSlugField, check that any non-callable value passed to populate_from is a string type - - Fix: tests, fix ChangingDirectoryTests: cd back in tearDown - - Fix: show_template_tags, should handle AppConfig class in INSTALLED applications - - Improvement: runserver_plus, reduce reraise pollution in traceback page - - Improvement: dumpscript, prevent many2many field with custom intermediate models to be added directly on the parent model - - Docs: fix typos +- Fix: foreignkey_searchinput, remove unnecessary img tag +- Fix: sqldiff, fix deprecated get_indexes call +- Fix: AutoSlugField, check that any non-callable value passed to populate_from is a string type +- Fix: tests, fix ChangingDirectoryTests: cd back in tearDown +- Fix: show_template_tags, should handle AppConfig class in INSTALLED applications +- Improvement: runserver_plus, reduce reraise pollution in traceback page +- Improvement: dumpscript, prevent many2many field with custom intermediate models to be added directly on the parent model +- Docs: fix typos 2.1.0 ----- Changes: - - Fix: travis +- Fix: travis 2.0.9 ----- Changes: - - Improvement: use README as project description on PyPI +- Improvement: use README as project description on PyPI 2.0.8 ----- @@ -402,118 +456,118 @@ Changes: Please stop using ForeignKeyAutocompleteAdmin edition :-) Changes: - - Fix: special markers in runserver_plus.rst - - Fix: shell_plus, refactor reading pythonrc file outside of exec(compile(...)) - - Fix: reset_db, fix default utf8 support - - Fix: autoslugfield, Fix autoslug generation when existing model is copied - - Improvement: Cleanup management commands options after argparse migration #916 - - Improvement: sqldiff, add more tests - - Improvement: sqldiff, add DurationField and SearchVectorField - - Improvement: shell_plus, add more tests - - Improvement: shell_plus, backport macos fix for tab completion - - Improvement: clear_cache, add --all option - - Improvement: pipchecker, treat dev versions as unstable - - Deprecation: ForeignKeyAutocompleteAdmin, Django 2.0 has similar capabilities, which are much better supported. +- Fix: special markers in runserver_plus.rst +- Fix: shell_plus, refactor reading pythonrc file outside of exec(compile(...)) +- Fix: reset_db, fix default utf8 support +- Fix: autoslugfield, Fix autoslug generation when existing model is copied +- Improvement: Cleanup management commands options after argparse migration #916 +- Improvement: sqldiff, add more tests +- Improvement: sqldiff, add DurationField and SearchVectorField +- Improvement: shell_plus, add more tests +- Improvement: shell_plus, backport macos fix for tab completion +- Improvement: clear_cache, add --all option +- Improvement: pipchecker, treat dev versions as unstable +- Deprecation: ForeignKeyAutocompleteAdmin, Django 2.0 has similar capabilities, which are much better supported. 2.0.7 ----- Changes: - - Fix: pipchecker, pip 10.0.0 compatibility - - Fix: sqldiff, improve support of GIS fields by using Django introspection - - Fix: shell_plus, fix bug in windows when PYTHONPATH is defined - - Fix: shell_plus, Call execute on CursorWrapper instead of directly on cursor to ensure wrappers are run - - Fix: runserver_plus, Call execute on CursorWrapper instead of directly on cursor to ensure wrappers are run - - Improvement: sqldiff, drop old compatibility code - - Improvement: ForeignKeyAutocompleteAdminMixin, improvements for Django >1.9 +- Fix: pipchecker, pip 10.0.0 compatibility +- Fix: sqldiff, improve support of GIS fields by using Django introspection +- Fix: shell_plus, fix bug in windows when PYTHONPATH is defined +- Fix: shell_plus, Call execute on CursorWrapper instead of directly on cursor to ensure wrappers are run +- Fix: runserver_plus, Call execute on CursorWrapper instead of directly on cursor to ensure wrappers are run +- Improvement: sqldiff, drop old compatibility code +- Improvement: ForeignKeyAutocompleteAdminMixin, improvements for Django >1.9 2.0.6 ----- Changes: - - Fix: shell_plus, Fix of deprecation warning in collision resolvers +- Fix: shell_plus, Fix of deprecation warning in collision resolvers 2.0.5 ----- Changes: - - Improvement: setup.py, Use PEP 508 when setuptools is version 36 or higher should fix issues with pipenv - - Fix: docs, Docs should show that django 2.0 is supported +- Improvement: setup.py, Use PEP 508 when setuptools is version 36 or higher should fix issues with pipenv +- Fix: docs, Docs should show that django 2.0 is supported 2.0.4 ----- Changes: - - Fix: setup.py, fix installation of typing in python < 3.5 +- Fix: setup.py, fix installation of typing in python < 3.5 2.0.3 ----- Changes: - - Fix: shell_plus, python 2.7 support broken due to use of Python3 super() +- Fix: shell_plus, python 2.7 support broken due to use of Python3 super() 2.0.2 ----- Changes: - - Improvement: sqldiff, add --include-defaults to include default value in missing field for sqldiff #1064 +- Improvement: sqldiff, add --include-defaults to include default value in missing field for sqldiff #1064 2.0.1 ----- Changes: - - Fix: setup.py, do not include `typing` requirement in recent versions of Python - - Improvement: shell_plus, add support for using -- to pass cli argument directly to underlying python shell implementation - - New: generate_password, Generates a new password based on `BaseUserManager.make_random_password` +- Fix: setup.py, do not include `typing` requirement in recent versions of Python +- Improvement: shell_plus, add support for using -- to pass cli argument directly to underlying python shell implementation +- New: generate_password, Generates a new password based on `BaseUserManager.make_random_password` 2.0.0 ----- Changes: - - Fix: runserver_plus, for 1.11 still using MIDDLEWARE_CLASSES - - Fix: show_urls, Fix display in Django 2.0 - - Fix: validate_templates, remove realpath in validate_templates - - Fix: sqldiff, bug with including proxy models in sqldiff output - - Improvement: shell_plus, allow configurating of sqlparse formatting and pygments formatting - - Improvement: shell_plus, add collision resolvers based on app label - - Improvement: shell_plus, automatic importing of subclasses defined in SHELL_PLUS_SUBCLASSES_IMPORT - - New: reset_schema, simple command to recreate public schema in PostgreSQL - - Docs: fix links to Werkzeug documentation +- Fix: runserver_plus, for 1.11 still using MIDDLEWARE_CLASSES +- Fix: show_urls, Fix display in Django 2.0 +- Fix: validate_templates, remove realpath in validate_templates +- Fix: sqldiff, bug with including proxy models in sqldiff output +- Improvement: shell_plus, allow configurating of sqlparse formatting and pygments formatting +- Improvement: shell_plus, add collision resolvers based on app label +- Improvement: shell_plus, automatic importing of subclasses defined in SHELL_PLUS_SUBCLASSES_IMPORT +- New: reset_schema, simple command to recreate public schema in PostgreSQL +- Docs: fix links to Werkzeug documentation 1.9.9 ----- Changes: - - Fix: runserver_plus, fix for Django 2.0 middleware handling - - Fix: shell_plus, fixed app_name resolving - - Fix: AutoSlugField, deconstruct did not match construction values - - Fix: runjob, not compatible with apps that use AppConfig in INSTALLED_APPS - - Improvement: runserver_plus, added configuring paths to certificates - - Improvement: sample.py template, add newline to avoid linter warnings - - Improvement: jobs, add integration tests for runjob and runjobs management commands - - New: merge_model_instances, new management command for de-duplicating model instances +- Fix: runserver_plus, fix for Django 2.0 middleware handling +- Fix: shell_plus, fixed app_name resolving +- Fix: AutoSlugField, deconstruct did not match construction values +- Fix: runjob, not compatible with apps that use AppConfig in INSTALLED_APPS +- Improvement: runserver_plus, added configuring paths to certificates +- Improvement: sample.py template, add newline to avoid linter warnings +- Improvement: jobs, add integration tests for runjob and runjobs management commands +- New: merge_model_instances, new management command for de-duplicating model instances 1.9.8 ----- Changes: - - Fix: show_urls, fix for Django 2.0 (Locale URL Resolvers are still broken) - - Fix: runserver_plus, fix rendering of ipv6 link - - Improvement: validate_templates, allow relative paths - - Improvement: validate_templates, automatically include app templates - - Improvement: pip_checker, could not find some packages - - Docs: shell_plus, `--print-sql` usage clearification +- Fix: show_urls, fix for Django 2.0 (Locale URL Resolvers are still broken) +- Fix: runserver_plus, fix rendering of ipv6 link +- Improvement: validate_templates, allow relative paths +- Improvement: validate_templates, automatically include app templates +- Improvement: pip_checker, could not find some packages +- Docs: shell_plus, `--print-sql` usage clearification 1.9.7 ----- @@ -522,52 +576,51 @@ This release add checking types with MyPy to the test suite. At this point only a few lines of code are explicitly typed. Changes: - - Improvement: shell_plus, Collision resolver implemented. - - Improvement: shell_plus, Skipping all models importing feature added. - - Improvement: runscript, Script execution directory policy feature added. - - django-extensions now requires the [typing](https://pypi.python.org/pypi/typing) package. +- Improvement: shell_plus, Collision resolver implemented. +- Improvement: shell_plus, Skipping all models importing feature added. +- Improvement: runscript, Script execution directory policy feature added. +- django-extensions now requires the [typing](https://pypi.python.org/pypi/typing) package. 1.9.6 ----- Fix boo-boo with release version in django_extensions/__init__.py - 1.9.4 ----- Changes: - - Fix missing test case +- Fix missing test case 1.9.3 ----- Changes: - - Tests: shell_plus, simple test for get_imported_objects +- Tests: shell_plus, simple test for get_imported_objects 1.9.2 ----- Changes: - - Fix: mail_debug, regression in mail_debug for older Pythons - - Fix: shell_plus, SyntaxError on exec(), python compatibility - - Fix: ForeignKeyAutocompleteAdminMixin, use text/plain +- Fix: mail_debug, regression in mail_debug for older Pythons +- Fix: shell_plus, SyntaxError on exec(), python compatibility +- Fix: ForeignKeyAutocompleteAdminMixin, use text/plain 1.9.1 ----- Changes: - - Fix: graph_models, fix json option - - Fix: runserver_plus, avoid duplicate messages logged to console - - Fix: mail_debug, python3 fix - - Improvement: sqldiff, basic support for array types in postgresql - - Improvement: runscript, handle import errors better - - Docs: updated documentation for model extensions +- Fix: graph_models, fix json option +- Fix: runserver_plus, avoid duplicate messages logged to console +- Fix: mail_debug, python3 fix +- Improvement: sqldiff, basic support for array types in postgresql +- Improvement: runscript, handle import errors better +- Docs: updated documentation for model extensions 1.9.0 ----- @@ -577,29 +630,28 @@ default behaviour to automatically load PYTHONSTARTUP and ~/.pythonrc.py unless --no-startup is set. Changes: - - Fix: pipchecker, fix up-to-date check for Github sha commits - - Fix: JSONField, fix handling to_python() for strings with tests - - Fix: print_settings, fix print_settings to receive positional args - - Improvement: shell_plus, update PYTHONSTARTUP / pythonrc handling to match Django - - Improvement: shell_plus, added new 1.11 features from django.db.models to shell_plus import list - - Improvement: runserver_plus, startup message now accounts for https - - Docs: jobs, improve documentation about jobs scheduling - - Docs: admin, add documentation for ForeignKeyAutocompleteStackedInline and ForeignKeyAutocompleteTabularInline - - Docs: fix typos +- Fix: pipchecker, fix up-to-date check for Github sha commits +- Fix: JSONField, fix handling to_python() for strings with tests +- Fix: print_settings, fix print_settings to receive positional args +- Improvement: shell_plus, update PYTHONSTARTUP / pythonrc handling to match Django +- Improvement: shell_plus, added new 1.11 features from django.db.models to shell_plus import list +- Improvement: runserver_plus, startup message now accounts for https +- Docs: jobs, improve documentation about jobs scheduling +- Docs: admin, add documentation for ForeignKeyAutocompleteStackedInline and ForeignKeyAutocompleteTabularInline +- Docs: fix typos 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 ! - +- 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 ----- @@ -610,120 +662,119 @@ 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 +- 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 algorithm from django - - Docs: fix grammar and spelling mistakes - +- 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 algorithm from django +- Docs: fix grammar 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 +- 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 registering 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 +- Improvement: admin_generator, use decorator style for registering 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) +- 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 +- 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 +- 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 +- 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 +- 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 +- Fix: sqldiff, fix optional app_label arguments +- Fix: runscript, remove args from command class +- Doc: runscript, remove = from --script-args example 1.7.0 ----- @@ -736,70 +787,70 @@ 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 +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 +- 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 + +- 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. +- 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 +- 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 ----- @@ -812,439 +863,441 @@ Bumped version number for incomplete PyPi upload 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 +- 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) +- 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 consistency - - Removed: jquery library (after dropping support for Django 1.5) +- 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 consistency +- 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 +- 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_settings, 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 +- 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_settings, 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, correctly support parsing lists for cli options - - Improvement: sqldsn, support postfix - - Improvement: utils, remove get_project_root function +- 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, correctly 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 +- 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 +- 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 +- 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 +- 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 +- 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 unused command line option - - Docs: print_user_for_session, add note about SESSION_ENGINE - - Docs: runserver_plus, added section about IO calls and CPU usage +- 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 unused 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 +- 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 +- 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 +- 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. +- 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 compatibility fix for Django 1.4 - - Fix: ForeignKey Field, handling of __str__ instead of __unicode__ in python3 +- Improvement: sqldiff, fix for dbcolumn not used in few places when generating the sqldiff +- Fix: sqldiff, backwards compatibility 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 +- 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 +- 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 +- 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 +- 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 +- 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 +- 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, Programmatically 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 +- Feature: shell_plus, add --kernel option to start as standalone IPython kernel +- Feature: reset_db, Programmatically 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 + +- 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 +- 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 +- 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 +- 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 built-in 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 +- 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 built-in 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 +- 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 + +- 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 diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index f488dbe81..7bf677598 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -2,8 +2,7 @@ There are many ways to contribute to the project. You may improve the documentation, address a bug, add some feature to the code or do something else. All sort of contributions are welcome. - -### Development +## Development To start development on this project, fork this repository and follow the following instructions. @@ -22,11 +21,13 @@ $ source venv/bin/activate # for accessing the GUI portion of the test application (venv) $ export DJANGO_EXTENSIONS_DATABASE_NAME="db.sqlite3" # you may change if you want to use any other database +# run migrations +(venv) $ python manage.py migrate # start the development server (venv) $ python manage.py runserver ``` -### Testing +## Testing To run tests against a particular `python` and `django` version installed inside your virtual environment, you may use: diff --git a/README.rst b/README.rst index 37f8bc4d1..ca5bf09e2 100644 --- a/README.rst +++ b/README.rst @@ -47,7 +47,7 @@ minutes Eric walks you through a half a dozen command extensions. There is also Requirements ============ -Django Extensions requires Django 2.2 or later. +Django Extensions requires Django 3.2 or later. Getting It @@ -68,7 +68,9 @@ Installing It ============= To enable `django_extensions` in your project you need to add it to `INSTALLED_APPS` in your projects -`settings.py` file:: +`settings.py` file: + +.. code-block:: python INSTALLED_APPS = ( ... @@ -108,8 +110,8 @@ Open Source projects can always use more help. Fixing a problem, documenting a f 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/ +- Mailing list: https://groups.google.com/group/django-extensions +- Translations: https://www.transifex.com/projects/p/django-extensions/ Documentation @@ -132,6 +134,6 @@ Please remember that nobody is paid directly to develop or maintain Django Exten 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://ericholscher.com/blog/2008/sep/12/screencast-django-command-extensions/ +__ https://vimeo.com/1720508 __ https://www.youtube.com/watch?v=1F6G3ONhr4k diff --git a/django_extensions/__init__.py b/django_extensions/__init__.py index d4291672a..6a53c7df5 100644 --- a/django_extensions/__init__.py +++ b/django_extensions/__init__.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -VERSION = (3, 1, 4) +VERSION = (3, 2, 3) def get_version(version): @@ -18,12 +18,3 @@ def get_version(version): __version__ = get_version(VERSION) - -try: - import django - - if django.VERSION < (3, 2): - default_app_config = 'django_extensions.apps.DjangoExtensionsConfig' -except ModuleNotFoundError: - # this part is useful for allow setup.py to be used for version checks - pass diff --git a/django_extensions/db/fields/__init__.py b/django_extensions/db/fields/__init__.py index e320dfd42..bc524f14f 100644 --- a/django_extensions/db/fields/__init__.py +++ b/django_extensions/db/fields/__init__.py @@ -143,7 +143,7 @@ def slugify_function(self, content): slug = AutoSlugField(populate_from='title') Inspired by SmileyChris' Unique Slugify snippet: - http://www.djangosnippets.org/snippets/690/ + https://www.djangosnippets.org/snippets/690/ """ def __init__(self, *args, **kwargs): @@ -319,6 +319,9 @@ class RandomCharField(UniqueFieldMixin, CharField): include_punctuation If set to True, include punctuation characters (default: False) + + keep_default + If set to True, keeps the default initialization value (default: False) """ def __init__(self, *args, **kwargs): @@ -341,6 +344,7 @@ def __init__(self, *args, **kwargs): self.include_alpha = kwargs.pop('include_alpha', True) self.check_is_bool('include_alpha') self.include_punctuation = kwargs.pop('include_punctuation', False) + self.keep_default = kwargs.pop('keep_default', False) self.check_is_bool('include_punctuation') self.max_unique_query_attempts = kwargs.pop('max_unique_query_attempts', MAX_UNIQUE_QUERY_ATTEMPTS) @@ -362,7 +366,7 @@ def in_unique_together(self, model_instance): return False def pre_save(self, model_instance, add): - if not add and getattr(model_instance, self.attname) != '': + if (not add or self.keep_default) and getattr(model_instance, self.attname) != '': return getattr(model_instance, self.attname) population = '' @@ -480,7 +484,7 @@ class UUIDFieldMixin: By default uses UUID version 4 (randomly generated UUID). The field support all uuid versions which are natively supported by the uuid python module, except version 2. - For more information see: http://docs.python.org/lib/module-uuid.html + For more information see: https://docs.python.org/lib/module-uuid.html """ DEFAULT_MAX_LENGTH = 36 diff --git a/django_extensions/import_subclasses.py b/django_extensions/import_subclasses.py index 4e6140ca5..ad0f3dbe1 100644 --- a/django_extensions/import_subclasses.py +++ b/django_extensions/import_subclasses.py @@ -39,7 +39,7 @@ def collect_subclasses(self): # type: () -> Dict[str, List[Tuple[str, str]]] but in future functionality of aliasing subclasses can be added. """ result = {} # type: Dict[str, List[Tuple[str, str]]] - for loader, module_name, is_pkg in walk_packages(path=[settings.BASE_DIR]): + for loader, module_name, is_pkg in walk_packages(path=[str(settings.BASE_DIR)]): subclasses_from_module = self._collect_classes_from_module(module_name) if subclasses_from_module: result[module_name] = subclasses_from_module diff --git a/django_extensions/locale/ar/LC_MESSAGES/django.po b/django_extensions/locale/ar/LC_MESSAGES/django.po new file mode 100644 index 000000000..89875dcb0 --- /dev/null +++ b/django_extensions/locale/ar/LC_MESSAGES/django.po @@ -0,0 +1,109 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR , YEAR. +# +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2020-06-06 11:44+0100\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: admin/__init__.py:139 +msgid "and" +msgstr "و" + +#: admin/__init__.py:141 +#, python-format +msgid "Use the left field to do %(model_name)s lookups in the fields %(field_list)s." +msgstr "إستعمل الحقل الأيسر من %(model_name)s لبحث ضمن الأحقال التالية %(field_list)s " + +#: admin/filter.py:24 admin/filter.py:53 +msgid "Yes" +msgstr "نعم" + +#: admin/filter.py:25 admin/filter.py:54 +msgid "No" +msgstr "لا" + +#: admin/filter.py:32 +msgid "All" +msgstr "كل" + +#: db/models.py:18 +msgid "created" +msgstr "تم تكونه" + +#: db/models.py:19 +msgid "modified" +msgstr "تم تعديله" + +#: db/models.py:37 +msgid "title" +msgstr "عنوان" + +#: db/models.py:38 +msgid "description" +msgstr "وصف" + +#: db/models.py:59 +msgid "slug" +msgstr "رابط " + +#: db/models.py:120 mongodb/models.py:76 +msgid "Inactive" +msgstr "غير نشط" + +#: db/models.py:121 mongodb/models.py:77 +msgid "Active" +msgstr "نشط" + +#: db/models.py:123 +msgid "status" +msgstr "الحالة" + +#: db/models.py:124 mongodb/models.py:80 +msgid "keep empty for an immediate activation" +msgstr "أترك الحقل فارغ ليتم التنشيط مباشرة" + +#: db/models.py:125 mongodb/models.py:81 +msgid "keep empty for indefinite activation" +msgstr "أترك الحقل فارغ لتنشيط لمدة غير محددة" + +#: mongodb/fields/__init__.py:22 +#, python-format +msgid "String (up to %(max_length)s)" +msgstr "سلسلة الإحرف (طولها يصل إلى %(max_length)s)" + +#: validators.py:14 +msgid "Control Characters like new lines or tabs are not allowed." +msgstr "لا يسمح إستعمال أحرف تحكم مثل حرف العودة إلى السطر أو علامات التبويب" + +#: validators.py:48 +msgid "Leading and Trailing whitespaces are not allowed." +msgstr "المسافات البيضاء الزائدة عند البداية أو نهاية غير مسموح بها" + +#: validators.py:74 +msgid "Only a hex string is allowed." +msgstr "مسموح إستعمال سلسلة أحرف hex فقط" + +#: validators.py:75 +#, python-format +msgid "Invalid length. Must be %(length)d characters." +msgstr "الطول غير مقبول, يجب أن لا يكون أطول من %(length)d" + +#: validators.py:76 +#, python-format +msgid "Ensure that there are more than %(min)s characters." +msgstr "تأكد أن طول سلسلة الإحرف أطول من %(min)s " + +#: validators.py:77 +#, python-format +msgid "Ensure that there are no more than %(max)s characters." +msgstr "تأكد أن طول سلسلة الأحرف لا تتجوز %(max)s " diff --git a/django_extensions/locale/fr/LC_MESSAGES/django.po b/django_extensions/locale/fr/LC_MESSAGES/django.po index 0e9afbe19..7dce17f79 100644 --- a/django_extensions/locale/fr/LC_MESSAGES/django.po +++ b/django_extensions/locale/fr/LC_MESSAGES/django.po @@ -13,7 +13,7 @@ msgstr "" "POT-Creation-Date: 2011-02-02 11:42+0100\n" "PO-Revision-Date: 2014-01-11 11:14+0000\n" "Last-Translator: mathiasuk\n" -"Language-Team: French (http://www.transifex.com/projects/p/django-extensions/language/fr/)\n" +"Language-Team: French (https://www.transifex.com/projects/p/django-extensions/language/fr/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" diff --git a/django_extensions/management/commands/clean_pyc.py b/django_extensions/management/commands/clean_pyc.py index f710eb511..a3197a9d5 100644 --- a/django_extensions/management/commands/clean_pyc.py +++ b/django_extensions/management/commands/clean_pyc.py @@ -2,6 +2,7 @@ import fnmatch import os from os.path import join as _j +from typing import List from django.conf import settings from django.core.management.base import BaseCommand, CommandError @@ -12,7 +13,7 @@ class Command(BaseCommand): help = "Removes all python bytecode compiled files from the project." - requires_system_checks = False + requires_system_checks: List[str] = [] def add_arguments(self, parser): parser.add_argument( diff --git a/django_extensions/management/commands/clear_cache.py b/django_extensions/management/commands/clear_cache.py index b022c61dc..dcfcde112 100644 --- a/django_extensions/management/commands/clear_cache.py +++ b/django_extensions/management/commands/clear_cache.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# Author: AxiaCore S.A.S. http://axiacore.com +# Author: AxiaCore S.A.S. https://axiacore.com from django.conf import settings from django.core.cache import DEFAULT_CACHE_ALIAS, caches from django.core.cache.backends.base import InvalidCacheBackendError diff --git a/django_extensions/management/commands/compile_pyc.py b/django_extensions/management/commands/compile_pyc.py index 1b702fd7b..0bcd1b466 100644 --- a/django_extensions/management/commands/compile_pyc.py +++ b/django_extensions/management/commands/compile_pyc.py @@ -3,6 +3,7 @@ import os import py_compile from os.path import join as _j +from typing import List from django.conf import settings from django.core.management.base import BaseCommand, CommandError @@ -12,7 +13,7 @@ class Command(BaseCommand): help = "Compile python bytecode files for the project." - requires_system_checks = False + requires_system_checks: List[str] = [] def add_arguments(self, parser): parser.add_argument('--path', '-p', action='store', dest='path', diff --git a/django_extensions/management/commands/create_command.py b/django_extensions/management/commands/create_command.py index 5844f91e3..d48c8dd99 100644 --- a/django_extensions/management/commands/create_command.py +++ b/django_extensions/management/commands/create_command.py @@ -2,6 +2,7 @@ import os import sys import shutil +from typing import List from django.core.management.base import AppCommand from django.core.management.color import color_style @@ -12,7 +13,7 @@ class Command(AppCommand): help = "Creates a Django management command directory structure for the given app name in the app's directory." - requires_system_checks = False + requires_system_checks: List[str] = [] # Can't import settings during this command, because they haven't # necessarily been created. can_import_settings = True diff --git a/django_extensions/management/commands/create_jobs.py b/django_extensions/management/commands/create_jobs.py index 30c7e0cca..aa9aff1e9 100644 --- a/django_extensions/management/commands/create_jobs.py +++ b/django_extensions/management/commands/create_jobs.py @@ -2,6 +2,7 @@ import os import sys import shutil +from typing import List from django.core.management.base import AppCommand from django.core.management.color import color_style @@ -12,7 +13,7 @@ class Command(AppCommand): help = "Creates a Django jobs command directory structure for the given app name in the current directory." - requires_system_checks = False + requires_system_checks: List[str] = [] # Can't import settings during this command, because they haven't # necessarily been created. can_import_settings = True diff --git a/django_extensions/management/commands/create_template_tags.py b/django_extensions/management/commands/create_template_tags.py index c444e43f0..83ab9cf93 100644 --- a/django_extensions/management/commands/create_template_tags.py +++ b/django_extensions/management/commands/create_template_tags.py @@ -1,6 +1,7 @@ # -*- coding: utf-8 -*- import os import sys +from typing import List from django.core.management.base import AppCommand @@ -21,7 +22,7 @@ def add_arguments(self, parser): help='The name to use for the template tag base name. Defaults to `appname`_tags.' ) - requires_system_checks = False + requires_system_checks: List[str] = [] # Can't import settings during this command, because they haven't # necessarily been created. can_import_settings = True diff --git a/django_extensions/management/commands/drop_test_database.py b/django_extensions/management/commands/drop_test_database.py index be2089dc5..66d45b73a 100644 --- a/django_extensions/management/commands/drop_test_database.py +++ b/django_extensions/management/commands/drop_test_database.py @@ -1,4 +1,5 @@ # -*- coding: utf-8 -*- +import importlib.util from itertools import count import os import logging @@ -21,7 +22,7 @@ class Command(BaseCommand): def add_arguments(self, parser): super().add_arguments(parser) parser.add_argument( - '--noinput', action='store_false', dest='interactive', + '--noinput', '--no-input', action='store_false', dest='interactive', default=True, help='Tells Django to NOT prompt the user for input of any kind.' ) parser.add_argument( @@ -167,9 +168,13 @@ def format_filename(name, number): cursor.execute(drop_query) elif engine in POSTGRESQL_ENGINES: - import psycopg2 as Database # NOQA + has_psycopg3 = importlib.util.find_spec("psycopg") + if has_psycopg3: + import psycopg as Database # NOQA + else: + import psycopg2 as Database # NOQA - conn_params = {'database': 'template1'} + conn_params = {'dbname': 'template1'} if user: conn_params['user'] = user if password: @@ -180,7 +185,10 @@ def format_filename(name, number): conn_params['port'] = database_port connection = Database.connect(**conn_params) - connection.set_isolation_level(0) # autocommit false + if has_psycopg3: + connection.autocommit = True + else: + connection.set_isolation_level(0) # autocommit false cursor = connection.cursor() for db_name in get_database_names('{}_{}'.format): diff --git a/django_extensions/management/commands/dumpscript.py b/django_extensions/management/commands/dumpscript.py index 086a8c81e..384549c13 100644 --- a/django_extensions/management/commands/dumpscript.py +++ b/django_extensions/management/commands/dumpscript.py @@ -2,7 +2,7 @@ """ Title: Dumpscript management command Project: Hardytools (queryset-refactor version) - Author: Will Hardy (http://willhardy.com.au) + Author: Will Hardy Date: June 2008 Usage: python manage.py dumpscript appname > scripts/scriptname.py $Revision: 217 $ @@ -41,7 +41,7 @@ ) from django.db.models.deletion import Collector from django.utils import timezone -from django.utils.encoding import force_str, smart_text +from django.utils.encoding import force_str, smart_str from django_extensions.management.utils import signalcommand @@ -70,7 +70,8 @@ def orm_item_locator(orm_obj): v = clean_dict[key] if v is not None: if isinstance(v, datetime.datetime): - v = timezone.make_aware(v) + if not timezone.is_aware(v): + v = timezone.make_aware(v) clean_dict[key] = StrToCodeChanger('dateutil.parser.parse("%s")' % v.isoformat()) elif not isinstance(v, (str, int, float)): clean_dict[key] = str("%s" % v) @@ -205,7 +206,7 @@ def get_imports(self): Return a dictionary of import statements, with the variable being defined as the key. """ - return {self.model.__name__: smart_text(self.model.__module__)} + return {self.model.__name__: smart_str(self.model.__module__)} imports = property(get_imports) def get_lines(self): diff --git a/django_extensions/management/commands/generate_password.py b/django_extensions/management/commands/generate_password.py index e52369484..071cbb27c 100644 --- a/django_extensions/management/commands/generate_password.py +++ b/django_extensions/management/commands/generate_password.py @@ -1,4 +1,6 @@ # -*- coding: utf-8 -*- +from typing import List + try: from django.contrib.auth.base_user import BaseUserManager except ImportError: @@ -10,7 +12,7 @@ class Command(BaseCommand): help = "Generates a new password that can be used for a user password. This uses Django core's default password generator `BaseUserManager.make_random_password()`." - requires_system_checks = False + requires_system_checks: List[str] = [] def add_arguments(self, parser): parser.add_argument( diff --git a/django_extensions/management/commands/generate_secret_key.py b/django_extensions/management/commands/generate_secret_key.py index f48232440..0ec129118 100644 --- a/django_extensions/management/commands/generate_secret_key.py +++ b/django_extensions/management/commands/generate_secret_key.py @@ -1,4 +1,6 @@ # -*- coding: utf-8 -*- +from typing import List + from django.core.management.base import BaseCommand from django.core.management.utils import get_random_secret_key @@ -8,7 +10,7 @@ class Command(BaseCommand): help = "Generates a new SECRET_KEY that can be used in a project settings file." - requires_system_checks = False + requires_system_checks: List[str] = [] @signalcommand def handle(self, *args, **options): diff --git a/django_extensions/management/commands/graph_models.py b/django_extensions/management/commands/graph_models.py index c70e16968..3e0ef77de 100644 --- a/django_extensions/management/commands/graph_models.py +++ b/django_extensions/management/commands/graph_models.py @@ -153,6 +153,12 @@ def __init__(self, *args, **kwargs): 'dest': 'relations_as_fields', 'help': 'Do not show relations as fields in the graph.', }, + '--relation-fields-only': { + 'action': 'store', + 'default': False, + 'dest': 'relation_fields_only', + 'help': 'Only display fields that are relevant for relations', + }, '--disable-sort-fields -S': { 'action': 'store_false', 'default': True, @@ -163,7 +169,7 @@ def __init__(self, *args, **kwargs): 'action': 'store_true', 'default': False, 'dest': 'hide_edge_labels', - 'help': 'Do not showrelations labels in the graph.', + 'help': 'Do not show relations labels in the graph.', }, '--arrow-shape': { 'action': 'store', @@ -172,6 +178,12 @@ def __init__(self, *args, **kwargs): 'choices': ['box', 'crow', 'curve', 'icurve', 'diamond', 'dot', 'inv', 'none', 'normal', 'tee', 'vee'], 'help': 'Arrow shape to use for relations. Default is dot. Available shapes: box, crow, curve, icurve, diamond, dot, inv, none, normal, tee, vee.', }, + '--color-code-deletions': { + 'action': 'store_true', + 'default': False, + 'dest': 'color_code_deletions', + 'help': 'Color the relations according to their on_delete setting, where it it applicable. The colors are: red (CASCADE), orange (SET_NULL), green (SET_DEFAULT), yellow (SET), blue (PROTECT), grey (DO_NOTHING) and purple (RESTRICT).', + }, '--rankdir': { 'action': 'store', 'default': 'TB', diff --git a/django_extensions/management/commands/list_model_info.py b/django_extensions/management/commands/list_model_info.py index 15e067f0e..c0a027314 100644 --- a/django_extensions/management/commands/list_model_info.py +++ b/django_extensions/management/commands/list_model_info.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# Author: OmenApps. http://www.omenapps.com +# Author: OmenApps. https://omenapps.com import inspect from django.apps import apps as django_apps diff --git a/django_extensions/management/commands/mail_debug.py b/django_extensions/management/commands/mail_debug.py index 4bf6b0ba6..7a8424dc4 100644 --- a/django_extensions/management/commands/mail_debug.py +++ b/django_extensions/management/commands/mail_debug.py @@ -3,6 +3,7 @@ import sys from logging import getLogger from smtpd import SMTPServer +from typing import List from django.core.management.base import BaseCommand, CommandError @@ -33,7 +34,7 @@ class Command(BaseCommand): help = "Starts a test mail server for development." args = '[optional port number or ippaddr:port]' - requires_system_checks = False + requires_system_checks: List[str] = [] def add_arguments(self, parser): super().add_arguments(parser) diff --git a/django_extensions/management/commands/managestate.py b/django_extensions/management/commands/managestate.py new file mode 100644 index 000000000..82f2fc153 --- /dev/null +++ b/django_extensions/management/commands/managestate.py @@ -0,0 +1,174 @@ +# -*- coding: utf-8 -*- +import json +from operator import itemgetter +from pathlib import Path + +from django.core.management import call_command +from django.core.management.base import BaseCommand, CommandError +from django.db import DEFAULT_DB_ALIAS, connections +from django.db.migrations.loader import MigrationLoader +from django.db.migrations.recorder import MigrationRecorder +from django.utils import timezone + +from django_extensions.management.utils import signalcommand + +DEFAULT_FILENAME = 'managestate.json' +DEFAULT_STATE = 'default' + + +class Command(BaseCommand): + help = 'Manage database state in the convenient way.' + conn = database = None + _applied_migrations = None + migrate_args: dict + migrate_options: dict + filename: str + verbosity: int + + def add_arguments(self, parser): + parser.add_argument( + 'action', choices=('dump', 'load'), + help='An action to do. ' + 'Dump action saves applied migrations to a file. ' + 'Load action applies migrations specified in a file.', + ) + parser.add_argument( + 'state', nargs='?', default=DEFAULT_STATE, + help=f'A name of a state. Usually a name of a git branch. Defaults to "{DEFAULT_STATE}"', + ) + parser.add_argument( + '-d', '--database', default=DEFAULT_DB_ALIAS, + help=f'Nominates a database to synchronize. Defaults to the "{DEFAULT_DB_ALIAS}" database.', + ) + parser.add_argument( + '-f', '--filename', default=DEFAULT_FILENAME, + help=f'A file to write to. Defaults to "{DEFAULT_FILENAME}"', + ) + + # migrate command arguments + parser.add_argument( + '--noinput', '--no-input', action='store_false', dest='interactive', + help='The argument for "migrate" command. ' + 'Tells Django to NOT prompt the user for input of any kind.', + ) + parser.add_argument( + '--fake', action='store_true', + help='The argument for "migrate" command. ' + 'Mark migrations as run without actually running them.', + ) + parser.add_argument( + '--fake-initial', action='store_true', + help='The argument for "migrate" command. ' + 'Detect if tables already exist and fake-apply initial migrations if so. Make sure ' + 'that the current database schema matches your initial migration before using this ' + 'flag. Django will only check for an existing table name.', + ) + parser.add_argument( + '--plan', action='store_true', + help='The argument for "migrate" command. ' + 'Shows a list of the migration actions that will be performed.', + ) + parser.add_argument( + '--run-syncdb', action='store_true', + help='The argument for "migrate" command. ' + 'Creates tables for apps without migrations.', + ) + parser.add_argument( + '--check', action='store_true', dest='check_unapplied', + help='The argument for "migrate" command. ' + 'Exits with a non-zero status if unapplied migrations exist.', + ) + + @signalcommand + def handle(self, action, database, filename, state, *args, **options): + self.migrate_args = args + self.migrate_options = options + self.verbosity = options['verbosity'] + self.conn = connections[database] + self.database = database + self.filename = filename + getattr(self, action)(state) + + def dump(self, state: str): + """Save applied migrations to a file.""" + migrated_apps = self.get_migrated_apps() + migrated_apps.update(self.get_applied_migrations()) + self.write({state: migrated_apps}) + self.stdout.write(self.style.SUCCESS( + f'Migrations for state "{state}" have been successfully saved to {self.filename}.' + )) + + def load(self, state: str): + """Apply migrations from a file.""" + migrations = self.read().get(state) + if migrations is None: + raise CommandError(f'No such state saved: {state}') + + kwargs = { + **self.migrate_options, + 'database': self.database, + 'verbosity': self.verbosity - 1 if self.verbosity > 1 else 0 + } + + for app, migration in migrations.items(): + if self.is_applied(app, migration): + continue + + if self.verbosity > 1: + self.stdout.write(self.style.WARNING(f'Applying migrations for "{app}"')) + args = (app, migration, *self.migrate_args) + call_command('migrate', *args, **kwargs) + + self.stdout.write(self.style.SUCCESS( + f'Migrations for "{state}" have been successfully applied.' + )) + + def get_migrated_apps(self) -> dict: + """Installed apps having migrations.""" + apps = MigrationLoader(self.conn).migrated_apps + migrated_apps = dict.fromkeys(apps, 'zero') + if self.verbosity > 1: + self.stdout.write('Apps having migrations: ' + ', '.join(sorted(migrated_apps))) + return migrated_apps + + def get_applied_migrations(self) -> dict: + """Installed apps with last applied migrations.""" + if self._applied_migrations: + return self._applied_migrations + + migrations = MigrationRecorder(self.conn).applied_migrations() + last_applied = sorted(migrations.keys(), key=itemgetter(1)) + + self._applied_migrations = dict(last_applied) + return self._applied_migrations + + def is_applied(self, app: str, migration: str) -> bool: + """Check whether a migration for an app is applied or not.""" + applied = self.get_applied_migrations().get(app) + if applied == migration: + if self.verbosity > 1: + self.stdout.write(self.style.WARNING( + f'Migrations for "{app}" are already applied.' + )) + return True + return False + + def read(self) -> dict: + """Get saved state from the file.""" + path = Path(self.filename) + if not path.exists() or not path.is_file(): + raise CommandError(f'No such file: {self.filename}') + + with open(self.filename) as file: + return json.load(file) + + def write(self, data: dict): + """Write new data to the file using existent one.""" + try: + saved = self.read() + except CommandError: + saved = {} + + saved.update(data, updated_at=str(timezone.now())) + with open(self.filename, 'w') as file: + json.dump(saved, file, indent=2, sort_keys=True) diff --git a/django_extensions/management/commands/pipchecker.py b/django_extensions/management/commands/pipchecker.py index 5615d5099..9f4a84b71 100644 --- a/django_extensions/management/commands/pipchecker.py +++ b/django_extensions/management/commands/pipchecker.py @@ -24,7 +24,31 @@ except ImportError: from pip._internal.download import PipSession # type:ignore from pip._internal.req.req_file import parse_requirements - from pip._internal.utils.misc import get_installed_distributions + try: + from pip._internal.utils.misc import get_installed_distributions # type:ignore + except ImportError: + from typing import cast + + def get_installed_distributions( + local_only=True, + include_editables=True, + editables_only=False, + user_only=False, + paths=None, + ): + """Return a list of installed Distribution objects. + Left for compatibility until direct pkg_resources uses are refactored out. + """ + + from pip._internal.metadata import pkg_resources + + dists = pkg_resources.Environment.from_paths(paths).iter_installed_distributions( + local_only=local_only, + include_editables=include_editables, + editables_only=editables_only, + user_only=user_only, + ) + return [cast(pkg_resources.Distribution, dist)._dist for dist in dists] except ImportError: # pip < 10 try: @@ -152,6 +176,7 @@ def check_pypi(self): try: available = pypi.package_releases(req["pip_req"].name, True) or pypi.package_releases(req["pip_req"].name.replace('-', '_'), True) retry = False + sleep(1) # crude way slow down to avoid HTTPTooManyRequests except Fault as err: self.stdout.write(err.faultString) self.stdout.write("Retrying in 60 seconds!") @@ -191,8 +216,8 @@ def check_github(self): curl -u 'rizumu' -d '{"scopes":["repo"], "note":"pipchecker"}' https://api.github.com/authorizations For more info on github api tokens: - https://help.github.com/articles/creating-an-oauth-token-for-command-line-use - http://developer.github.com/v3/oauth/#oauth-authorizations-api + https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/creating-a-personal-access-token + https://docs.github.com/en/developers/apps/building-oauth-apps/authorizing-oauth-apps Requirement Format ------------------ diff --git a/django_extensions/management/commands/raise_test_exception.py b/django_extensions/management/commands/raise_test_exception.py new file mode 100644 index 000000000..9cc3b7599 --- /dev/null +++ b/django_extensions/management/commands/raise_test_exception.py @@ -0,0 +1,23 @@ +# -*- coding: utf-8 -*- +from django.core.management.base import BaseCommand + +from django_extensions.management.utils import signalcommand + + +class DjangoExtensionsTestException(Exception): + pass + + +class Command(BaseCommand): + help = ( + "Raises a test Exception named DjangoExtensionsTestException. " + "Useful for debugging integration with error reporters like Sentry." + ) + + @signalcommand + def handle(self, *args, **options): + message = ( + "This is a test exception via the " + "django-extensions raise_test_exception management command." + ) + raise DjangoExtensionsTestException(message) diff --git a/django_extensions/management/commands/reset_db.py b/django_extensions/management/commands/reset_db.py index bca8247f4..e35273c97 100644 --- a/django_extensions/management/commands/reset_db.py +++ b/django_extensions/management/commands/reset_db.py @@ -2,8 +2,9 @@ """ reset_db command -originally from http://www.djangosnippets.org/snippets/828/ by dnordberg +originally from https://www.djangosnippets.org/snippets/828/ by dnordberg """ +import importlib.util import os import logging import warnings @@ -24,7 +25,7 @@ class Command(BaseCommand): def add_arguments(self, parser): super().add_arguments(parser) parser.add_argument( - '--noinput', action='store_false', + '--noinput', '--no-input', action='store_false', dest='interactive', default=True, help='Tells Django to NOT prompt the user for input of any kind.' ) @@ -141,9 +142,13 @@ def handle(self, *args, **options): logging.info('Executing... "%s"', create_query) connection.query(create_query.strip()) elif engine in POSTGRESQL_ENGINES: - import psycopg2 as Database # NOQA + has_psycopg3 = importlib.util.find_spec("psycopg") + if has_psycopg3: + import psycopg as Database # NOQA + else: + import psycopg2 as Database # NOQA - conn_params = {'database': 'template1'} + conn_params = {'dbname': 'template1'} if user: conn_params['user'] = user if password: @@ -154,7 +159,10 @@ def handle(self, *args, **options): conn_params['port'] = database_port connection = Database.connect(**conn_params) - connection.set_isolation_level(0) # autocommit false + if has_psycopg3: + connection.autocommit = True + else: + connection.set_isolation_level(0) # autocommit false cursor = connection.cursor() if options['close_sessions']: diff --git a/django_extensions/management/commands/reset_schema.py b/django_extensions/management/commands/reset_schema.py index 95b567325..8ef0c0f33 100644 --- a/django_extensions/management/commands/reset_schema.py +++ b/django_extensions/management/commands/reset_schema.py @@ -24,7 +24,7 @@ class Command(BaseCommand): def add_arguments(self, parser): super().add_arguments(parser) parser.add_argument( - '--noinput', action='store_false', + '--noinput', '--no-input', action='store_false', dest='interactive', default=True, help='Tells Django to NOT prompt the user for input of any kind.' ) diff --git a/django_extensions/management/commands/runscript.py b/django_extensions/management/commands/runscript.py index a6739029d..921c6e393 100644 --- a/django_extensions/management/commands/runscript.py +++ b/django_extensions/management/commands/runscript.py @@ -89,8 +89,6 @@ def add_arguments(self, parser): @signalcommand def handle(self, *args, **options): - self.check() - self.check_migrations() NOTICE = self.style.SQL_TABLE NOTICE2 = self.style.SQL_FIELD diff --git a/django_extensions/management/commands/runserver_plus.py b/django_extensions/management/commands/runserver_plus.py index 2c9af4384..6ed2174ce 100644 --- a/django_extensions/management/commands/runserver_plus.py +++ b/django_extensions/management/commands/runserver_plus.py @@ -7,7 +7,8 @@ import traceback import webbrowser import functools -from typing import Set +from pathlib import Path +from typing import List, Set # NOQA import django from django.conf import settings @@ -15,7 +16,8 @@ from django.core.management.color import color_style from django.core.servers.basehttp import get_internal_wsgi_application from django.dispatch import Signal -from django.utils.autoreload import get_reloader +from django.template.autoreload import get_template_directories, reset_loaders +from django.utils.autoreload import file_changed, get_reloader from django.views import debug as django_views_debug try: @@ -76,22 +78,62 @@ _error_files = set() # type: Set[str] +def get_all_template_files() -> Set[str]: + template_list = set() + + for template_dir in get_template_directories(): + for base_dir, _, filenames in os.walk(template_dir): + for filename in filenames: + template_list.add(os.path.join(base_dir, filename)) + + return template_list + + if HAS_WERKZEUG: # Monkey patch the reloader to support adding more files to extra_files for name, reloader_loop_klass in _reloader.reloader_loops.items(): class WrappedReloaderLoop(reloader_loop_klass): # type: ignore def __init__(self, *args, **kwargs): + self._template_files: Set[str] = get_all_template_files() super().__init__(*args, **kwargs) self._extra_files = self.extra_files @property def extra_files(self): - return self._extra_files.union(_error_files) + template_files = get_all_template_files() + + # reset loaders if there are new files detected + if len(self._template_files) != len(template_files): + + changed = template_files.difference(self._template_files) + for filename in changed: + _log("info", f" * New file {filename} added, reset template loaders") + self.register_file_changed(filename) + + reset_loaders() + + self._template_files = template_files + + return self._extra_files.union(_error_files, template_files) @extra_files.setter def extra_files(self, extra_files): self._extra_files = extra_files + def trigger_reload(self, filename: str) -> None: + path = Path(filename) + results = file_changed.send(sender=self, file_path=path) + if not any(res[1] for res in results): + super().trigger_reload(filename) + else: + _log("info", f" * Detected change in {filename!r}, reset template loaders") + self.register_file_changed(filename) + + def register_file_changed(self, filename): + if hasattr(self, "mtimes"): + mtime = os.stat(filename).st_mtime + self.mtimes[filename] = mtime + _reloader.reloader_loops[name] = WrappedReloaderLoop @@ -128,7 +170,7 @@ class Command(BaseCommand): help = "Starts a lightweight Web server for development." # Validation is called explicitly each time the server is reloaded. - requires_system_checks = False + requires_system_checks: List[str] = [] DEFAULT_CRT_EXTENSION = ".crt" DEFAULT_KEY_EXTENSION = ".key" @@ -165,6 +207,8 @@ def add_arguments(self, parser): 'Either --cert-file or --key-file must be provided to use SSL.') parser.add_argument('--extra-file', dest='extra_files', action="append", type=str, default=[], help='auto-reload whenever the given file changes too (can be specified multiple times)') + parser.add_argument('--exclude-pattern', dest='exclude_patterns', action="append", type=str, default=[], + help='ignore reload on changes to files matching this pattern (can be specified multiple times)') parser.add_argument('--reloader-interval', dest='reloader_interval', action="store", type=int, default=DEFAULT_POLLER_RELOADER_INTERVAL, help='After how many seconds auto-reload should scan for updates in poller-mode [default=%s]' % DEFAULT_POLLER_RELOADER_INTERVAL) parser.add_argument('--reloader-type', dest='reloader_type', action="store", type=str, default=DEFAULT_POLLER_RELOADER_TYPE, @@ -309,7 +353,7 @@ def application(env, start_response): def inner_run(self, options): if not HAS_WERKZEUG: - raise CommandError("Werkzeug is required to use runserver_plus. Please visit http://werkzeug.pocoo.org/ or install via pip. (pip install Werkzeug)") + raise CommandError("Werkzeug is required to use runserver_plus. Please visit https://werkzeug.palletsprojects.com/ or install via pip. (pip install Werkzeug)") # Set colored output if settings.DEBUG: @@ -322,8 +366,11 @@ def inner_run(self, options): class WSGIRequestHandler(_WSGIRequestHandler): def make_environ(self): environ = super().make_environ() - if not options['keep_meta_shutdown_func']: + if not options['keep_meta_shutdown_func'] and 'werkzeug.server.shutdown' in environ: del environ['werkzeug.server.shutdown'] + remote_user = os.getenv('REMOTE_USER') + if remote_user is not None: + environ['REMOTE_USER'] = remote_user return environ threaded = options['threaded'] @@ -333,6 +380,7 @@ def make_environ(self): reloader_interval = options['reloader_interval'] reloader_type = options['reloader_type'] self.extra_files = set(options['extra_files']) + exclude_patterns = set(options['exclude_patterns']) self.nopin = options['nopin'] @@ -380,7 +428,7 @@ def make_environ(self): if self.show_startup_messages: print("\nDjango version %s, using settings %r" % (django.get_version(), settings.SETTINGS_MODULE)) print("Development server is running at %s" % (bind_url,)) - print("Using the Werkzeug debugger (http://werkzeug.pocoo.org/)") + print("Using the Werkzeug debugger (https://werkzeug.palletsprojects.com/)") print("Quit the server with %s." % quit_command) if open_browser: @@ -392,6 +440,8 @@ def make_environ(self): if getattr(settings, 'RUNSERVER_PLUS_EXTRA_FILES', []): self.extra_files |= set(settings.RUNSERVER_PLUS_EXTRA_FILES) + exclude_patterns |= set(getattr(settings, 'RUNSERVER_PLUS_EXCLUDE_PATTERNS', [])) + # Werkzeug needs to be clued in its the main instance if running # without reloader or else it won't show key. # https://git.io/vVIgo @@ -413,6 +463,7 @@ def make_environ(self): use_reloader=use_reloader, use_debugger=True, extra_files=self.extra_files, + exclude_patterns=exclude_patterns, reloader_interval=reloader_interval, reloader_type=reloader_type, threaded=threaded, diff --git a/django_extensions/management/commands/set_fake_emails.py b/django_extensions/management/commands/set_fake_emails.py index 4eb4a9c8b..4a5e34362 100644 --- a/django_extensions/management/commands/set_fake_emails.py +++ b/django_extensions/management/commands/set_fake_emails.py @@ -8,6 +8,8 @@ """ +from typing import List + from django.conf import settings from django.contrib.auth import get_user_model from django.core.management.base import BaseCommand, CommandError @@ -19,7 +21,7 @@ class Command(BaseCommand): help = '''DEBUG only: give all users a new email based on their account data ("%s" by default). Possible parameters are: username, first_name, last_name''' % (DEFAULT_FAKE_EMAIL, ) - requires_system_checks = False + requires_system_checks: List[str] = [] def add_arguments(self, parser): super().add_arguments(parser) diff --git a/django_extensions/management/commands/set_fake_passwords.py b/django_extensions/management/commands/set_fake_passwords.py index 5d619bd02..1d03fe037 100644 --- a/django_extensions/management/commands/set_fake_passwords.py +++ b/django_extensions/management/commands/set_fake_passwords.py @@ -7,6 +7,8 @@ setting.DEBUG is True. """ +from typing import List + from django.conf import settings from django.contrib.auth import get_user_model from django.core.management.base import BaseCommand, CommandError @@ -18,7 +20,7 @@ class Command(BaseCommand): help = 'DEBUG only: sets all user passwords to a common value ("%s" by default)' % (DEFAULT_FAKE_PASSWORD, ) - requires_system_checks = False + requires_system_checks: List[str] = [] def add_arguments(self, parser): super().add_arguments(parser) diff --git a/django_extensions/management/commands/shell_plus.py b/django_extensions/management/commands/shell_plus.py index dcb991174..b4ad275cf 100644 --- a/django_extensions/management/commands/shell_plus.py +++ b/django_extensions/management/commands/shell_plus.py @@ -255,23 +255,19 @@ def run_notebookapp(self, app_init, options, use_kernel_specs=True, history=True ksm = app.kernel_spec_manager for kid, ks in self.generate_kernel_specs(app, ipython_arguments).items(): roots = [os.path.dirname(ks.resource_dir), ksm.user_kernel_dir] - success = False + for root in roots: kernel_dir = os.path.join(root, kid) try: if not os.path.exists(kernel_dir): os.makedirs(kernel_dir) - with open(os.path.join(kernel_dir, 'kernel.json'), 'w') as f: f.write(ks.to_json()) - - success = True break except OSError: continue - - if not success: - raise CommandError("Could not write kernel %r in directories %r" % (kid, roots)) + else: + raise CommandError('Could not write kernel %r in directories %r' % (kid, roots)) app.start() @@ -488,7 +484,7 @@ def set_application_name(self, options): Use the fallback_application_name to let the user override it with PGAPPNAME env variable - http://www.postgresql.org/docs/9.4/static/libpq-connect.html#LIBPQ-PARAMKEYWORDS # noqa + https://www.postgresql.org/docs/current/libpq-connect.html#LIBPQ-PARAMKEYWORDS # noqa """ supported_backends = ( 'django.db.backends.postgresql', diff --git a/django_extensions/management/commands/show_template_tags.py b/django_extensions/management/commands/show_template_tags.py index 79561890e..b0ebc75f3 100644 --- a/django_extensions/management/commands/show_template_tags.py +++ b/django_extensions/management/commands/show_template_tags.py @@ -8,7 +8,7 @@ from django.core.management import color from django.core.management import BaseCommand from django.utils import termcolors -from django.utils.encoding import smart_text +from django.utils.encoding import smart_str from django_extensions.compat import load_tag_library from django_extensions.management.color import _dummy_style_func @@ -38,10 +38,10 @@ def format_block(block, nlspaces=0): The purpose is to let us list a code block as a multiline, triple-quoted Python string, taking care of indentation concerns. - http://code.activestate.com/recipes/145672/ + https://code.activestate.com/recipes/145672/ """ # separate block into lines - lines = smart_text(block).split('\n') + lines = smart_str(block).split('\n') # remove leading/trailing empty lines while lines and not lines[0]: diff --git a/django_extensions/management/commands/show_urls.py b/django_extensions/management/commands/show_urls.py index cddca120e..9f60023ba 100644 --- a/django_extensions/management/commands/show_urls.py +++ b/django_extensions/management/commands/show_urls.py @@ -125,6 +125,8 @@ def handle(self, *args, **options): func = func.func decorators.insert(0, 'functools.partial') + if hasattr(func, 'view_class'): + func = func.view_class if hasattr(func, '__name__'): func_name = func.__name__ elif hasattr(func, '__class__'): diff --git a/django_extensions/management/commands/sqlcreate.py b/django_extensions/management/commands/sqlcreate.py index a0af17549..9a579af35 100644 --- a/django_extensions/management/commands/sqlcreate.py +++ b/django_extensions/management/commands/sqlcreate.py @@ -2,6 +2,7 @@ import socket import sys import warnings +from typing import List from django.conf import settings from django.core.management.base import BaseCommand, CommandError @@ -19,7 +20,7 @@ class Command(BaseCommand): ./manage.py sqlcreate [--database=] | mysql -u -p ./manage.py sqlcreate [--database=] | psql -U -W""" - requires_system_checks = False + requires_system_checks: List[str] = [] can_import_settings = True def add_arguments(self, parser): diff --git a/django_extensions/management/commands/sqldiff.py b/django_extensions/management/commands/sqldiff.py index 550ed8f7b..d1dc8e015 100644 --- a/django_extensions/management/commands/sqldiff.py +++ b/django_extensions/management/commands/sqldiff.py @@ -30,6 +30,7 @@ from django.core.management.base import OutputWrapper from django.core.management.color import no_style from django.db import connection, transaction, models +from django.db.models import UniqueConstraint from django.db.models.fields import AutoField, IntegerField from django.db.models.options import normalize_together @@ -264,7 +265,7 @@ def sql_to_dict(self, query, param): sql_to_dict(query, param) -> list of dicts - code from snippet at http://www.djangosnippets.org/snippets/1383/ + code from snippet at https://www.djangosnippets.org/snippets/1383/ """ cursor = connection.cursor() cursor.execute(query, param) @@ -381,6 +382,23 @@ def strip_parameters(self, field_type): return field_type.split(" ")[0].split("(")[0].lower() return field_type + def get_index_together(self, meta): + indexes_normalized = list(normalize_together(meta.index_together)) + + for idx in meta.indexes: + indexes_normalized.append(idx.fields) + + return self.expand_together(indexes_normalized, meta) + + def get_unique_together(self, meta): + unique_normalized = list(normalize_together(meta.unique_together)) + + for constraint in meta.constraints: + if isinstance(constraint, UniqueConstraint): + unique_normalized.append(constraint.fields) + + return self.expand_together(unique_normalized, meta) + def expand_together(self, together, meta): new_together = [] for fields in normalize_together(together): @@ -411,7 +429,7 @@ def find_unique_missing_in_db(self, meta, table_indexes, table_constraints, tabl if db_type.startswith('text'): self.add_difference('index-missing-in-db', table_name, [attname], index_name + '_like', ' text_pattern_ops') - unique_together = self.expand_together(meta.unique_together, meta) + unique_together = self.get_unique_together(meta) db_unique_columns = normalize_together([v['columns'] for v in table_constraints.values() if v['unique'] and not v['index']]) for unique_columns in unique_together: @@ -427,7 +445,7 @@ def find_unique_missing_in_db(self, meta, table_indexes, table_constraints, tabl def find_unique_missing_in_model(self, meta, table_indexes, table_constraints, table_name): fields = dict([(field.column, field) for field in all_local_fields(meta)]) - unique_together = self.expand_together(meta.unique_together, meta) + unique_together = self.get_unique_together(meta) for constraint_name, constraint in table_constraints.items(): if not constraint['unique']: @@ -463,7 +481,7 @@ def find_index_missing_in_db(self, meta, table_indexes, table_constraints, table if db_type.startswith('text'): self.add_difference('index-missing-in-db', table_name, [attname], index_name + '_like', ' text_pattern_ops') - index_together = self.expand_together(meta.index_together, meta) + index_together = self.get_index_together(meta) db_index_together = normalize_together([v['columns'] for v in table_constraints.values() if v['index'] and not v['unique']]) for columns in index_together: if columns in db_index_together: @@ -478,7 +496,7 @@ def find_index_missing_in_db(self, meta, table_indexes, table_constraints, table def find_index_missing_in_model(self, meta, table_indexes, table_constraints, table_name): fields = dict([(field.column, field) for field in all_local_fields(meta)]) meta_index_names = [idx.name for idx in meta.indexes] - index_together = self.expand_together(meta.index_together, meta) + index_together = self.get_index_together(meta) for constraint_name, constraint in table_constraints.items(): if constraint_name in meta_index_names: @@ -642,7 +660,7 @@ def find_differences(self): transaction.rollback() # reset transaction continue - # map table_contraints into table_indexes + # map table_constraints into table_indexes table_indexes = {} for contraint_name, dct in table_constraints.items(): @@ -838,8 +856,8 @@ def get_field_db_type(self, description, field=None, table_name=None): def find_index_missing_in_model(self, meta, table_indexes, table_constraints, table_name): fields = dict([(field.column, field) for field in all_local_fields(meta)]) meta_index_names = [idx.name for idx in meta.indexes] - index_together = self.expand_together(meta.index_together, meta) - unique_together = self.expand_together(meta.unique_together, meta) + index_together = self.get_index_together(meta) + unique_together = self.get_unique_together(meta) for constraint_name, constraint in table_constraints.items(): if constraint_name in meta_index_names: @@ -904,7 +922,7 @@ def find_unique_missing_in_db(self, meta, table_indexes, table_constraints, tabl if db_type.startswith('text'): self.add_difference('index-missing-in-db', table_name, [attname], index_name + '_like', ' text_pattern_ops') - unique_together = self.expand_together(meta.unique_together, meta) + unique_together = self.get_unique_together(meta) # This comparison changed from superclass - otherwise function is the same db_unique_columns = normalize_together([v['columns'] for v in table_constraints.values() if v['unique']]) @@ -929,7 +947,7 @@ def load_null(self): # sqlite does not support tablespaces tablespace = "public" # index, column_name, column_type, nullable, default_value - # see: http://www.sqlite.org/pragma.html#pragma_table_info + # see: https://www.sqlite.org/pragma.html#pragma_table_info for table_info in self.sql_to_dict("PRAGMA table_info('%s');" % table_name, []): key = (tablespace, table_name, table_info['name']) self.null[key] = not table_info['notnull'] @@ -953,7 +971,7 @@ def find_unique_missing_in_db(self, meta, table_indexes, table_constraints, tabl if column in unique_columns and (constraint['unique'] or constraint['primary_key']): skip_list.append(column) - unique_together = self.expand_together(meta.unique_together, meta) + unique_together = self.get_unique_together(meta) db_unique_columns = normalize_together([v['columns'] for v in table_constraints.values() if v['unique']]) for unique_columns in unique_together: @@ -1192,7 +1210,7 @@ def get_field_db_type(self, description, field=None, table_name=None): # constraints for the type in `get_data_type_arrayfield` which instantiates # the array base_field or maybe even better restructure sqldiff entirely # to be based around the concrete type yielded by the code below. That gives - # the complete type the database uses, why not use thie much earlier in the + # the complete type the database uses, why not use this much earlier in the # process to compare to whatever django spits out as the desired database type ? attname = field.db_column or field.attname introspect_db_type = self.sql_to_dict( diff --git a/django_extensions/management/commands/sqldsn.py b/django_extensions/management/commands/sqldsn.py index dab377231..e5795205d 100644 --- a/django_extensions/management/commands/sqldsn.py +++ b/django_extensions/management/commands/sqldsn.py @@ -7,6 +7,7 @@ import sys import warnings +from typing import List from django.conf import settings from django.core.management.base import BaseCommand, CommandError @@ -16,28 +17,96 @@ from django_extensions.utils.deprecation import RemovedInNextVersionWarning +def _sqlite_name(dbhost, dbport, dbname, dbuser, dbpass): + return dbname + + +def _mysql_keyvalue(dbhost, dbport, dbname, dbuser, dbpass): + dsnstr = f'host="{dbhost}", db="{dbname}", user="{dbuser}", passwd="{dbpass}"' + if dbport is not None: + dsnstr += f', port="{dbport}"' + return dsnstr + + +def _mysql_args(dbhost, dbport, dbname, dbuser, dbpass): + dsnstr = f'-h "{dbhost}" -D "{dbname}" -u "{dbuser}" -p "{dbpass}"' + if dbport is not None: + dsnstr += f' -P {dbport}' + return dsnstr + + +def _postgresql_keyvalue(dbhost, dbport, dbname, dbuser, dbpass): + dsnstr = f"host='{dbhost}' dbname='{dbname}' user='{dbuser}' password='{dbpass}'" + if dbport is not None: + dsnstr += f" port='{dbport}'" + return dsnstr + + +def _postgresql_kwargs(dbhost, dbport, dbname, dbuser, dbpass): + dsnstr = f"host={dbhost!r}, database={dbname!r}, user={dbuser!r}, password={dbpass!r}" + if dbport is not None: + dsnstr += f", port={dbport!r}" + return dsnstr + + +def _postgresql_pgpass(dbhost, dbport, dbname, dbuser, dbpass): + return ':'.join(str(s) for s in [dbhost, dbport, dbname, dbuser, dbpass]) + + +def _uri(engine): + def inner(dbhost, dbport, dbname, dbuser, dbpass): + host = dbhost or '' + if dbport is not None and dbport != '': + host += f':{dbport}' + if dbuser is not None and dbuser != '': + user = dbuser + if dbpass is not None and dbpass != '': + user += f':{dbpass}' + host = f'{user}@{host}' + return f'{engine}://{host}/{dbname}' + return inner + + +_FORMATTERS = [ + (SQLITE_ENGINES, None, _sqlite_name), + (SQLITE_ENGINES, 'filename', _sqlite_name), + (SQLITE_ENGINES, 'uri', _uri('sqlite')), + (MYSQL_ENGINES, None, _mysql_keyvalue), + (MYSQL_ENGINES, 'keyvalue', _mysql_keyvalue), + (MYSQL_ENGINES, 'args', _mysql_args), + (MYSQL_ENGINES, 'uri', _uri('mysql')), + (POSTGRESQL_ENGINES, None, _postgresql_keyvalue), + (POSTGRESQL_ENGINES, 'keyvalue', _postgresql_keyvalue), + (POSTGRESQL_ENGINES, 'kwargs', _postgresql_kwargs), + (POSTGRESQL_ENGINES, 'uri', _uri('postgresql')), + (POSTGRESQL_ENGINES, 'pgpass', _postgresql_pgpass), +] + + class Command(BaseCommand): help = "Prints DSN on stdout, as specified in settings.py" - requires_system_checks = False + requires_system_checks: List[str] = [] can_import_settings = True def add_arguments(self, parser): super().add_arguments(parser) - parser.add_argument( + dbspec = parser.add_mutually_exclusive_group() + dbspec.add_argument( '-R', '--router', action='store', dest='router', default=DEFAULT_DB_ALIAS, help='Use this router-database other then default (deprecated: use --database instead)' ) - parser.add_argument( + dbspec.add_argument( '--database', default=DEFAULT_DB_ALIAS, help='Nominates a database to run command for. Defaults to the "%s" database.' % DEFAULT_DB_ALIAS, ) + styles = sorted(set([style for _, style, _ in _FORMATTERS if style is not None])) parser.add_argument( '-s', '--style', action='store', - dest='style', default=None, - help='DSN format style: keyvalue, uri, pgpass, all' + dest='style', default=None, choices=styles + ['all'], + help='DSN format style.' ) - parser.add_argument( + dbspec.add_argument( '-a', '--all', action='store_true', dest='all', default=False, help='Show DSN for all database routes' @@ -79,78 +148,27 @@ def show_dsn(self, database, options): dbname = dbinfo.get('NAME') dbhost = dbinfo.get('HOST') dbport = dbinfo.get('PORT') - - dsn = [] - - if engine in SQLITE_ENGINES: - dsn.append('{}'.format(dbname)) - elif engine in MYSQL_ENGINES: - dsn.append(self._mysql(dbhost, dbport, dbname, dbuser, dbpass)) - elif engine in POSTGRESQL_ENGINES: - dsn.extend(self._postgresql( - dbhost, dbport, dbname, dbuser, dbpass, dsn_style=dsn_style)) - else: - dsn.append(self.style.ERROR('Unknown database, can''t generate DSN')) + if dbport == '': + dbport = None + + dsn = [ + formatter(dbhost, dbport, dbname, dbuser, dbpass) + for engines, style, formatter in _FORMATTERS + if engine in engines and ( + dsn_style == style or dsn_style == 'all' and style is not None) + ] + + if not dsn: + available = ', '.join( + style for engines, style, _ in _FORMATTERS + if engine in engines and style is not None) + dsn = [self.style.ERROR( + f"Invalid style {dsn_style} for {engine} (available: {available})" + if available else "Unknown database, can't generate DSN" + )] if not quiet: - sys.stdout.write(self.style.SQL_TABLE("DSN for database '%s' with engine '%s':\n" % (database, engine))) + sys.stdout.write(self.style.SQL_TABLE(f'DSN for database {database!r} with engine {engine!r}:\n')) for output in dsn: - sys.stdout.write("{}\n".format(output)) - - def _mysql(self, dbhost, dbport, dbname, dbuser, dbpass): - dsnstr = 'host="{0}", db="{2}", user="{3}", passwd="{4}"' - - if dbport is not None: - dsnstr += ', port="{1}"' - - return dsnstr.format(dbhost, dbport, dbname, dbuser, dbpass) - - def _postgresql(self, dbhost, dbport, dbname, dbuser, dbpass, dsn_style=None): # noqa - """PostgreSQL psycopg2 driver accepts two syntaxes - - Plus a string for .pgpass file - """ - dsn = [] - - if dsn_style is None or dsn_style == 'all' or dsn_style == 'keyvalue': - dsnstr = "host='{0}' dbname='{2}' user='{3}' password='{4}'" - - if dbport is not None: - dsnstr += " port='{1}'" - - dsn.append(dsnstr.format( - dbhost, - dbport, - dbname, - dbuser, - dbpass, - )) - - if dsn_style in ('all', 'kwargs'): - dsnstr = "host='{0}', database='{2}', user='{3}', password='{4}'" - if dbport is not None: - dsnstr += ", port='{1}'" - - dsn.append(dsnstr.format( - dbhost, - dbport, - dbname, - dbuser, - dbpass, - )) - - if dsn_style in ('all', 'uri'): - dsnstr = "postgresql://{user}:{password}@{host}/{name}" - - dsn.append(dsnstr.format( - host="{host}:{port}".format(host=dbhost, port=dbport) if dbport else dbhost, # noqa - name=dbname, - user=dbuser, - password=dbpass, - )) - - if dsn_style in ('all', 'pgpass'): - dsn.append(':'.join(map(str, filter(None, [dbhost, dbport, dbname, dbuser, dbpass])))) - - return dsn + sys.stdout.write(f'{output}\n') diff --git a/django_extensions/management/commands/update_permissions.py b/django_extensions/management/commands/update_permissions.py index 6b0e4cce5..227c183ec 100644 --- a/django_extensions/management/commands/update_permissions.py +++ b/django_extensions/management/commands/update_permissions.py @@ -1,5 +1,4 @@ # -*- coding: utf-8 -*- -from django import VERSION as DJANGO_VERSION from django.apps import apps as django_apps from django.contrib.auth.management import create_permissions, _get_all_permissions from django.contrib.auth.models import Permission @@ -34,14 +33,6 @@ def handle(self, *args, **options): do_create, do_update = True, True for app in apps: - if DJANGO_VERSION < (2, 2): - # see https://github.com/django/django/commit/bec651a427fc032d9115d30c8c5d0e702d754f6c - # Ensure that contenttypes are created for this app. Needed if - # 'django.contrib.auth' is in INSTALLED_APPS before - # 'django.contrib.contenttypes'. - from django.contrib.contenttypes.management import create_contenttypes - create_contenttypes(app, verbosity=options['verbosity']) - if do_create: # create permissions if they do not exist create_permissions(app, options['verbosity']) diff --git a/django_extensions/management/debug_cursor.py b/django_extensions/management/debug_cursor.py index ad68cfd6a..d161bd8d8 100644 --- a/django_extensions/management/debug_cursor.py +++ b/django_extensions/management/debug_cursor.py @@ -3,7 +3,6 @@ import traceback from contextlib import contextmanager -import django from django.conf import settings from django.core.exceptions import ImproperlyConfigured from django.db.backends import utils @@ -19,7 +18,6 @@ def monkey_patch_cursordebugwrapper(print_sql=None, print_sql_location=False, tr if truncate is None: truncate = getattr(settings, '%s_PRINT_SQL_TRUNCATE' % confprefix, DEFAULT_PRINT_SQL_TRUNCATE_CHARS) - # Code orginally from http://gist.github.com/118990 sqlparse = None if getattr(settings, '%s_SQLPARSE_ENABLED' % confprefix, True): try: @@ -87,15 +85,14 @@ class PrintCursorQueryWrapper(PrintQueryWrapperMixin, _CursorDebugWrapper): utils.CursorDebugWrapper = PrintCursorQueryWrapper postgresql_base = None - if django.VERSION >= (3, 0): - try: - from django.db.backends.postgresql import base as postgresql_base - _PostgreSQLCursorDebugWrapper = postgresql_base.CursorDebugWrapper + try: + from django.db.backends.postgresql import base as postgresql_base + _PostgreSQLCursorDebugWrapper = postgresql_base.CursorDebugWrapper - class PostgreSQLPrintCursorDebugWrapper(PrintQueryWrapperMixin, _PostgreSQLCursorDebugWrapper): - pass - except (ImproperlyConfigured, TypeError): - postgresql_base = None + class PostgreSQLPrintCursorDebugWrapper(PrintQueryWrapperMixin, _PostgreSQLCursorDebugWrapper): + pass + except (ImproperlyConfigured, TypeError): + postgresql_base = None if postgresql_base: postgresql_base.CursorDebugWrapper = PostgreSQLPrintCursorDebugWrapper diff --git a/django_extensions/management/jobs.py b/django_extensions/management/jobs.py index 1c6cdb48a..e13a4f2ec 100644 --- a/django_extensions/management/jobs.py +++ b/django_extensions/management/jobs.py @@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- import os import sys -from imp import find_module +import importlib from typing import Optional # NOQA from django.apps import apps @@ -67,22 +67,20 @@ def my_import(name): def find_jobs(jobs_dir): try: - return [f[:-3] for f in os.listdir(jobs_dir) if not f.startswith('_') and f.endswith(".py")] + return sorted([f[:-3] for f in os.listdir(jobs_dir) if not f.startswith('_') and f.endswith(".py")]) except OSError: return [] -def find_job_module(app_name, when=None): +def find_job_module(app_name: str, when: Optional[str] = None) -> str: + """Find the directory path to a job module.""" parts = app_name.split('.') parts.append('jobs') if when: parts.append(when) - parts.reverse() - path = None - while parts: - part = parts.pop() - f, path, descr = find_module(part, path and [path] or None) - return path + module_name = ".".join(parts) + module = importlib.import_module(module_name) + return module.__path__[0] def import_job(app_name, name, when=None): diff --git a/django_extensions/management/modelviz.py b/django_extensions/management/modelviz.py index 15552d07c..b9d1cddea 100644 --- a/django_extensions/management/modelviz.py +++ b/django_extensions/management/modelviz.py @@ -13,9 +13,13 @@ import re from django.apps import apps +from django.db.models import deletion from django.db.models.fields.related import ( ForeignKey, ManyToManyField, OneToOneField, RelatedField, ) +from django.db.models.fields.reverse_related import ( + OneToOneRel, ManyToOneRel, +) from django.contrib.contenttypes.fields import GenericRelation from django.template import Context, Template, loader from django.utils.encoding import force_str @@ -27,9 +31,9 @@ __license__ = "Python" __author__ = "Bas van Oostveen ", __contributors__ = [ - "Antonio Cavedoni " - "Stefano J. Attardi ", - "limodou ", + "Antonio Cavedoni " + "Stefano J. Attardi ", + "limodou", "Carlo C8E Miron", "Andre Campos ", "Justin Findlay ", @@ -41,9 +45,21 @@ "Mikkel Munch Mortensen ", "Andrzej Bistram ", "Daniel Lipsitt ", + "Tobias Mitterdorfer " ] +ON_DELETE_COLORS = { + deletion.CASCADE: 'red', + deletion.PROTECT: 'blue', + deletion.SET_NULL: 'orange', + deletion.SET_DEFAULT: 'green', + deletion.SET: 'yellow', + deletion.DO_NOTHING: 'grey', + deletion.RESTRICT: 'purple', +} + + def parse_file_or_list(arg): if not arg: return [] @@ -68,6 +84,7 @@ def __init__(self, app_labels, **kwargs): self.verbose_names = kwargs.get('verbose_names', False) self.inheritance = kwargs.get('inheritance', True) self.relations_as_fields = kwargs.get("relations_as_fields", True) + self.relation_fields_only = kwargs.get("relation_fields_only", False) self.sort_fields = kwargs.get("sort_fields", True) self.language = kwargs.get('language', None) if self.language is not None: @@ -80,6 +97,7 @@ def __init__(self, app_labels, **kwargs): ) self.hide_edge_labels = kwargs.get('hide_edge_labels', False) self.arrow_shape = kwargs.get("arrow_shape") + self.color_code_deletions = kwargs.get("color_code_deletions", False) if self.all_applications: self.app_labels = [app.label for app in apps.get_app_configs()] else: @@ -152,7 +170,7 @@ def add_attributes(self, field, abstract_fields): 'primary_key': field.primary_key, } - def add_relation(self, field, model, extras=""): + def add_relation(self, field, model, extras="", color=None): if self.verbose_names and field.verbose_name: label = force_str(field.verbose_name) if label.islower(): @@ -183,6 +201,9 @@ def add_relation(self, field, model, extras=""): else: target_model = field.remote_field.model + if color: + extras = '[{}, color={}]'.format(extras[1:-1], color) + _rel = self.get_relation_context(target_model, field, label, extras) if _rel not in model['relations'] and self.use_model(_rel['target']): @@ -337,9 +358,15 @@ def process_local_fields(self, field, model, abstract_fields): # excluding field redundant with inheritance relation # excluding fields inherited from abstract classes. they too show as local_fields return newmodel + + color = None + if self.color_code_deletions and isinstance(field, (OneToOneField, ForeignKey)): + field_on_delete = getattr(field.remote_field, 'on_delete', None) + color = ON_DELETE_COLORS.get(field_on_delete) + if isinstance(field, OneToOneField): relation = self.add_relation( - field, newmodel, '[arrowhead=none, arrowtail=none, dir=both]' + field, newmodel, '[arrowhead=none, arrowtail=none, dir=both]', color ) elif isinstance(field, ForeignKey): relation = self.add_relation( @@ -348,6 +375,7 @@ def process_local_fields(self, field, model, abstract_fields): '[arrowhead=none, arrowtail={}, dir=both]'.format( self.arrow_shape ), + color ) else: relation = None @@ -416,6 +444,12 @@ def skip_field(self, field): return True if field.name in self.exclude_columns: return True + if self.relation_fields_only: + if not isinstance( + field, + (ForeignKey, ManyToManyField, OneToOneField, RelatedField, OneToOneRel, ManyToOneRel) + ): + return True return False diff --git a/django_extensions/mongodb/fields/__init__.py b/django_extensions/mongodb/fields/__init__.py index 857a5dba0..e87cace8b 100644 --- a/django_extensions/mongodb/fields/__init__.py +++ b/django_extensions/mongodb/fields/__init__.py @@ -56,7 +56,7 @@ class AutoSlugField(SlugField): If set to True, overwrites the slug on every save (default: False) Inspired by SmileyChris' Unique Slugify snippet: - http://www.djangosnippets.org/snippets/690/ + https://www.djangosnippets.org/snippets/690/ """ def __init__(self, *args, **kwargs): @@ -125,7 +125,7 @@ def create_slug(self, model_instance, add): if model_instance.pk: queryset = queryset.exclude(pk=model_instance.pk) - # form a kwarg dict used to impliment any unique_together contraints + # form a kwarg dict used to impliment any unique_together constraints kwargs = {} for params in model_instance._meta.unique_together: if self.attname in params: @@ -216,7 +216,7 @@ class UUIDField(StringField): By default uses UUID version 1 (generate from host ID, sequence number and current time) The field support all uuid versions which are natively supported by the uuid python module. - For more information see: http://docs.python.org/lib/module-uuid.html + For more information see: https://docs.python.org/lib/module-uuid.html """ def __init__(self, verbose_name=None, name=None, auto=True, version=1, node=None, clock_seq=None, namespace=None, **kwargs): diff --git a/django_extensions/static/django_extensions/js/jquery.ajaxQueue.js b/django_extensions/static/django_extensions/js/jquery.ajaxQueue.js index cd4492c13..aca15d9a6 100644 --- a/django_extensions/static/django_extensions/js/jquery.ajaxQueue.js +++ b/django_extensions/static/django_extensions/js/jquery.ajaxQueue.js @@ -1,8 +1,5 @@ /** * Ajax Queue Plugin - * - * Homepage: http://jquery.com/plugins/project/ajaxqueue - * Documentation: http://docs.jquery.com/AjaxQueue */ /** diff --git a/django_extensions/static/django_extensions/js/jquery.bgiframe.js b/django_extensions/static/django_extensions/js/jquery.bgiframe.js index 5c3735d68..1a452ba00 100644 --- a/django_extensions/static/django_extensions/js/jquery.bgiframe.js +++ b/django_extensions/static/django_extensions/js/jquery.bgiframe.js @@ -1,4 +1,4 @@ -/*! Copyright (c) 2010 Brandon Aaron (http://brandonaaron.net) +/*! Copyright (c) 2010 Brandon Aaron (http://brandon.aaron.sh/) * Licensed under the MIT License (LICENSE.txt). * * Version 2.1.2 diff --git a/django_extensions/templatetags/highlighting.py b/django_extensions/templatetags/highlighting.py index a38d39d73..f172fd3df 100644 --- a/django_extensions/templatetags/highlighting.py +++ b/django_extensions/templatetags/highlighting.py @@ -4,14 +4,11 @@ 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 %}