Hello community,
here is the log from the commit of package python-django-extensions for
openSUSE:Factory checked in at 2020-02-20 14:58:23
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/python-django-extensions (Old)
and /work/SRC/openSUSE:Factory/.python-django-extensions.new.26092 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "python-django-extensions"
Thu Feb 20 14:58:23 2020 rev:6 rq:777580 version:2.2.8
Changes:
--------
---
/work/SRC/openSUSE:Factory/python-django-extensions/python-django-extensions.changes
2020-01-20 22:54:38.075324816 +0100
+++
/work/SRC/openSUSE:Factory/.python-django-extensions.new.26092/python-django-extensions.changes
2020-02-20 14:58:28.606652602 +0100
@@ -1,0 +2,6 @@
+Thu Feb 20 07:59:33 UTC 2020 - Tomáš Chvátal <[email protected]>
+
+- Update to 2.2.8:
+ * Various small fixes
+
+-------------------------------------------------------------------
Old:
----
django-extensions-2.2.5.tar.gz
New:
----
django-extensions-2.2.8.tar.gz
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Other differences:
------------------
++++++ python-django-extensions.spec ++++++
--- /var/tmp/diff_new_pack.n9qBcp/_old 2020-02-20 14:58:29.750654846 +0100
+++ /var/tmp/diff_new_pack.n9qBcp/_new 2020-02-20 14:58:29.754654854 +0100
@@ -18,7 +18,7 @@
%define skip_python2 1
Name: python-django-extensions
-Version: 2.2.5
+Version: 2.2.8
Release: 0
Summary: Extensions for Django
License: BSD-3-Clause
@@ -60,44 +60,26 @@
%prep
%setup -q -n django-extensions-%{version}
-# See https://github.com/django-extensions/django-extensions/issues/1123
-rm tests/test_encrypted_fields.py
-
-# pip checks not possible in rpmbuild,
-# and also not particularly useful when packaged.
-rm tests/management/commands/test_pipchecker.py
-
-# tests are completely borked and the keyczar module is deprecated
-#rm tests/db/fields/test_encrypted.py
-
%build
export LANG=en_US.UTF-8
%python_build
%install
+export LANG=en_US.UTF-8
%python_install
%python_expand %fdupes %{buildroot}%{$python_sitelib}
%check
export LANG=en_US.UTF-8
export DJANGO_SETTINGS_MODULE=tests.testapp.settings
-
-%if 0%{?have_python2} && ! 0%{?skip_python2}
-# It is not possible to use %%pytest here, as it expands to py.test-3.7
-# which causes /usr/bin to be in the PYTHONPATH.
-# django_extensions/management/commands/mail_debug.py imports smtpd,
-# and python2-base adds smtpd.py to /usr/bin, so the import fails on
-# Python 3.
-
-python2 -m pytest
-%endif
-%if 0%{?have_python3} && ! 0%{?skip_python3}
# Test collection exception ValueError: wrapper loop when unwrapping call
-python3 -m pytest \
+# test_should_highlight_python_syntax_with_name - breaks ordering on py
versions, fragile
+%python_expand $python -m pytest -v \
--ignore tests/test_logging_filters.py \
--ignore tests/management/commands/test_reset_db.py \
- --ignore tests/management/commands/test_reset_schema.py
-%endif
+ --ignore tests/management/commands/test_reset_schema.py \
+ --ignore tests/management/commands/test_pipchecker.py \
+ -k 'not test_should_highlight_python_syntax_with_name'
%files %{python_files}
%license LICENSE
++++++ django-extensions-2.2.5.tar.gz -> django-extensions-2.2.8.tar.gz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/django-extensions-2.2.5/.travis.yml
new/django-extensions-2.2.8/.travis.yml
--- old/django-extensions-2.2.5/.travis.yml 2019-10-20 17:41:01.000000000
+0200
+++ new/django-extensions-2.2.8/.travis.yml 2020-02-10 20:05:14.000000000
+0100
@@ -5,99 +5,98 @@
matrix:
fast_finish: true
include:
+ # Misc
- python: 2.7
env: TOXENV=py27-flake8
- - python: 3.5
- env: TOXENV=py35-flake8
- - python: 3.6
- env: TOXENV=py36-flake8
- - python: 3.7
- env: TOXENV=py37-flake8
- python: 3.8
env: TOXENV=py38-flake8
- - python: 3.7
+ - python: 3.8
env: TOXENV=precommit
- - python: 3.7
+ - python: 3.8
env: TOXENV=safety
- - python: 3.7
+ - python: 3.8
env: TOXENV=mypy
- - python: 3.7
+ - python: 3.8
env: TOXENV=compile-catalog
+ # Django 1.11: Python 2.7, 3.5, 3.6 or 3.7 / PyPy 2.x or 3.x
- python: 2.7
env: TOXENV=py27-dj111
- python: 3.5
env: TOXENV=py35-dj111
- - python: 3.5
- env: TOXENV=py35-dj21
- python: 3.6
env: TOXENV=py36-dj111
+ - python: pypy
+ env: TOXENV=pypy-dj111
+ - python: pypy3
+ env: TOXENV=pypy3-dj111
+ # Django 2.2: Python 3.5, 3.6, 3.7 or 3.8 / PyPy 3.x
+ - python: 3.5
+ env: TOXENV=py35-dj22
- python: 3.6
- env: TOXENV=py36-dj21
- - python: 3.6
- env: TOXENV=py36-djmaster
- - python: 3.7
- env: TOXENV=py37-dj21
+ env: TOXENV=py36-dj22
- python: 3.7
env: TOXENV=py37-dj22
- - python: 3.8
- env: TOXENV=py38-dj22
- - python: 3.7
- env: TOXENV=py37-dj30
- - python: 3.8
- env: TOXENV=py38-dj30
- python: 3.7
env: TOXENV=py37-dj22-postgres
services:
- postgresql
- - python: 3.8
- env: TOXENV=py38-dj22-postgres
- services:
- - postgresql
- python: 3.7
- env: TOXENV=py37-dj30-postgres
+ env: TOXENV=py37-dj22-mysql
services:
- - postgresql
+ - mysql
- python: 3.8
- env: TOXENV=py38-dj30-postgres
+ env: TOXENV=py38-dj22
+ - python: 3.8
+ env: TOXENV=py38-dj22-postgres
services:
- postgresql
- - python: 3.7
- env: TOXENV=py37-dj22-mysql
- services:
- - mysql
- python: 3.8
env: TOXENV=py38-dj22-mysql
services:
- mysql
+ - python: pypy3
+ env: TOXENV=pypy3-dj22
+ # Django 3.0: Python 3.6, 3.7 or 3.8 / PyPy 3.x
+ - python: 3.6
+ env: TOXENV=py36-dj30
+ - python: 3.7
+ env: TOXENV=py37-dj30
+ - python: 3.7
+ env: TOXENV=py37-dj30-postgres
+ services:
+ - postgresql
- python: 3.7
env: TOXENV=py37-dj30-mysql
services:
- mysql
- python: 3.8
+ env: TOXENV=py38-dj30
+ - python: 3.8
+ env: TOXENV=py38-dj30-postgres
+ services:
+ - postgresql
+ - python: 3.8
env: TOXENV=py38-dj30-mysql
services:
- mysql
+ - python: pypy3
+ env: TOXENV=pypy3-dj30
+ # Django master: Python 3.6, 3.7 or 3.8 / PyPy 3.x
+ - python: 3.6
+ env: TOXENV=py36-djmaster
- python: 3.7
env: TOXENV=py37-djmaster
- - python: 3.8
- env: TOXENV=py38-djmaster
- python: 3.7
env: TOXENV=py37-djmaster-postgres
services:
- postgresql
- python: 3.8
+ env: TOXENV=py38-djmaster
+ - python: 3.8
env: TOXENV=py38-djmaster-postgres
services:
- postgresql
- - python: pypy2.7-5.10.0
- env: TOXENV=pypy-dj111
- - python: pypy3.5-5.10.1
- env: TOXENV=pypy3-dj111
- - python: pypy3.5-5.10.1
- env: TOXENV=pypy3-dj20
- - python: pypy3.5-5.10.1
- env: TOXENV=pypy3-dj21
- - python: pypy3.5-5.10.1
+ - python: pypy3
env: TOXENV=pypy3-djmaster
allow_failures:
@@ -111,9 +110,9 @@
env: TOXENV=py37-djmaster-postgres
- python: 3.8
env: TOXENV=py38-djmaster-postgres
- - python: pypy3.5-5.10.1
+ - python: pypy3
env: TOXENV=pypy3-djmaster
- - python: pypy2.7-5.10.0
+ - python: pypy
env: TOXENV=pypy-dj111
install:
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/django-extensions-2.2.5/CHANGELOG.md
new/django-extensions-2.2.8/CHANGELOG.md
--- old/django-extensions-2.2.5/CHANGELOG.md 2019-10-20 17:41:01.000000000
+0200
+++ new/django-extensions-2.2.8/CHANGELOG.md 2020-02-10 20:05:14.000000000
+0100
@@ -1,6 +1,39 @@
Changelog
=========
+2.2.8
+-----
+
+Changes:
+ - 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
+
+
+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
+
+
2.2.5
-----
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/django-extensions-2.2.5/django_extensions/__init__.py
new/django-extensions-2.2.8/django_extensions/__init__.py
--- old/django-extensions-2.2.5/django_extensions/__init__.py 2019-10-20
17:41:01.000000000 +0200
+++ new/django-extensions-2.2.8/django_extensions/__init__.py 2020-02-10
20:05:14.000000000 +0100
@@ -1,5 +1,5 @@
# -*- coding: utf-8 -*-
-VERSION = (2, 2, 5)
+VERSION = (2, 2, 8)
def get_version(version):
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/django-extensions-2.2.5/django_extensions/auth/mixins.py
new/django-extensions-2.2.8/django_extensions/auth/mixins.py
--- old/django-extensions-2.2.5/django_extensions/auth/mixins.py
1970-01-01 01:00:00.000000000 +0100
+++ new/django-extensions-2.2.8/django_extensions/auth/mixins.py
2020-02-10 20:05:14.000000000 +0100
@@ -0,0 +1,15 @@
+# -*- coding: utf-8 -*-
+from django.contrib.auth.mixins import UserPassesTestMixin
+
+
+class ModelUserFieldPermissionMixin(UserPassesTestMixin):
+ model_permission_user_field = 'user'
+
+ def get_model_permission_user_field(self):
+ return self.model_permission_user_field
+
+ def test_func(self):
+ model_attr = self.get_model_permission_user_field()
+ current_user = self.request.user
+
+ return current_user == getattr(self.get_queryset().first(), model_attr)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/django-extensions-2.2.5/django_extensions/db/fields/__init__.py
new/django-extensions-2.2.8/django_extensions/db/fields/__init__.py
--- old/django-extensions-2.2.5/django_extensions/db/fields/__init__.py
2019-10-20 17:41:01.000000000 +0200
+++ new/django-extensions-2.2.8/django_extensions/db/fields/__init__.py
2020-02-10 20:05:14.000000000 +0100
@@ -23,7 +23,7 @@
from django.conf import settings
from django.core.exceptions import ImproperlyConfigured
-from django.db.models import DateTimeField, CharField, SlugField
+from django.db.models import DateTimeField, CharField, SlugField, Q
from django.db.models.constants import LOOKUP_SEP
from django.template.defaultfilters import slugify
from django.utils.crypto import get_random_string
@@ -66,9 +66,22 @@
for param in params:
kwargs[param] = getattr(model_instance, param, None)
+ # for support django 2.2+
+ query = Q()
+ constraints = getattr(model_instance._meta, 'constraints', None)
+ if constraints:
+ for constraint in constraints:
+ if self.attname in constraint.fields:
+ condition = {
+ field: getattr(model_instance, field, None)
+ for field in constraint.fields
+ if field != self.attname
+ }
+ query &= Q(**condition)
+
new = six.next(iterator)
kwargs[self.attname] = new
- while not new or queryset.filter(**kwargs):
+ while not new or queryset.filter(query, **kwargs):
new = six.next(iterator)
kwargs[self.attname] = new
setattr(model_instance, self.attname, new)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/django-extensions-2.2.5/django_extensions/management/commands/delete_squashed_migrations.py
new/django-extensions-2.2.8/django_extensions/management/commands/delete_squashed_migrations.py
---
old/django-extensions-2.2.5/django_extensions/management/commands/delete_squashed_migrations.py
2019-10-20 17:41:01.000000000 +0200
+++
new/django-extensions-2.2.8/django_extensions/management/commands/delete_squashed_migrations.py
2020-02-10 20:05:14.000000000 +0100
@@ -17,7 +17,6 @@
class Command(BaseCommand):
-
help = "Deletes left over migrations that have been replaced by a "
"squashed migration and converts squashed migration into a normal "
"migration. Modifies your source tree! Use with care!"
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/django-extensions-2.2.5/django_extensions/management/commands/pipchecker.py
new/django-extensions-2.2.8/django_extensions/management/commands/pipchecker.py
---
old/django-extensions-2.2.5/django_extensions/management/commands/pipchecker.py
2019-10-20 17:41:01.000000000 +0200
+++
new/django-extensions-2.2.8/django_extensions/management/commands/pipchecker.py
2020-02-10 20:05:14.000000000 +0100
@@ -8,7 +8,10 @@
from django.core.management.base import BaseCommand, CommandError
try:
- from pip._internal.download import PipSession
+ try:
+ from pip._internal.network.session import PipSession
+ except ImportError:
+ from pip._internal.download import PipSession
from pip._internal.req.req_file import parse_requirements
from pip._internal.utils.misc import get_installed_distributions
except ImportError:
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/django-extensions-2.2.5/django_extensions/management/commands/runserver_plus.py
new/django-extensions-2.2.8/django_extensions/management/commands/runserver_plus.py
---
old/django-extensions-2.2.5/django_extensions/management/commands/runserver_plus.py
2019-10-20 17:41:01.000000000 +0200
+++
new/django-extensions-2.2.8/django_extensions/management/commands/runserver_plus.py
2020-02-10 20:05:14.000000000 +0100
@@ -6,14 +6,12 @@
import re
import socket
import sys
-import time
import django
from django.conf import settings
from django.core.exceptions import ImproperlyConfigured
from django.core.management.base import BaseCommand, CommandError
from django.core.servers.basehttp import get_internal_wsgi_application
-from django.db.backends import utils
try:
from django.utils.autoreload import gen_filenames
except ImportError: # Django >=2.2
@@ -25,19 +23,15 @@
try:
if 'whitenoise.runserver_nostatic' in settings.INSTALLED_APPS:
USE_STATICFILES = False
- elif 'django.contrib.staticfiles' in settings.INSTALLED_APPS:
+ else:
from django.contrib.staticfiles.handlers import StaticFilesHandler
USE_STATICFILES = True
- elif 'staticfiles' in settings.INSTALLED_APPS:
- from staticfiles.handlers import StaticFilesHandler # noqa
- USE_STATICFILES = True
- else:
- USE_STATICFILES = False
except ImportError:
USE_STATICFILES = False
from django_extensions.management.technical_response import
null_technical_500_response
from django_extensions.management.utils import RedirectHandler, has_ipdb,
setup_logger, signalcommand
+from django_extensions.management.debug_cursor import
monkey_patch_cursordebugwrapper
naiveip_re = re.compile(r"""^(?:
@@ -79,6 +73,8 @@
help='Specifies an output file to send a copy of
all messages (not flushed immediately).')
parser.add_argument('--print-sql', action='store_true', default=False,
help="Print SQL queries as they're executed")
+ parser.add_argument('--print-sql-location', action='store_true',
default=False,
+ help="Show location in code where SQL query
generated from")
cert_group = parser.add_mutually_exclusive_group()
cert_group.add_argument('--cert', dest='cert_path', action="store",
type=str,
help='Deprecated alias for --cert-file
option.')
@@ -143,47 +139,6 @@
werklogger.addHandler(logredirect)
werklogger.propagate = False
- if options["print_sql"]:
- try:
- import sqlparse
- except ImportError:
- sqlparse = None # noqa
-
- try:
- import pygments.lexers
- import pygments.formatters
- except ImportError:
- pygments = None
-
- truncate = getattr(settings, 'RUNSERVER_PLUS_PRINT_SQL_TRUNCATE',
1000)
-
- class PrintQueryWrapper(utils.CursorDebugWrapper):
- def execute(self, sql, params=()):
- starttime = time.time()
- try:
- return utils.CursorWrapper.execute(self, sql, params)
- finally:
- execution_time = time.time() - starttime
- raw_sql = self.db.ops.last_executed_query(self.cursor,
sql, params)
-
- if sqlparse:
- raw_sql = raw_sql[:truncate]
- raw_sql = sqlparse.format(raw_sql,
reindent_aligned=True, truncate_strings=500)
-
- if pygments:
- raw_sql = pygments.highlight(
- raw_sql,
- pygments.lexers.get_lexer_by_name("sql"),
- pygments.formatters.TerminalFormatter()
- )
-
- logger.info(raw_sql)
- logger.info("")
- logger.info('[Execution time: %.6fs] [Database: %s]' %
(execution_time, self.db.alias))
- logger.info("")
-
- utils.CursorDebugWrapper = PrintQueryWrapper
-
pdb_option = options['pdb']
ipdb_option = options['ipdb']
pm = options['pm']
@@ -259,11 +214,13 @@
self.addr = '::1' if self.use_ipv6 else '127.0.0.1'
self._raw_ipv6 = True
- self.inner_run(options)
+ with monkey_patch_cursordebugwrapper(print_sql=options["print_sql"],
print_sql_location=options["print_sql_location"], logger=logger.info,
confprefix="RUNSERVER_PLUS"):
+ self.inner_run(options)
def inner_run(self, options):
try:
- from werkzeug import run_simple, DebuggedApplication
+ from werkzeug import run_simple
+ from werkzeug.debug import DebuggedApplication
from werkzeug.serving import WSGIRequestHandler as
_WSGIRequestHandler
# Set colored output
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/django-extensions-2.2.5/django_extensions/management/commands/shell_plus.py
new/django-extensions-2.2.8/django_extensions/management/commands/shell_plus.py
---
old/django-extensions-2.2.5/django_extensions/management/commands/shell_plus.py
2019-10-20 17:41:01.000000000 +0200
+++
new/django-extensions-2.2.8/django_extensions/management/commands/shell_plus.py
2020-02-10 20:05:14.000000000 +0100
@@ -2,17 +2,16 @@
import os
import six
import sys
-import time
import traceback
from django.conf import settings
from django.core.management.base import BaseCommand, CommandError
-from django.db.backends import utils
from django.utils.datastructures import OrderedSet
from six import PY3
from django_extensions.management.shells import import_objects
from django_extensions.management.utils import signalcommand
+from django_extensions.management.debug_cursor import
monkey_patch_cursordebugwrapper
def use_vi_mode():
@@ -90,6 +89,11 @@
help="Print SQL queries as they're executed"
)
parser.add_argument(
+ '--print-sql-location', action='store_true',
+ default=False,
+ help="Show location in code where SQL query generated from"
+ )
+ parser.add_argument(
'--dont-load', action='append', dest='dont_load', default=[],
help='Ignore autoloading of some apps/models. Can be used several
times.'
)
@@ -108,6 +112,10 @@
dest='no_browser',
help='Don\'t open the notebook in a browser after startup.'
)
+ parser.add_argument(
+ '-c', '--command',
+ help='Instead of opening an interactive shell, run a command as
Django and exit.',
+ )
def run_from_argv(self, argv):
if '--' in argv[2:]:
@@ -164,17 +172,20 @@
return run_kernel
def get_notebook(self, options):
- from IPython import release
+ try:
+ from IPython import release
+ except ImportError:
+ return traceback.format_exc()
try:
from notebook.notebookapp import NotebookApp
except ImportError:
if release.version_info[0] >= 7:
- raise
+ return traceback.format_exc()
try:
from IPython.html.notebookapp import NotebookApp
except ImportError:
if release.version_info[0] >= 3:
- raise
+ return traceback.format_exc()
try:
from IPython.frontend.html.notebook import notebookapp
NotebookApp = notebookapp.NotebookApp
@@ -206,7 +217,7 @@
manage_py_dir, manage_py =
os.path.split(os.path.realpath(sys.argv[0]))
- if manage_py == 'manage.py' and os.path.isdir(manage_py_dir) and
manage_py_dir != os.getcwd():
+ if manage_py == 'manage.py' and os.path.isdir(manage_py_dir):
pythonpath = ks.env.get('PYTHONPATH',
os.environ.get('PYTHONPATH', ''))
pythonpath = pythonpath.split(os.pathsep)
if manage_py_dir not in pythonpath:
@@ -436,116 +447,71 @@
use_ptipython = options['ptipython']
verbosity = options["verbosity"]
print_sql = getattr(settings, 'SHELL_PLUS_PRINT_SQL', False)
- truncate = getattr(settings, 'SHELL_PLUS_PRINT_SQL_TRUNCATE', 1000)
-
- if options["print_sql"] or print_sql:
-
- # Code from http://gist.github.com/118990
- try:
- import sqlparse
- sqlparse_format_kwargs_defaults = dict(
- reindent_aligned=True,
- truncate_strings=500,
- )
- sqlparse_format_kwargs = getattr(settings,
'SHELL_PLUS_SQLPARSE_FORMAT_KWARGS', sqlparse_format_kwargs_defaults)
- except ImportError:
- sqlparse = None
-
- try:
- import pygments.lexers
- import pygments.formatters
-
- pygments_formatter = getattr(settings,
'SHELL_PLUS_PYGMENTS_FORMATTER', pygments.formatters.TerminalFormatter)
- pygments_formatter_kwargs = getattr(settings,
'SHELL_PLUS_PYGMENTS_FORMATTER_KWARGS', {})
- except ImportError:
- pygments = None
+ with monkey_patch_cursordebugwrapper(print_sql=options["print_sql"] or
print_sql, print_sql_location=options["print_sql_location"],
confprefix="SHELL_PLUS"):
+ shells = (
+ ('notebook', self.get_notebook),
+ ('ptipython', self.get_ptipython),
+ ('ptpython', self.get_ptpython),
+ ('bpython', self.get_bpython),
+ ('ipython', self.get_ipython),
+ ('plain', self.get_plain),
+ ('idle', self.get_idle),
+ )
+ SETTINGS_SHELL_PLUS = getattr(settings, 'SHELL_PLUS', None)
- class PrintQueryWrapper(utils.CursorDebugWrapper):
- def execute(self, sql, params=()):
- starttime = time.time()
- try:
- return utils.CursorWrapper.execute(self, sql, params)
- finally:
- execution_time = time.time() - starttime
- raw_sql = self.db.ops.last_executed_query(self.cursor,
sql, params)
- if truncate:
- raw_sql = raw_sql[:truncate]
-
- if sqlparse:
- raw_sql = sqlparse.format(raw_sql,
**sqlparse_format_kwargs)
-
- if pygments:
- raw_sql = pygments.highlight(
- raw_sql,
- pygments.lexers.get_lexer_by_name("sql"),
-
pygments_formatter(**pygments_formatter_kwargs),
- )
-
- print(raw_sql)
- print("")
- print('Execution time: %.6fs [Database: %s]' %
(execution_time, self.db.alias))
- print("")
-
- utils.CursorDebugWrapper = PrintQueryWrapper
-
- shells = (
- ('ptipython', self.get_ptipython),
- ('ptpython', self.get_ptpython),
- ('bpython', self.get_bpython),
- ('ipython', self.get_ipython),
- ('plain', self.get_plain),
- ('idle', self.get_idle),
- )
- SETTINGS_SHELL_PLUS = getattr(settings, 'SHELL_PLUS', None)
-
- shell = None
- shell_name = "any"
- self.set_application_name(options)
- if use_kernel:
- shell = self.get_kernel(options)
- shell_name = "IPython Kernel"
- elif use_notebook:
- shell = self.get_notebook(options)
- shell_name = "IPython Notebook"
- elif use_plain:
- shell = self.get_plain(options)
- shell_name = "plain"
- elif use_ipython:
- shell = self.get_ipython(options)
- shell_name = "IPython"
- elif use_bpython:
- shell = self.get_bpython(options)
- shell_name = "BPython"
- elif use_ptpython:
- shell = self.get_ptpython(options)
- shell_name = "ptpython"
- elif use_ptipython:
- shell = self.get_ptipython(options)
- shell_name = "ptipython"
- elif use_idle:
- shell = self.get_idle(options)
- shell_name = "idle"
- elif SETTINGS_SHELL_PLUS:
- shell_name = SETTINGS_SHELL_PLUS
- shell = dict(shells)[shell_name](options)
- else:
- for shell_name, func in shells:
- if verbosity > 2:
- print(self.style.NOTICE("Trying shell: %s" % shell_name))
- shell = func(options)
- if callable(shell):
- if verbosity > 1:
- print(self.style.NOTICE("Using shell: %s" %
shell_name))
- break
+ shell = None
+ shell_name = "any"
+ self.set_application_name(options)
+ if use_kernel:
+ shell = self.get_kernel(options)
+ shell_name = "IPython Kernel"
+ elif use_notebook:
+ shell = self.get_notebook(options)
+ shell_name = "IPython Notebook"
+ elif use_plain:
+ shell = self.get_plain(options)
+ shell_name = "plain"
+ elif use_ipython:
+ shell = self.get_ipython(options)
+ shell_name = "IPython"
+ elif use_bpython:
+ shell = self.get_bpython(options)
+ shell_name = "BPython"
+ elif use_ptpython:
+ shell = self.get_ptpython(options)
+ shell_name = "ptpython"
+ elif use_ptipython:
+ shell = self.get_ptipython(options)
+ shell_name = "ptipython"
+ elif use_idle:
+ shell = self.get_idle(options)
+ shell_name = "idle"
+ elif SETTINGS_SHELL_PLUS:
+ shell_name = SETTINGS_SHELL_PLUS
+ shell = dict(shells)[shell_name](options)
+ else:
+ for shell_name, func in shells:
+ if verbosity > 2:
+ print(self.style.NOTICE("Trying shell: %s" %
shell_name))
+ shell = func(options)
+ if callable(shell):
+ if verbosity > 1:
+ print(self.style.NOTICE("Using shell: %s" %
shell_name))
+ break
+
+ if not callable(shell):
+ if shell:
+ print(shell)
+ print(self.style.ERROR("Could not load %s interactive Python
environment." % shell_name))
+ return
- if not callable(shell):
- if shell:
- print(shell)
- print(self.style.ERROR("Could not load %s interactive Python
environment." % shell_name))
- return
+ if self.tests_mode:
+ return 130
- if self.tests_mode:
- return 130
+ if options['command']:
+ imported_objects = self.get_imported_objects(options)
+ exec(options['command'], {}, imported_objects)
+ return
- shell()
+ shell()
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/django-extensions-2.2.5/django_extensions/management/debug_cursor.py
new/django-extensions-2.2.8/django_extensions/management/debug_cursor.py
--- old/django-extensions-2.2.5/django_extensions/management/debug_cursor.py
1970-01-01 01:00:00.000000000 +0100
+++ new/django-extensions-2.2.8/django_extensions/management/debug_cursor.py
2020-02-10 20:05:14.000000000 +0100
@@ -0,0 +1,110 @@
+# -*- coding: utf-8 -*-
+import six
+import time
+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
+
+
+@contextmanager
+def monkey_patch_cursordebugwrapper(print_sql=None, print_sql_location=False,
truncate=None, logger=six.print_, confprefix="DJANGO_EXTENSIONS"):
+ if not print_sql:
+ yield
+ else:
+ truncate = getattr(settings, '%s_PRINT_SQL_TRUNCATE' % confprefix,
1000)
+
+ # Code orginally from http://gist.github.com/118990
+ sqlparse = None
+ if getattr(settings, '%s_SQLPARSE_ENABLED' % confprefix, True):
+ try:
+ import sqlparse
+
+ sqlparse_format_kwargs_defaults = dict(
+ reindent_aligned=True,
+ truncate_strings=500,
+ )
+ sqlparse_format_kwargs = getattr(settings,
'%s_SQLPARSE_FORMAT_KWARGS' % confprefix, sqlparse_format_kwargs_defaults)
+ except ImportError:
+ sqlparse = None
+
+ pygments = None
+ if getattr(settings, '%s_PYGMENTS_ENABLED' % confprefix, True):
+ try:
+ import pygments.lexers
+ import pygments.formatters
+
+ pygments_formatter = getattr(settings, '%s_PYGMENTS_FORMATTER'
% confprefix, pygments.formatters.TerminalFormatter)
+ pygments_formatter_kwargs = getattr(settings,
'%s_PYGMENTS_FORMATTER_KWARGS' % confprefix, {})
+ except ImportError:
+ pass
+
+ class PrintQueryWrapperMixin:
+ def execute(self, sql, params=()):
+ starttime = time.time()
+ try:
+ return utils.CursorWrapper.execute(self, sql, params)
+ finally:
+ execution_time = time.time() - starttime
+ raw_sql = self.db.ops.last_executed_query(self.cursor,
sql, params)
+ if truncate:
+ raw_sql = raw_sql[:truncate]
+
+ if sqlparse:
+ raw_sql = sqlparse.format(raw_sql,
**sqlparse_format_kwargs)
+
+ if pygments:
+ raw_sql = pygments.highlight(
+ raw_sql,
+ pygments.lexers.get_lexer_by_name("sql"),
+ pygments_formatter(**pygments_formatter_kwargs),
+ )
+
+ logger(raw_sql)
+ logger("Execution time: %.6fs [Database: %s]" %
(execution_time, self.db.alias))
+ if print_sql_location:
+ logger("Location of SQL Call:")
+ logger(''.join(traceback.format_stack()))
+
+ _CursorDebugWrapper = utils.CursorDebugWrapper
+
+ class PrintCursorQueryWrapper(PrintQueryWrapperMixin,
_CursorDebugWrapper):
+ pass
+
+ try:
+ from django.db import connection
+ _force_debug_cursor = connection.force_debug_cursor
+ except Exception:
+ connection = None
+
+ 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
+
+ class
PostgreSQLPrintCursorDebugWrapper(PrintQueryWrapperMixin,
_PostgreSQLCursorDebugWrapper):
+ pass
+ except (ImproperlyConfigured, TypeError):
+ postgresql_base = None
+
+ if postgresql_base:
+ postgresql_base.CursorDebugWrapper =
PostgreSQLPrintCursorDebugWrapper
+
+ if connection:
+ connection.force_debug_cursor = True
+
+ yield
+
+ utils.CursorDebugWrapper = _CursorDebugWrapper
+
+ if postgresql_base:
+ postgresql_base.CursorDebugWrapper = _PostgreSQLCursorDebugWrapper
+
+ if connection:
+ connection.force_debug_cursor = _force_debug_cursor
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/django-extensions-2.2.5/django_extensions/management/shells.py
new/django-extensions-2.2.8/django_extensions/management/shells.py
--- old/django-extensions-2.2.5/django_extensions/management/shells.py
2019-10-20 17:41:01.000000000 +0200
+++ new/django-extensions-2.2.8/django_extensions/management/shells.py
2020-02-10 20:05:14.000000000 +0100
@@ -139,7 +139,7 @@
print(style.ERROR("Unable to import %r" % directive))
except TypeError:
if not quiet_load:
- print(style.ERROR("Unable to import %r from %r" %
directive))
+ print(style.ERROR("Unable to import %r from %r" %
(directive[1], directive[0])))
return imported_objects
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/django-extensions-2.2.5/django_extensions/settings.py
new/django-extensions-2.2.8/django_extensions/settings.py
--- old/django-extensions-2.2.5/django_extensions/settings.py 2019-10-20
17:41:01.000000000 +0200
+++ new/django-extensions-2.2.8/django_extensions/settings.py 2020-02-10
20:05:14.000000000 +0100
@@ -13,6 +13,7 @@
DEFAULT_MYSQL_ENGINES = (
'django.db.backends.mysql',
'django.contrib.gis.db.backends.mysql',
+ 'mysql.connector.django',
)
DEFAULT_POSTGRESQL_ENGINES = (
'django.db.backends.postgresql',
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/django-extensions-2.2.5/docs/conf.py
new/django-extensions-2.2.8/docs/conf.py
--- old/django-extensions-2.2.5/docs/conf.py 2019-10-20 17:41:01.000000000
+0200
+++ new/django-extensions-2.2.8/docs/conf.py 2020-02-10 20:05:14.000000000
+0100
@@ -47,7 +47,7 @@
# The short X.Y version.
version = '2.2'
# The full version, including alpha/beta/rc tags.
-release = '2.2.5'
+release = '2.2.8'
# The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages.
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/django-extensions-2.2.5/docs/index.rst
new/django-extensions-2.2.8/docs/index.rst
--- old/django-extensions-2.2.5/docs/index.rst 2019-10-20 17:41:01.000000000
+0200
+++ new/django-extensions-2.2.8/docs/index.rst 2020-02-10 20:05:14.000000000
+0100
@@ -31,6 +31,9 @@
$ cd django-extensions
$ python setup.py install
+Then you will need to add the *django_extensions* application to the
+``INSTALLED_APPS`` setting of your Django project *settings.py* file.
+
For more detailed instructions check out our :doc:`installation_instructions`.
Enjoy.
Compatibility with versions of Python and Django
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/django-extensions-2.2.5/docs/permissions.rst
new/django-extensions-2.2.8/docs/permissions.rst
--- old/django-extensions-2.2.5/docs/permissions.rst 1970-01-01
01:00:00.000000000 +0100
+++ new/django-extensions-2.2.8/docs/permissions.rst 2020-02-10
20:05:14.000000000 +0100
@@ -0,0 +1,42 @@
+Permissions
+==============
+
+:synopsis: Permissions Mixins to limit access and model instances in a view.
+
+Introduction
+------------
+Django Extensions offers mixins for Class Based Views that make it easier to
+query and limit access to certain views.
+
+Current Mixins
+---------------------------------
+* *UserPermissionMixin* - A Class Based View mixin that limits the
accessibility to the view based on the "owner" of the view.
+This will check if the currently logged in user (``self.request.user``)
matches the owner of the model instance.
+By default, the "owner" will be called "user".
+
+.. code-block:: python
+
+ # models.py
+
+ from django.db import models
+ from django.conf import settings
+
+ class MyModel(models.Model):
+ author = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete =
models.CASCADE)
+ content = models.TextField()
+
+
+.. code-block:: python
+
+ # views.py
+
+ from django.views.generic import UpdateView
+
+ from django_extensions.auth.mixins import UserPermissionMixin
+
+ from .models import MyModel
+
+ class MyModelUpdateView(UserPermissionMixin, UpdateView):
+ model = MyModel
+ template_name = 'mymodels/update.html'
+ model_permission_user_field = 'author'
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/django-extensions-2.2.5/docs/print_settings.rst
new/django-extensions-2.2.8/docs/print_settings.rst
--- old/django-extensions-2.2.5/docs/print_settings.rst 2019-10-20
17:41:01.000000000 +0200
+++ new/django-extensions-2.2.8/docs/print_settings.rst 2020-02-10
20:05:14.000000000 +0100
@@ -1,7 +1,7 @@
print_settings
==============
-:synopsis: Django managment command similar to ``diffsettings`` but shows
*selected* active Django settings or *all* if no args passed.
+:synopsis: Django management command similar to ``diffsettings`` but shows
*selected* active Django settings or *all* if no args passed.
Introduction
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/django-extensions-2.2.5/docs/runserver_plus.rst
new/django-extensions-2.2.8/docs/runserver_plus.rst
--- old/django-extensions-2.2.5/docs/runserver_plus.rst 2019-10-20
17:41:01.000000000 +0200
+++ new/django-extensions-2.2.8/docs/runserver_plus.rst 2020-02-10
20:05:14.000000000 +0100
@@ -190,6 +190,20 @@
},
}
+Other configuration options and their defaults include:
+
+::
+
+ # Truncate SQL queries to this many characters (None means no truncation)
+ RUNSERVER_PLUS_PRINT_SQL_TRUNCATE = 1000
+
+ # After how many seconds auto-reload should scan for updates in poller-mode
+ RUNSERVERPLUS_POLLER_RELOADER_INTERVAL = 1
+
+ # Werkzeug reloader type [auto, watchdog, or stat]
+ RUNSERVERPLUS_POLLER_RELOADER_TYPE = 'auto'
+
+
IO Calls and CPU Usage
^^^^^^^^^^^^^^^^^^^^^^
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/django-extensions-2.2.5/setup.cfg
new/django-extensions-2.2.8/setup.cfg
--- old/django-extensions-2.2.5/setup.cfg 2019-10-20 17:41:01.000000000
+0200
+++ new/django-extensions-2.2.8/setup.cfg 2020-02-10 20:05:14.000000000
+0100
@@ -8,7 +8,7 @@
[tool:pytest]
norecursedirs=venv* .tox .eggs build dist django_extensions.egg-info
django_extensions/mongodb
-addopts = --doctest-modules --ignore=django_extensions/db/fields/encrypted.py
--doctest-ignore-import-errors
+addopts = --doctest-modules --ignore=django_extensions/db/fields/encrypted.py
--doctest-ignore-import-errors --nomigrations
[isort]
combine_as_imports = true
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/django-extensions-2.2.5/setup.py
new/django-extensions-2.2.8/setup.py
--- old/django-extensions-2.2.5/setup.py 2019-10-20 17:41:01.000000000
+0200
+++ new/django-extensions-2.2.8/setup.py 2020-02-10 20:05:14.000000000
+0100
@@ -173,6 +173,7 @@
'Framework :: Django :: 1.11',
'Framework :: Django :: 2.1',
'Framework :: Django :: 2.2',
+ 'Framework :: Django :: 3.0',
'Intended Audience :: Developers',
'License :: OSI Approved :: MIT License',
'Operating System :: OS Independent',
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/django-extensions-2.2.5/tests/auth/test_mixins.py
new/django-extensions-2.2.8/tests/auth/test_mixins.py
--- old/django-extensions-2.2.5/tests/auth/test_mixins.py 1970-01-01
01:00:00.000000000 +0100
+++ new/django-extensions-2.2.8/tests/auth/test_mixins.py 2020-02-10
20:05:14.000000000 +0100
@@ -0,0 +1,47 @@
+# -*- coding: utf-8 -*-
+from django.test import TestCase, RequestFactory
+from django.http import HttpResponse
+from django.views.generic import DetailView
+from django.contrib.auth import get_user_model
+from django.contrib.auth.models import AnonymousUser
+from django.core.exceptions import PermissionDenied
+
+from django_extensions.auth.mixins import ModelUserFieldPermissionMixin
+
+from tests.testapp.models import HasOwnerModel
+
+
+class EmptyResponseView(DetailView):
+ model = HasOwnerModel
+
+ def get(self, request, *args, **kwargs):
+ return HttpResponse()
+
+
+class OwnerView(ModelUserFieldPermissionMixin, EmptyResponseView):
+ model_permission_user_field = 'owner'
+
+
+class ModelUserFieldPermissionMixinTests(TestCase):
+ factory = RequestFactory()
+ User = get_user_model()
+
+ @classmethod
+ def setUpTestData(cls):
+ cls.user = cls.User.objects.create(username="Joe", password="pass")
+ cls.ownerModel = HasOwnerModel.objects.create(owner=cls.user)
+
+ # Test if owner model has access
+ def test_permission_pass(self):
+ request = self.factory.get('/permission-required/' +
str(self.ownerModel.id))
+ request.user = self.user
+ resp = OwnerView.as_view()(request)
+ self.assertEqual(resp.status_code, 200)
+
+ # # Test if non owner model is redirected
+ def test_permission_denied_and_redirect(self):
+ request = self.factory.get('/permission-required/' +
str(self.ownerModel.id))
+ request.user = AnonymousUser()
+ resp = OwnerView.as_view()(request)
+ self.assertRaises(PermissionDenied)
+ self.assertEqual(resp.status_code, 302)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/django-extensions-2.2.5/tests/db/fields/test_uniq_field_mixin.py
new/django-extensions-2.2.8/tests/db/fields/test_uniq_field_mixin.py
--- old/django-extensions-2.2.5/tests/db/fields/test_uniq_field_mixin.py
2019-10-20 17:41:01.000000000 +0200
+++ new/django-extensions-2.2.8/tests/db/fields/test_uniq_field_mixin.py
2020-02-10 20:05:14.000000000 +0100
@@ -131,7 +131,7 @@
mocked_qs_all.assert_called_with()
def test_find_unique(self):
- def filter_func(**kwargs):
+ def filter_func(*args, **kwargs):
uniq_field = kwargs.get('uniq_field')
if uniq_field == 'a':
return mocked_qs
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/django-extensions-2.2.5/tests/management/commands/shell_plus_tests/test_shell_plus.py
new/django-extensions-2.2.8/tests/management/commands/shell_plus_tests/test_shell_plus.py
---
old/django-extensions-2.2.5/tests/management/commands/shell_plus_tests/test_shell_plus.py
2019-10-20 17:41:01.000000000 +0200
+++
new/django-extensions-2.2.8/tests/management/commands/shell_plus_tests/test_shell_plus.py
2020-02-10 20:05:14.000000000 +0100
@@ -1,7 +1,12 @@
# -*- coding: utf-8 -*-
import os
+import re
+import six
+import django
+import pytest
import inspect
+from django.core.management import call_command
from django.db.models import Model
from django.test import override_settings
@@ -9,6 +14,26 @@
from django_extensions.management.shells import SHELL_PLUS_DJANGO_IMPORTS
[email protected](django.VERSION < (2, 0), reason="This test works only on
Django greater than 2.0.0")
[email protected]_db()
+@override_settings(SHELL_PLUS_SQLPARSE_ENABLED=False,
SHELL_PLUS_PYGMENTS_ENABLED=False)
+def test_shell_plus_print_sql(capsys):
+ out = six.StringIO()
+ try:
+ from django.db import connection
+ from django.db.backends import utils
+ CursorDebugWrapper = utils.CursorDebugWrapper
+ force_debug_cursor = True if connection.force_debug_cursor else False
+ call_command("shell_plus", plain=True, print_sql=True,
command="User.objects.all().exists()")
+ finally:
+ utils.CursorDebugWrapper = CursorDebugWrapper
+ connection.force_debug_cursor = force_debug_cursor
+
+ out, err = capsys.readouterr()
+
+ assert re.search(r"SELECT\s+.+\s+FROM\s+.auth_user.\s+LIMIT\s+1", out)
+
+
def test_shell_plus_plain_startup():
parser = shell_plus.Command().create_parser("test", "shell_plus")
args = ["--plain"]
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/django-extensions-2.2.5/tests/management/commands/test_delete_squashed_migrations.py
new/django-extensions-2.2.8/tests/management/commands/test_delete_squashed_migrations.py
---
old/django-extensions-2.2.5/tests/management/commands/test_delete_squashed_migrations.py
2019-10-20 17:41:01.000000000 +0200
+++
new/django-extensions-2.2.8/tests/management/commands/test_delete_squashed_migrations.py
2020-02-10 20:05:14.000000000 +0100
@@ -7,7 +7,7 @@
from django import get_version
from django.core.management import CommandError, call_command
from django.db import models
-from django.test import TestCase
+from django.test import TestCase, override_settings
from tests import testapp_with_appconfig
MIGRATIONS_DIR = os.path.join(testapp_with_appconfig.__path__[0], 'migrations')
@@ -18,6 +18,7 @@
from mock import patch
+@override_settings(MIGRATION_MODULES={'testapp_with_appconfig':
'tests.testapp_with_appconfig.migrations'})
class BaseDeleteSquashedMigrationsTestCase(TestCase):
def migration_exists(self, filename):
return os.path.exists(os.path.join(MIGRATIONS_DIR, filename))
@@ -39,6 +40,7 @@
os.remove(os.path.join(root, filename))
[email protected]
@pytest.mark.skipif(
LooseVersion(get_version()) <= LooseVersion('2.0.0'),
reason="This test works only on Django greater than 2.0.0",
@@ -79,8 +81,7 @@
CommandError,
"More than one migration matches '0001' in app
'testapp_with_appconfig'. Please be more specific."):
- call_command('delete_squashed_migrations',
'testapp_with_appconfig',
- '0001')
+ call_command('delete_squashed_migrations',
'testapp_with_appconfig', '0001')
def test_should_raise_CommandEror_if_squashed_migration_not_found(self):
class NameModel(models.Model):
@@ -109,6 +110,7 @@
'0002')
[email protected]
@pytest.mark.skipif(
LooseVersion(get_version()) <= LooseVersion('2.0.0'),
reason="This test works only on Django greater than 2.0.0",
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/django-extensions-2.2.5/tests/templatetags/test_highlighting.py
new/django-extensions-2.2.8/tests/templatetags/test_highlighting.py
--- old/django-extensions-2.2.5/tests/templatetags/test_highlighting.py
2019-10-20 17:41:01.000000000 +0200
+++ new/django-extensions-2.2.8/tests/templatetags/test_highlighting.py
2020-02-10 20:05:14.000000000 +0100
@@ -35,7 +35,7 @@
print("Love is colder than death")
{% endhighlight %}"""
expected_result = '''<div class="predesc"><span>Excerpt:
blah.py</span></div><div class="highlight"><pre><span></span><span
class="k">def</span> <span class="nf">need_food</span><span
class="p">(</span><span class="bp">self</span><span class="p">):</span>
- <span class="k">print</span><span class="p">(</span><span
class="s2">"Love is colder than death"</span><span class="p">)</span>
+ <span class="nb">print</span><span class="p">(</span><span
class="s2">"Love is colder than death"</span><span class="p">)</span>
</pre></div>'''
result = Template(content).render(self.ctx)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/django-extensions-2.2.5/tests/test_autoslug_fields.py
new/django-extensions-2.2.8/tests/test_autoslug_fields.py
--- old/django-extensions-2.2.5/tests/test_autoslug_fields.py 2019-10-20
17:41:01.000000000 +0200
+++ new/django-extensions-2.2.8/tests/test_autoslug_fields.py 2020-02-10
20:05:14.000000000 +0100
@@ -14,7 +14,8 @@
ChildSluggedTestModel, CustomFuncPrecedenceSluggedTestModel,
CustomFuncSluggedTestModel,
FKSluggedTestModel, FKSluggedTestModelCallable, FunctionSluggedTestModel,
ModelMethodSluggedTestModel, SluggedTestModel,
SluggedTestNoOverwriteOnAddModel,
- OverridedFindUniqueModel,
+ OverridedFindUniqueModel, SluggedWithConstraintsTestModel,
+ SluggedWithUniqueTogetherTestModel,
)
@@ -226,6 +227,35 @@
self.assertTrue(hasattr(m._meta.get_field('slug'), 'slugify_function'))
self.assertEqual(m._meta.get_field('slug').slugify_function('TEST'),
'test')
+ def test_auto_create_slug_with_unique_together(self):
+ m = SluggedWithUniqueTogetherTestModel(title='foo',
category='self-introduction')
+ m.save()
+ self.assertEqual(m.slug, 'foo')
+
+ m = SluggedWithUniqueTogetherTestModel(title='foo', category='review')
+ m.save()
+ self.assertEqual(m.slug, 'foo')
+
+ # check if satisfy database integrity
+ m = SluggedWithUniqueTogetherTestModel(title='foo', category='review')
+ m.save()
+ self.assertEqual(m.slug, 'foo-2')
+
+ @pytest.mark.skipif(django.VERSION < (2, 2), reason="This test works only
on Django greater than 2.2.0")
+ def test_auto_create_slug_with_constraints(self):
+ m = SluggedWithConstraintsTestModel(title='foo',
category='self-introduction')
+ m.save()
+ self.assertEqual(m.slug, 'foo')
+
+ m = SluggedWithConstraintsTestModel(title='foo', category='review')
+ m.save()
+ self.assertEqual(m.slug, 'foo')
+
+ # check if satisfy database integrity
+ m = SluggedWithConstraintsTestModel(title='foo', category='review')
+ m.save()
+ self.assertEqual(m.slug, 'foo-2')
+
class MigrationTest(TestCase):
def safe_exec(self, string, value=None):
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/django-extensions-2.2.5/tests/testapp/models.py
new/django-extensions-2.2.8/tests/testapp/models.py
--- old/django-extensions-2.2.5/tests/testapp/models.py 2019-10-20
17:41:01.000000000 +0200
+++ new/django-extensions-2.2.8/tests/testapp/models.py 2020-02-10
20:05:14.000000000 +0100
@@ -1,5 +1,8 @@
# -*- coding: utf-8 -*-
+import django
+
from django.db import models
+from django.contrib.auth import get_user_model
from django_extensions.db.fields import AutoSlugField,
ModificationDateTimeField, RandomCharField, ShortUUIDField
from django_extensions.db.fields.json import JSONField
@@ -7,6 +10,9 @@
from .fields import UniqField
+if django.VERSION >= (2, 2):
+ from django.db.models import UniqueConstraint
+
class Secret(models.Model):
name = models.CharField(blank=True, max_length=255, null=True)
@@ -146,6 +152,32 @@
app_label = 'django_extensions'
+class SluggedWithConstraintsTestModel(models.Model):
+ title = models.CharField(max_length=42)
+ slug = AutoSlugField(populate_from='title')
+ category = models.CharField(max_length=20, null=True)
+
+ class Meta:
+ app_label = 'django_extensions'
+ if django.VERSION >= (2, 2):
+ constraints = [
+ UniqueConstraint(
+ fields=['slug', 'category'],
+ name="unique_slug_and_category",
+ ),
+ ]
+
+
+class SluggedWithUniqueTogetherTestModel(models.Model):
+ title = models.CharField(max_length=42)
+ slug = AutoSlugField(populate_from='title')
+ category = models.CharField(max_length=20, null=True)
+
+ class Meta:
+ app_label = 'django_extensions'
+ unique_together = ['slug', 'category']
+
+
class OverridedFindUniqueAutoSlugField(AutoSlugField):
def find_unique(self, model_instance, field, iterator, *args):
self.overrided = True
@@ -426,3 +458,11 @@
class Meta:
app_label = 'django_extensions'
+
+
+class HasOwnerModel(models.Model):
+ content = models.TextField(default="")
+ owner = models.ForeignKey(get_user_model(), on_delete=models.CASCADE)
+
+ class Meta:
+ app_label = 'django_extensions'
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/django-extensions-2.2.5/tox.ini
new/django-extensions-2.2.8/tox.ini
--- old/django-extensions-2.2.5/tox.ini 2019-10-20 17:41:01.000000000 +0200
+++ new/django-extensions-2.2.8/tox.ini 2020-02-10 20:05:14.000000000 +0100
@@ -5,100 +5,90 @@
[tox]
envlist =
- precommit
- safety
- mypy
- {py27,py34,py35,py36,py37}-flake8
- {py27,py34,py35,py36,py37,pypy}-dj111
- {py34,py35,py36,py37,pypy}-dj20
- {py35,py36,py37,pypy}-dj21
- {py35,py36,py37,pypy}-dj22
- {py36,py37}-dj30
- {py35,py36,py37,pypy}-djmaster
- py37-dj22-postgres
- py37-dj22-mysql
- py37-djmaster-postgres
+ precommit
+ safety
+ mypy
+ {py27,py37,py38}-flake8
+ {py27,py34,py35,py36,py37,pypy}-dj111
+ {py34,py35,py36,py37,pypy}-dj20
+ {py35,py36,py37,pypy}-dj21
+ {py35,py36,py37,pypy}-dj22
+ {py36,py37}-dj30
+ {py35,py36,py37,pypy}-djmaster
+ py37-dj22-postgres
+ py37-dj22-mysql
+ py37-djmaster-postgres
[testenv]
commands = make test
whitelist_externals = make
passenv =
- DJANGO_EXTENSIONS_DATABASE_ENGINE
- DJANGO_EXTENSIONS_DATABASE_NAME
- DJANGO_EXTENSIONS_DATABASE_USER
+ DJANGO_EXTENSIONS_DATABASE_ENGINE
+ DJANGO_EXTENSIONS_DATABASE_NAME
+ DJANGO_EXTENSIONS_DATABASE_USER
setenv =
- postgres: DJANGO_EXTENSIONS_DATABASE_ENGINE =
{env:DJANGO_EXTENSIONS_DATABASE_ENGINE:django.db.backends.postgresql}
- postgres: DJANGO_EXTENSIONS_DATABASE_NAME =
{env:DJANGO_EXTENSIONS_DATABASE_NAME:django_extensions_test}
- mysql: DJANGO_EXTENSIONS_DATABASE_ENGINE =
{env:DJANGO_EXTENSIONS_DATABASE_ENGINE:django.db.backends.mysql}
- mysql: DJANGO_EXTENSIONS_DATABASE_NAME =
{env:DJANGO_EXTENSIONS_DATABASE_NAME:django_extensions_test}
-
-deps =
- dj111: Django>=1.11,<2.0
- dj20: Django>=2.0,<2.1
- dj21: Django>=2.1,<2.2
- dj22: Django==2.2
- dj30: Django>=3.0a1,<3.1
- djmaster: https://github.com/django/django/archive/master.tar.gz
- shortuuid
- werkzeug
- django_pdb
- python-dateutil
- pytest-django
- pytest-cov
+ postgres: DJANGO_EXTENSIONS_DATABASE_ENGINE =
{env:DJANGO_EXTENSIONS_DATABASE_ENGINE:django.db.backends.postgresql}
+ postgres: DJANGO_EXTENSIONS_DATABASE_NAME =
{env:DJANGO_EXTENSIONS_DATABASE_NAME:django_extensions_test}
+ mysql: DJANGO_EXTENSIONS_DATABASE_ENGINE =
{env:DJANGO_EXTENSIONS_DATABASE_ENGINE:django.db.backends.mysql}
+ mysql: DJANGO_EXTENSIONS_DATABASE_NAME =
{env:DJANGO_EXTENSIONS_DATABASE_NAME:django_extensions_test}
+
+deps =
+ dj111: Django>=1.11,<2.0
+ dj20: Django>=2.0,<2.1
+ dj21: Django>=2.1,<2.2
+ dj22: Django==2.2
+ dj30: Django>=3.0a1,<3.1
+ djmaster: https://github.com/django/django/archive/master.tar.gz
+ shortuuid
+ werkzeug
+ django_pdb
+ python-dateutil
+ pytest-django
+ pytest-cov
factory-boy
- py27: python-keyczar
+ py27: python-keyczar
py{34,35,36,37}: python3-keyczar
requests
- mock
+ mock
pygments
postgres: psycopg2-binary
mysql: mysqlclient
[testenv:precommit]
deps =
- pre-commit
+ pre-commit
commands = pre-commit run -a
[testenv:safety]
deps =
- safety
+ safety
commands = safety check --full-report
[testenv:mypy]
deps =
- mypy
+ mypy
commands = mypy --ignore-missing-imports django_extensions
[testenv:compile-catalog]
whitelist_externals =
- make
+ make
deps =
- Babel
+ Babel
commands =
- make compile-catalog
+ make compile-catalog
[testenv:py27-flake8]
deps =
- flake8
+ flake8
commands = flake8 django_extensions tests
-[testenv:py34-flake8]
-deps =
- flake8
-commands = flake8 django_extensions tests
-
-[testenv:py35-flake8]
-deps =
- flake8
-commands = flake8 django_extensions tests
-
-[testenv:py36-flake8]
+[testenv:py37-flake8]
deps =
- flake8
+ flake8
commands = flake8 django_extensions tests
-[testenv:py37-flake8]
+[testenv:py38-flake8]
deps =
- flake8
+ flake8
commands = flake8 django_extensions tests