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">&quot;Love is colder than death&quot;</span><span class="p">)</span>
+    <span class="nb">print</span><span class="p">(</span><span 
class="s2">&quot;Love is colder than death&quot;</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


Reply via email to