From 48410860524efcd173c0b58c0f32f758ed8fff30 Mon Sep 17 00:00:00 2001 From: Wes Okes Date: Mon, 1 Aug 2022 09:36:57 -0400 Subject: [PATCH 01/27] version upgrades --- .travis.yml | 24 +++++++++++++++++++----- docs/release_notes.rst | 7 +++++++ querybuilder/version.py | 2 +- requirements/requirements.txt | 2 +- settings.py | 20 +++++++++++++------- setup.py | 7 ++++--- tox.ini | 23 +++++++++++++++-------- 7 files changed, 60 insertions(+), 25 deletions(-) diff --git a/.travis.yml b/.travis.yml index 65b295f..a886afd 100644 --- a/.travis.yml +++ b/.travis.yml @@ -2,33 +2,47 @@ dist: xenial language: python sudo: false +services: + - postgresql + python: - - "3.6" - "3.7" - "3.8" + - "3.9" env: + global: + - PGPORT=5433 + - PGUSER=travis matrix: - - DJANGO=2.2 - DJANGO=3.0 - DJANGO=3.1 + - DJANGO=3.2 + - DJANGO=4.0 + - DJANGO=4.1 - DJANGO=master addons: - postgresql: '9.6' + postgresql: '13' + apt: + packages: + - postgresql-13 + - postgresql-client-13 matrix: include: - - { python: "3.6", env: TOXENV=flake8 } + - { python: "3.7", env: TOXENV=flake8 } allow_failures: - env: DJANGO=master install: - pip install tox-travis + # needed to fix setup.py error + - pip install importlib-metadata==4.12.0 before_script: - - psql -c 'CREATE DATABASE querybuilder;' -U postgres + - psql -c 'CREATE DATABASE querybuilder;' -U travis script: - tox diff --git a/docs/release_notes.rst b/docs/release_notes.rst index d9383b6..7d88b3f 100644 --- a/docs/release_notes.rst +++ b/docs/release_notes.rst @@ -1,6 +1,13 @@ Release Notes ============= +v3.0.0 +------ +* Add support for django 3.2, 4.0, 4.1 +* Add support for python 3.9 +* Remove support for django 2.2 +* Remove support for python 3.6 + v2.0.0 ------ * Add support Django 3.0, 3.1 diff --git a/querybuilder/version.py b/querybuilder/version.py index afced14..4eb28e3 100644 --- a/querybuilder/version.py +++ b/querybuilder/version.py @@ -1 +1 @@ -__version__ = '2.0.0' +__version__ = '3.0.0' diff --git a/requirements/requirements.txt b/requirements/requirements.txt index a9cf3ca..4de01dc 100644 --- a/requirements/requirements.txt +++ b/requirements/requirements.txt @@ -1,4 +1,4 @@ -Django>=2.2 +Django>=3.0 pytz>=2015.6 fleming>=0.6.0 six diff --git a/settings.py b/settings.py index f92d61d..71bc00c 100644 --- a/settings.py +++ b/settings.py @@ -12,17 +12,23 @@ def configure_settings(): test_db = os.environ.get('DB', None) if test_db is None: db_config = { - 'ENGINE': 'django.db.backends.postgresql_psycopg2', - 'NAME': 'ambition', - 'USER': 'ambition', - 'PASSWORD': 'ambition', - 'HOST': 'db' + 'ENGINE': 'django.db.backends.postgresql', + 'NAME': 'ambition_test', + 'USER': 'postgres', + 'PASSWORD': '', + 'HOST': 'db', } elif test_db == 'postgres': db_config = { - 'ENGINE': 'django.db.backends.postgresql_psycopg2', - 'USER': 'postgres', + 'ENGINE': 'django.db.backends.postgresql', 'NAME': 'querybuilder', + 'USER': 'travis', + 'PORT': '5433', + } + elif test_db == 'sqlite': + db_config = { + 'ENGINE': 'django.db.backends.sqlite3', + 'NAME': 'regex_field', } else: raise RuntimeError('Unsupported test DB {0}'.format(test_db)) diff --git a/setup.py b/setup.py index 5c26b9d..e88063c 100644 --- a/setup.py +++ b/setup.py @@ -37,17 +37,18 @@ def get_lines(file_path): packages=find_packages(), classifiers=[ 'Programming Language :: Python', - 'Programming Language :: Python :: 3.6', 'Programming Language :: Python :: 3.7', 'Programming Language :: Python :: 3.8', + 'Programming Language :: Python :: 3.9', 'Intended Audience :: Developers', 'License :: OSI Approved :: MIT License', 'Operating System :: OS Independent', 'Framework :: Django', - 'Framework :: Django :: 2.2', 'Framework :: Django :: 3.0', 'Framework :: Django :: 3.1', - 'Development Status :: 5 - Production/Stable', + 'Framework :: Django :: 3.2', + 'Framework :: Django :: 4.0', + 'Framework :: Django :: 4.1', ], license='MIT', install_requires=install_requires, diff --git a/tox.ini b/tox.ini index 0b15b2f..d699472 100644 --- a/tox.ini +++ b/tox.ini @@ -1,18 +1,23 @@ [tox] envlist = flake8 - py{36,37}-django22 - py{36,37,38}-django30 - py{36,37,38}-django31 - py{36,37,38}-djangomaster + coverage + py{37,38}-django30 + py{37,38}-django31 + py{37,38}-django32 + py{38,39}-django40 + py{38,39}-django41 + py{38,39}-djangomaster [testenv] setenv = DB = postgres deps = - django22: Django>=2.2,<3.0 - django30: Django>=3.0,<3.1 - django31: Django>=3.1,<3.2 + django30: Django~=3.0 + django31: Django~=3.1 + django32: Django~=3.2 + django40: Django~=4.0 + django41: Django==4.1rc1 djangomaster: https://github.com/django/django/archive/master.tar.gz -rrequirements/requirements-testing.txt commands = @@ -25,7 +30,9 @@ commands = flake8 querybuilder [travis:env] DJANGO = - 2.2: django22 3.0: django30 3.1: django31 + 3.2: django32 + 4.0: django40 + 4.1: django41 master: djangomaster From f27d0ff2b5b4c5f58333d5d00499b35a3a75ac23 Mon Sep 17 00:00:00 2001 From: Wes Okes Date: Mon, 1 Aug 2022 11:10:49 -0400 Subject: [PATCH 02/27] version difference --- querybuilder/tests/json_tests.py | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/querybuilder/tests/json_tests.py b/querybuilder/tests/json_tests.py index 670532a..8f18df7 100644 --- a/querybuilder/tests/json_tests.py +++ b/querybuilder/tests/json_tests.py @@ -47,11 +47,7 @@ def test_one(self): ) ) - # Django 3.1 changes the raw queryset behavior so querybuilder isn't going to change that behavior - if VERSION[0] == 3 and VERSION[1] == 1: - self.assertEqual(query.select(), [{'my_two_alias': '"two"'}]) - else: - self.assertEqual(query.select(), [{'my_two_alias': 'two'}]) + self.assertEqual(query.select(), [{'my_two_alias': '"two"'}]) query = Query().from_table(MetricRecord, fields=[one_field]).where(**{ one_field.get_where_key(): '1' @@ -64,11 +60,7 @@ def test_one(self): ) ) - # Django 3.1 changes the raw queryset behavior so querybuilder isn't going to change that behavior - if VERSION[0] == 3 and VERSION[1] == 1: - self.assertEqual(query.select(), [{'my_one_alias': '1'}]) - else: - self.assertEqual(query.select(), [{'my_one_alias': 1}]) + self.assertEqual(query.select(), [{'my_one_alias': '1'}]) query = Query().from_table(MetricRecord, fields=[one_field]).where(**{ one_field.get_where_key(): '2' From df8db6bebcee025fd007f9e2c251d85c5e75aecc Mon Sep 17 00:00:00 2001 From: Wes Okes Date: Fri, 19 Aug 2022 16:25:12 -0400 Subject: [PATCH 03/27] testing only 3.8 --- .travis.yml | 13 +++++----- querybuilder/helpers.py | 12 +++++++++ querybuilder/query.py | 4 +-- querybuilder/tests/json_tests.py | 12 +++++++-- setup.py | 1 + tox.ini | 44 +++++++++++++++++--------------- 6 files changed, 56 insertions(+), 30 deletions(-) diff --git a/.travis.yml b/.travis.yml index a886afd..92ab42c 100644 --- a/.travis.yml +++ b/.travis.yml @@ -15,12 +15,13 @@ env: - PGPORT=5433 - PGUSER=travis matrix: - - DJANGO=3.0 - - DJANGO=3.1 - - DJANGO=3.2 - - DJANGO=4.0 - - DJANGO=4.1 - - DJANGO=master + - DJANGO=2.2 +# - DJANGO=3.0 +# - DJANGO=3.1 +# - DJANGO=3.2 +# - DJANGO=4.0 +# - DJANGO=4.1 +# - DJANGO=master addons: postgresql: '13' diff --git a/querybuilder/helpers.py b/querybuilder/helpers.py index b1765e4..d6b0239 100644 --- a/querybuilder/helpers.py +++ b/querybuilder/helpers.py @@ -51,3 +51,15 @@ def set_value_for_keypath(item, keypath, value, create_if_needed=False, delimete return item else: return None + + +class Empty: + pass + + +def copy_instance(instance): + obj = Empty() + obj.__class__ = instance.__class__ + # Copy references to everything. + obj.__dict__ = instance.__dict__.copy() + return obj diff --git a/querybuilder/query.py b/querybuilder/query.py index e521c0b..879993a 100644 --- a/querybuilder/query.py +++ b/querybuilder/query.py @@ -10,7 +10,7 @@ from querybuilder.fields import FieldFactory, CountField, MaxField, MinField, SumField, AvgField -from querybuilder.helpers import set_value_for_keypath +from querybuilder.helpers import set_value_for_keypath, copy_instance from querybuilder.tables import TableFactory, ModelTable, QueryTable @@ -1534,7 +1534,7 @@ def wrap(self, alias=None): :return: The wrapped query """ field_names = self.get_field_names() - query = Query(self.connection).from_table(deepcopy(self), alias=alias) + query = Query(self.connection).from_table(copy_instance(self), alias=alias) self.__dict__.update(query.__dict__) # set explicit field names diff --git a/querybuilder/tests/json_tests.py b/querybuilder/tests/json_tests.py index 8f18df7..6aded36 100644 --- a/querybuilder/tests/json_tests.py +++ b/querybuilder/tests/json_tests.py @@ -47,7 +47,11 @@ def test_one(self): ) ) - self.assertEqual(query.select(), [{'my_two_alias': '"two"'}]) + # Django 3.0 changes the raw queryset behavior so querybuilder isn't going to change that behavior + if VERSION[0] >= 3: + self.assertEqual(query.select(), [{'my_two_alias': '"two"'}]) + else: + self.assertEqual(query.select(), [{'my_two_alias': 'two'}]) query = Query().from_table(MetricRecord, fields=[one_field]).where(**{ one_field.get_where_key(): '1' @@ -60,7 +64,11 @@ def test_one(self): ) ) - self.assertEqual(query.select(), [{'my_one_alias': '1'}]) + # Django 3.0 changes the raw queryset behavior so querybuilder isn't going to change that behavior + if VERSION[0] >= 3: + self.assertEqual(query.select(), [{'my_one_alias': '1'}]) + else: + self.assertEqual(query.select(), [{'my_one_alias': 1}]) query = Query().from_table(MetricRecord, fields=[one_field]).where(**{ one_field.get_where_key(): '2' diff --git a/setup.py b/setup.py index e88063c..26742e3 100644 --- a/setup.py +++ b/setup.py @@ -44,6 +44,7 @@ def get_lines(file_path): 'License :: OSI Approved :: MIT License', 'Operating System :: OS Independent', 'Framework :: Django', + 'Framework :: Django :: 2.2', 'Framework :: Django :: 3.0', 'Framework :: Django :: 3.1', 'Framework :: Django :: 3.2', diff --git a/tox.ini b/tox.ini index d699472..11ddea5 100644 --- a/tox.ini +++ b/tox.ini @@ -1,24 +1,27 @@ [tox] envlist = - flake8 - coverage - py{37,38}-django30 - py{37,38}-django31 - py{37,38}-django32 - py{38,39}-django40 - py{38,39}-django41 - py{38,39}-djangomaster +; flake8 +; coverage + py{38}-django22 +; py{37,38}-django22 +; py{37,38}-django30 +; py{37,38}-django31 +; py{37,38}-django32 +; py{38,39}-django40 +; py{38,39}-django41 +; py{38,39}-djangomaster [testenv] setenv = DB = postgres deps = - django30: Django~=3.0 - django31: Django~=3.1 - django32: Django~=3.2 - django40: Django~=4.0 - django41: Django==4.1rc1 - djangomaster: https://github.com/django/django/archive/master.tar.gz + django22: Django~=2.2 +; django30: Django~=3.0 +; django31: Django~=3.1 +; django32: Django~=3.2 +; django40: Django~=4.0 +; django41: Django==4.1rc1 +; djangomaster: https://github.com/django/django/archive/master.tar.gz -rrequirements/requirements-testing.txt commands = coverage run setup.py test @@ -30,9 +33,10 @@ commands = flake8 querybuilder [travis:env] DJANGO = - 3.0: django30 - 3.1: django31 - 3.2: django32 - 4.0: django40 - 4.1: django41 - master: djangomaster + 2.2: django22 +; 3.0: django30 +; 3.1: django31 +; 3.2: django32 +; 4.0: django40 +; 4.1: django41 +; master: djangomaster From 27e2fed1cb2949b8010abda7c3e09c725c537905 Mon Sep 17 00:00:00 2001 From: Wes Okes Date: Fri, 19 Aug 2022 16:26:30 -0400 Subject: [PATCH 04/27] django 3 --- .travis.yml | 2 +- tox.ini | 13 ++++++------- 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/.travis.yml b/.travis.yml index 92ab42c..6bd1c8a 100644 --- a/.travis.yml +++ b/.travis.yml @@ -16,7 +16,7 @@ env: - PGUSER=travis matrix: - DJANGO=2.2 -# - DJANGO=3.0 + - DJANGO=3.0 # - DJANGO=3.1 # - DJANGO=3.2 # - DJANGO=4.0 diff --git a/tox.ini b/tox.ini index 11ddea5..2cc2c19 100644 --- a/tox.ini +++ b/tox.ini @@ -1,10 +1,9 @@ [tox] envlist = -; flake8 -; coverage - py{38}-django22 -; py{37,38}-django22 -; py{37,38}-django30 + flake8 + coverage + py{37,38}-django22 + py{37,38}-django30 ; py{37,38}-django31 ; py{37,38}-django32 ; py{38,39}-django40 @@ -16,7 +15,7 @@ setenv = DB = postgres deps = django22: Django~=2.2 -; django30: Django~=3.0 + django30: Django~=3.0 ; django31: Django~=3.1 ; django32: Django~=3.2 ; django40: Django~=4.0 @@ -34,7 +33,7 @@ commands = flake8 querybuilder [travis:env] DJANGO = 2.2: django22 -; 3.0: django30 + 3.0: django30 ; 3.1: django31 ; 3.2: django32 ; 4.0: django40 From 37a2cb9cda82e3a65a478ea1c3a5676e74cc00e7 Mon Sep 17 00:00:00 2001 From: Wes Okes Date: Fri, 19 Aug 2022 16:34:48 -0400 Subject: [PATCH 05/27] trying to fix second db --- settings.py | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/settings.py b/settings.py index 71bc00c..280c8d2 100644 --- a/settings.py +++ b/settings.py @@ -19,6 +19,13 @@ def configure_settings(): 'HOST': 'db', } elif test_db == 'postgres': + # db_config = { + # 'ENGINE': 'django.db.backends.postgresql', + # 'NAME': 'querybuilder', + # 'USER': 'postgres', + # 'PASSWORD': '', + # 'HOST': 'db', + # } db_config = { 'ENGINE': 'django.db.backends.postgresql', 'NAME': 'querybuilder', @@ -33,16 +40,17 @@ def configure_settings(): else: raise RuntimeError('Unsupported test DB {0}'.format(test_db)) + db_config2 = db_config.copy() + db_config2.name = f'{db_config2.name}2' + db_config2.TEST_MIRROR = 'default' + settings.configure( TEST_RUNNER='django_nose.NoseTestSuiteRunner', NOSE_ARGS=['--nocapture', '--nologcapture', '--verbosity=1'], MIDDLEWARE_CLASSES=(), DATABASES={ 'default': db_config, - 'mock-second-database': { - 'ENGINE': 'django.db.backends.sqlite3', - 'TEST_MIRROR': 'default', - }, + 'mock-second-database': db_config2, }, INSTALLED_APPS=( 'django.contrib.auth', From 97ec41511ab14ecccf3fad770120d608983f752f Mon Sep 17 00:00:00 2001 From: Wes Okes Date: Fri, 19 Aug 2022 16:39:05 -0400 Subject: [PATCH 06/27] fix dict access --- settings.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/settings.py b/settings.py index 280c8d2..9cce709 100644 --- a/settings.py +++ b/settings.py @@ -41,8 +41,8 @@ def configure_settings(): raise RuntimeError('Unsupported test DB {0}'.format(test_db)) db_config2 = db_config.copy() - db_config2.name = f'{db_config2.name}2' - db_config2.TEST_MIRROR = 'default' + db_config2['name'] = f'{db_config2["name"]}2' + db_config2['TEST_MIRROR'] = 'default' settings.configure( TEST_RUNNER='django_nose.NoseTestSuiteRunner', From 0dc08ebbfe35cde7ee74764bff266ec9f76a3fb5 Mon Sep 17 00:00:00 2001 From: Wes Okes Date: Fri, 19 Aug 2022 16:46:47 -0400 Subject: [PATCH 07/27] fix dict copy --- settings.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/settings.py b/settings.py index 9cce709..3e251ff 100644 --- a/settings.py +++ b/settings.py @@ -1,3 +1,4 @@ +import copy import os from django.conf import settings @@ -40,7 +41,7 @@ def configure_settings(): else: raise RuntimeError('Unsupported test DB {0}'.format(test_db)) - db_config2 = db_config.copy() + db_config2 = copy.copy(db_config) db_config2['name'] = f'{db_config2["name"]}2' db_config2['TEST_MIRROR'] = 'default' From d443ac05abbf772d6bf1d5402ba0b57f265fc94f Mon Sep 17 00:00:00 2001 From: Wes Okes Date: Fri, 19 Aug 2022 16:50:29 -0400 Subject: [PATCH 08/27] actually use the correct key --- settings.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/settings.py b/settings.py index 3e251ff..6524411 100644 --- a/settings.py +++ b/settings.py @@ -42,7 +42,7 @@ def configure_settings(): raise RuntimeError('Unsupported test DB {0}'.format(test_db)) db_config2 = copy.copy(db_config) - db_config2['name'] = f'{db_config2["name"]}2' + db_config2['NAME'] = f'{db_config2["NAME"]}2' db_config2['TEST_MIRROR'] = 'default' settings.configure( From 117cf4e93197052a057616996adfde1d953ef11e Mon Sep 17 00:00:00 2001 From: Wes Okes Date: Fri, 19 Aug 2022 17:11:28 -0400 Subject: [PATCH 09/27] rebuild for tox? --- .travis.yml | 8 ++++---- requirements/requirements.txt | 2 +- tox.ini | 14 ++++++++------ 3 files changed, 13 insertions(+), 11 deletions(-) diff --git a/.travis.yml b/.travis.yml index 6bd1c8a..07e1004 100644 --- a/.travis.yml +++ b/.travis.yml @@ -6,9 +6,9 @@ services: - postgresql python: - - "3.7" +# - "3.7" - "3.8" - - "3.9" +# - "3.9" env: global: @@ -16,7 +16,7 @@ env: - PGUSER=travis matrix: - DJANGO=2.2 - - DJANGO=3.0 +# - DJANGO=3.0 # - DJANGO=3.1 # - DJANGO=3.2 # - DJANGO=4.0 @@ -46,7 +46,7 @@ before_script: - psql -c 'CREATE DATABASE querybuilder;' -U travis script: - - tox + - tox -r after_success: coveralls diff --git a/requirements/requirements.txt b/requirements/requirements.txt index 4de01dc..a9cf3ca 100644 --- a/requirements/requirements.txt +++ b/requirements/requirements.txt @@ -1,4 +1,4 @@ -Django>=3.0 +Django>=2.2 pytz>=2015.6 fleming>=0.6.0 six diff --git a/tox.ini b/tox.ini index 2cc2c19..a86748a 100644 --- a/tox.ini +++ b/tox.ini @@ -1,9 +1,10 @@ [tox] envlist = - flake8 - coverage - py{37,38}-django22 - py{37,38}-django30 +; flake8 +; coverage + py{38}-django22 +; py{37,38}-django22 +; py{37,38}-django30 ; py{37,38}-django31 ; py{37,38}-django32 ; py{38,39}-django40 @@ -15,7 +16,7 @@ setenv = DB = postgres deps = django22: Django~=2.2 - django30: Django~=3.0 +; django30: Django~=3.0 ; django31: Django~=3.1 ; django32: Django~=3.2 ; django40: Django~=4.0 @@ -23,6 +24,7 @@ deps = ; djangomaster: https://github.com/django/django/archive/master.tar.gz -rrequirements/requirements-testing.txt commands = + pip freeze coverage run setup.py test coverage report --fail-under=90 @@ -33,7 +35,7 @@ commands = flake8 querybuilder [travis:env] DJANGO = 2.2: django22 - 3.0: django30 +; 3.0: django30 ; 3.1: django31 ; 3.2: django32 ; 4.0: django40 From 6a80c669f8e5ff12adcbec43852f5080e828bcc3 Mon Sep 17 00:00:00 2001 From: Wes Okes Date: Fri, 19 Aug 2022 17:14:26 -0400 Subject: [PATCH 10/27] restore versions and rebuild --- .travis.yml | 16 ++++++++-------- tox.ini | 43 +++++++++++++++++++++---------------------- 2 files changed, 29 insertions(+), 30 deletions(-) diff --git a/.travis.yml b/.travis.yml index 07e1004..bd1a188 100644 --- a/.travis.yml +++ b/.travis.yml @@ -6,9 +6,9 @@ services: - postgresql python: -# - "3.7" + - "3.7" - "3.8" -# - "3.9" + - "3.9" env: global: @@ -16,12 +16,12 @@ env: - PGUSER=travis matrix: - DJANGO=2.2 -# - DJANGO=3.0 -# - DJANGO=3.1 -# - DJANGO=3.2 -# - DJANGO=4.0 -# - DJANGO=4.1 -# - DJANGO=master + - DJANGO=3.0 + - DJANGO=3.1 + - DJANGO=3.2 + - DJANGO=4.0 + - DJANGO=4.1 + - DJANGO=master addons: postgresql: '13' diff --git a/tox.ini b/tox.ini index a86748a..4f15b9b 100644 --- a/tox.ini +++ b/tox.ini @@ -1,27 +1,26 @@ [tox] envlist = -; flake8 -; coverage - py{38}-django22 -; py{37,38}-django22 -; py{37,38}-django30 -; py{37,38}-django31 -; py{37,38}-django32 -; py{38,39}-django40 -; py{38,39}-django41 -; py{38,39}-djangomaster + flake8 + coverage + py{37,38}-django22 + py{37,38}-django30 + py{37,38}-django31 + py{37,38}-django32 + py{38,39}-django40 + py{38,39}-django41 + py{38,39}-djangomaster [testenv] setenv = DB = postgres deps = django22: Django~=2.2 -; django30: Django~=3.0 -; django31: Django~=3.1 -; django32: Django~=3.2 -; django40: Django~=4.0 -; django41: Django==4.1rc1 -; djangomaster: https://github.com/django/django/archive/master.tar.gz + django30: Django~=3.0 + django31: Django~=3.1 + django32: Django~=3.2 + django40: Django~=4.0 + django41: Django~=4.1 + djangomaster: https://github.com/django/django/archive/master.tar.gz -rrequirements/requirements-testing.txt commands = pip freeze @@ -35,9 +34,9 @@ commands = flake8 querybuilder [travis:env] DJANGO = 2.2: django22 -; 3.0: django30 -; 3.1: django31 -; 3.2: django32 -; 4.0: django40 -; 4.1: django41 -; master: djangomaster + 3.0: django30 + 3.1: django31 + 3.2: django32 + 4.0: django40 + 4.1: django41 + master: djangomaster From 1e98f67ba38d23ff734ddfe0a9ee03050fb7bb22 Mon Sep 17 00:00:00 2001 From: Wes Okes Date: Fri, 19 Aug 2022 17:22:32 -0400 Subject: [PATCH 11/27] trying to get proper django versions --- tox.ini | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/tox.ini b/tox.ini index 4f15b9b..6d28977 100644 --- a/tox.ini +++ b/tox.ini @@ -14,12 +14,12 @@ envlist = setenv = DB = postgres deps = - django22: Django~=2.2 - django30: Django~=3.0 - django31: Django~=3.1 - django32: Django~=3.2 - django40: Django~=4.0 - django41: Django~=4.1 + django22: Django>=2.2<3.0 + django30: Django>=3.0<3.1 + django31: Django>=3.1<3.2 + django32: Django>=3.2<4.0 + django40: Django>=4.0<4.1 + django41: Django>=4.1<4.2 djangomaster: https://github.com/django/django/archive/master.tar.gz -rrequirements/requirements-testing.txt commands = From 7ea9f1a63158b58b465ea8cf92a49b306e88b53d Mon Sep 17 00:00:00 2001 From: Wes Okes Date: Fri, 19 Aug 2022 18:28:00 -0400 Subject: [PATCH 12/27] fixing tests --- .travis.yml | 4 +--- querybuilder/tests/base.py | 5 +++++ querybuilder/tests/helper_tests.py | 5 +++-- querybuilder/tests/json_tests.py | 6 +++--- querybuilder/tests/logger_tests.py | 6 +++--- querybuilder/tests/order_tests.py | 1 + querybuilder/tests/query_tests.py | 8 ++++---- querybuilder/tests/table_tests.py | 8 ++++---- querybuilder/tests/update_tests.py | 14 ++++++++++---- requirements/requirements-testing.txt | 1 - tox.ini | 17 +++++++++-------- 11 files changed, 43 insertions(+), 32 deletions(-) create mode 100644 querybuilder/tests/base.py diff --git a/.travis.yml b/.travis.yml index bd1a188..67a9436 100644 --- a/.travis.yml +++ b/.travis.yml @@ -39,14 +39,12 @@ matrix: install: - pip install tox-travis - # needed to fix setup.py error - - pip install importlib-metadata==4.12.0 before_script: - psql -c 'CREATE DATABASE querybuilder;' -U travis script: - - tox -r + - tox after_success: coveralls diff --git a/querybuilder/tests/base.py b/querybuilder/tests/base.py new file mode 100644 index 0000000..c867e75 --- /dev/null +++ b/querybuilder/tests/base.py @@ -0,0 +1,5 @@ +from django.test import TestCase + + +class QuerybuilderTestCase(TestCase): + databases = ['default', 'mock-second-database'] diff --git a/querybuilder/tests/helper_tests.py b/querybuilder/tests/helper_tests.py index 9ef8e24..8f6e507 100644 --- a/querybuilder/tests/helper_tests.py +++ b/querybuilder/tests/helper_tests.py @@ -1,11 +1,12 @@ -from django.test import TestCase from querybuilder.helpers import value_for_keypath, set_value_for_keypath +from querybuilder.tests.base import QuerybuilderTestCase -class HelperTest(TestCase): +class HelperTest(QuerybuilderTestCase): """ Tests the helper functions """ + def test_value_for_keypath(self): """ Tests all cases of value_for_keypath diff --git a/querybuilder/tests/json_tests.py b/querybuilder/tests/json_tests.py index 6aded36..bee8ff1 100644 --- a/querybuilder/tests/json_tests.py +++ b/querybuilder/tests/json_tests.py @@ -1,15 +1,15 @@ import unittest from django import VERSION -from django.test.testcases import TestCase from django.test.utils import override_settings from querybuilder.fields import JsonField from querybuilder.query import Query, JsonQueryset +from querybuilder.tests.base import QuerybuilderTestCase from querybuilder.tests.models import MetricRecord from querybuilder.tests.utils import get_postgres_version @override_settings(DEBUG=True) -class JsonFieldTest(TestCase): +class JsonFieldTest(QuerybuilderTestCase): def test_one(self): if get_postgres_version() < (9, 4): @@ -84,7 +84,7 @@ def test_one(self): @override_settings(DEBUG=True) -class JsonQuerysetTest(TestCase): +class JsonQuerysetTest(QuerybuilderTestCase): def test_one(self): if get_postgres_version() < (9, 4): diff --git a/querybuilder/tests/logger_tests.py b/querybuilder/tests/logger_tests.py index 87769f7..75a3daf 100644 --- a/querybuilder/tests/logger_tests.py +++ b/querybuilder/tests/logger_tests.py @@ -1,14 +1,14 @@ from django.db import connection -from django.test import TestCase from django.test.utils import override_settings from querybuilder.logger import Logger, LogManager from querybuilder.query import Query +from querybuilder.tests.base import QuerybuilderTestCase from querybuilder.tests.models import Account @override_settings(DEBUG=True) -class LogManagerTest(TestCase): +class LogManagerTest(QuerybuilderTestCase): """ Includes functions to test the LogManager """ @@ -35,7 +35,7 @@ def test_log_manager(self): @override_settings(DEBUG=True) -class LoggerTest(TestCase): +class LoggerTest(QuerybuilderTestCase): """ Includes functions to test the Logger """ diff --git a/querybuilder/tests/order_tests.py b/querybuilder/tests/order_tests.py index 97840d0..a0b02a8 100644 --- a/querybuilder/tests/order_tests.py +++ b/querybuilder/tests/order_tests.py @@ -3,6 +3,7 @@ class OrderByTest(QueryTestCase): + def test_order_by_single_asc(self): query = Query().from_table( table='test_table' diff --git a/querybuilder/tests/query_tests.py b/querybuilder/tests/query_tests.py index 39f90b2..14a3c48 100644 --- a/querybuilder/tests/query_tests.py +++ b/querybuilder/tests/query_tests.py @@ -1,12 +1,12 @@ import datetime from django.db import connections -from django.test import TestCase from django_dynamic_fixture import G import six from querybuilder.fields import CountField from querybuilder.query import Query +from querybuilder.tests.base import QuerybuilderTestCase from querybuilder.tests.models import User, Account, Order @@ -14,8 +14,7 @@ def get_comparison_str(item1, item2): return 'Items are not equal.\nGot:\n{0}\nExpected:\n{1}'.format(item1, item2) -class QueryConstructorTests(TestCase): - databases = ['default', 'mock-second-database'] +class QueryConstructorTests(QuerybuilderTestCase): def test_init_with_connection(self): """ @@ -47,7 +46,7 @@ def test_get_cursor_for_connection(self): self.assertEqual(query3.get_cursor().db, connections['default']) -class QueryTestCase(TestCase): +class QueryTestCase(QuerybuilderTestCase): def setUp(self): super(QueryTestCase, self).setUp() @@ -221,6 +220,7 @@ def test_find_field_alias(self): class FieldTest(QueryTestCase): + def test_cast(self): query = Query().from_table( table=Account, diff --git a/querybuilder/tests/table_tests.py b/querybuilder/tests/table_tests.py index 33dbd10..1114663 100644 --- a/querybuilder/tests/table_tests.py +++ b/querybuilder/tests/table_tests.py @@ -1,11 +1,11 @@ -from django.test import TestCase from querybuilder.fields import Field from querybuilder.query import Query from querybuilder.tables import TableFactory, SimpleTable, ModelTable, QueryTable +from querybuilder.tests.base import QuerybuilderTestCase from querybuilder.tests.models import Account -class TableFactoryTest(TestCase): +class TableFactoryTest(QuerybuilderTestCase): """ Tests the functionality of the table factory """ @@ -68,7 +68,7 @@ def test_dict(self): self.assertEqual('test_alias', table.alias) -class TableTest(TestCase): +class TableTest(QuerybuilderTestCase): """ Tests functionality of the Table class """ @@ -178,7 +178,7 @@ def test_find_field_none(self): self.assertIsNone(field) -class QueryTableTest(TestCase): +class QueryTableTest(QuerybuilderTestCase): """ Tests the QueryTable object """ diff --git a/querybuilder/tests/update_tests.py b/querybuilder/tests/update_tests.py index 57da80b..21ad1b2 100644 --- a/querybuilder/tests/update_tests.py +++ b/querybuilder/tests/update_tests.py @@ -1,5 +1,6 @@ import json +from django import VERSION from django.test.utils import override_settings from django_dynamic_fixture import G @@ -16,6 +17,11 @@ def setUp(self): self.logger = Logger() self.logger.start_logging() + # Starting on Django 4, the id field adds ::integer automatically + self.integer_cast_string = '' + if VERSION[0] >= 4: + self.integer_cast_string = '::integer' + def test_update_single_row(self): query = Query().from_table( table=Account, @@ -40,7 +46,7 @@ def test_update_single_row(self): 'SET user_id = new_values.user_id, ' 'first_name = new_values.first_name, ' 'last_name = new_values.last_name ' - 'FROM (VALUES (%s, %s::integer, %s::varchar(64), %s::varchar(64))) ' + f'FROM (VALUES (%s{self.integer_cast_string}, %s::integer, %s::varchar(64), %s::varchar(64))) ' 'AS new_values (id, user_id, first_name, last_name) ' 'WHERE querybuilder_tests_account.id = new_values.id' ) @@ -60,7 +66,7 @@ def test_update_single_row(self): "SET user_id = new_values.user_id, " "first_name = new_values.first_name, " "last_name = new_values.last_name " - "FROM (VALUES (1, 1::integer, 'Test''s'::varchar(64), '\"User\"'::varchar(64))) " + f"FROM (VALUES (1{self.integer_cast_string}, 1::integer, 'Test''s'::varchar(64), '\"User\"'::varchar(64))) " "AS new_values (id, user_id, first_name, last_name) " "WHERE querybuilder_tests_account.id = new_values.id" ) @@ -115,7 +121,7 @@ def test_update_multiple_rows(self): 'SET user_id = new_values.user_id, ' 'first_name = new_values.first_name, ' 'last_name = new_values.last_name ' - 'FROM (VALUES (%s, %s::integer, %s::varchar(64), %s::varchar(64)), (%s, %s, %s, %s)) ' + f'FROM (VALUES (%s{self.integer_cast_string}, %s::integer, %s::varchar(64), %s::varchar(64)), (%s, %s, %s, %s)) ' 'AS new_values (id, user_id, first_name, last_name) ' 'WHERE querybuilder_tests_account.id = new_values.id' ) @@ -138,7 +144,7 @@ def test_update_multiple_rows(self): "SET user_id = new_values.user_id, " "first_name = new_values.first_name, " "last_name = new_values.last_name " - "FROM (VALUES (1, 1::integer, 'Test'::varchar(64), 'User'::varchar(64)), " + f"FROM (VALUES (1{self.integer_cast_string}, 1::integer, 'Test'::varchar(64), 'User'::varchar(64)), " "(2, 2, 'Test2', 'User2')) " "AS new_values (id, user_id, first_name, last_name) " "WHERE querybuilder_tests_account.id = new_values.id" diff --git a/requirements/requirements-testing.txt b/requirements/requirements-testing.txt index 6a11970..91ef931 100644 --- a/requirements/requirements-testing.txt +++ b/requirements/requirements-testing.txt @@ -7,4 +7,3 @@ django-dynamic-fixture jsonfield==0.9.20 mock coverage -coveralls diff --git a/tox.ini b/tox.ini index 6d28977..0c375f8 100644 --- a/tox.ini +++ b/tox.ini @@ -1,7 +1,6 @@ [tox] envlist = flake8 - coverage py{37,38}-django22 py{37,38}-django30 py{37,38}-django31 @@ -14,17 +13,19 @@ envlist = setenv = DB = postgres deps = - django22: Django>=2.2<3.0 - django30: Django>=3.0<3.1 - django31: Django>=3.1<3.2 - django32: Django>=3.2<4.0 - django40: Django>=4.0<4.1 - django41: Django>=4.1<4.2 + django22: Django~=2.2 + django30: Django~=3.0 + django31: Django~=3.1 + django32: Django~=3.2 + django40: Django~=4.0 + django41: Django~=4.1 djangomaster: https://github.com/django/django/archive/master.tar.gz -rrequirements/requirements-testing.txt + commands = pip freeze - coverage run setup.py test + python --version + coverage run manage.py test querybuilder coverage report --fail-under=90 [testenv:flake8] From f70a67564547dde79308d1f61a6cb51d15c1a2af Mon Sep 17 00:00:00 2001 From: Wes Okes Date: Mon, 22 Aug 2022 14:35:31 -0400 Subject: [PATCH 13/27] github actions --- .github/workflows/tests.yml | 94 +++++++++++++++++++++++++++ .travis.yml => .travis_old.yml | 0 docs/release_notes.rst | 6 +- querybuilder/version.py | 2 +- requirements/requirements-testing.txt | 1 + tox.ini => tox_old.ini | 0 6 files changed, 100 insertions(+), 3 deletions(-) create mode 100644 .github/workflows/tests.yml rename .travis.yml => .travis_old.yml (100%) rename tox.ini => tox_old.ini (100%) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml new file mode 100644 index 0000000..27496ea --- /dev/null +++ b/.github/workflows/tests.yml @@ -0,0 +1,94 @@ +# copied from django-cte +name: querybuilder tests +on: + push: + branches: [master] + pull_request: + branches: [master,develop] + +jobs: + tests: + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: +# python: ['3.7', '3.8', '3.9'] + python: ['3.7'] + # Time to switch to pytest or nose2? + # nosetests is broken on 3.10 + # AttributeError: module 'collections' has no attribute 'Callable' + # https://github.com/nose-devs/nose/issues/1099 + django: + - 'Django~=2.2.0' +# - 'Django~=3.0.0' +# - 'Django~=3.1.0' +# - 'Django~=3.2.0' +# - 'Django~=4.0.0' +# - 'Django~=4.1.0' + experimental: [false] +# include: +# - python: '3.9' +# django: 'https://github.com/django/django/archive/refs/heads/main.zip#egg=Django' +# experimental: true +# # NOTE this job will appear to pass even when it fails because of +# # `continue-on-error: true`. Github Actions apparently does not +# # have this feature, similar to Travis' allow-failure, yet. +# # https://github.com/actions/toolkit/issues/399 +# exclude: +# - python: '3.7' +# django: 'Django~=4.0.0' +# - python: '3.7' +# django: 'Django~=4.1.0' + services: + postgres: + image: postgres:latest + env: + POSTGRES_DB: postgres + POSTGRES_PASSWORD: postgres + POSTGRES_USER: postgres + ports: + - 5432:5432 + options: >- + --health-cmd pg_isready + --health-interval 10s + --health-timeout 5s + --health-retries 5 + steps: + - uses: actions/checkout@v2 + - uses: actions/setup-python@v2 + with: + python-version: ${{ matrix.python }} + - name: Setup + run: | + python --version + pip install --upgrade pip wheel + pip install -r requirements/requirements.txt + pip install -r requirements/requirements-testing.txt + pip install "${{ matrix.django }}" + pip freeze + - name: Run tests + env: + DB_SETTINGS: >- + { + "ENGINE":"django.db.backends.postgresql_psycopg2", + "NAME":"querybuilder", + "USER":"postgres", + "PASSWORD":"postgres", + "HOST":"localhost", + "PORT":"5432" + } + DB_SETTINGS2: >- + { + "ENGINE":"django.db.backends.postgresql_psycopg2", + "NAME":"querybuilder2", + "USER":"postgres", + "PASSWORD":"postgres", + "HOST":"localhost", + "PORT":"5432" + } + run: | + coverage run manage.py test querybuilder + coverage report --fail-under=90 + continue-on-error: ${{ matrix.experimental }} + - name: Check style + run: flake8 querybuilder diff --git a/.travis.yml b/.travis_old.yml similarity index 100% rename from .travis.yml rename to .travis_old.yml diff --git a/docs/release_notes.rst b/docs/release_notes.rst index 7d88b3f..1cdbacd 100644 --- a/docs/release_notes.rst +++ b/docs/release_notes.rst @@ -1,12 +1,14 @@ Release Notes ============= +v3.0.1 +------ +* Switch to github actions + v3.0.0 ------ * Add support for django 3.2, 4.0, 4.1 * Add support for python 3.9 -* Remove support for django 2.2 -* Remove support for python 3.6 v2.0.0 ------ diff --git a/querybuilder/version.py b/querybuilder/version.py index 4eb28e3..b7a5531 100644 --- a/querybuilder/version.py +++ b/querybuilder/version.py @@ -1 +1 @@ -__version__ = '3.0.0' +__version__ = '3.0.1' diff --git a/requirements/requirements-testing.txt b/requirements/requirements-testing.txt index 91ef931..6d9a66d 100644 --- a/requirements/requirements-testing.txt +++ b/requirements/requirements-testing.txt @@ -7,3 +7,4 @@ django-dynamic-fixture jsonfield==0.9.20 mock coverage +flake8 diff --git a/tox.ini b/tox_old.ini similarity index 100% rename from tox.ini rename to tox_old.ini From c3a8e97258c17cf9e03f66ab05a33b8599503d4c Mon Sep 17 00:00:00 2001 From: Wes Okes Date: Mon, 22 Aug 2022 14:35:38 -0400 Subject: [PATCH 14/27] settings --- settings.py | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/settings.py b/settings.py index 6524411..35c08c7 100644 --- a/settings.py +++ b/settings.py @@ -1,4 +1,5 @@ import copy +import json import os from django.conf import settings @@ -20,18 +21,12 @@ def configure_settings(): 'HOST': 'db', } elif test_db == 'postgres': - # db_config = { - # 'ENGINE': 'django.db.backends.postgresql', - # 'NAME': 'querybuilder', - # 'USER': 'postgres', - # 'PASSWORD': '', - # 'HOST': 'db', - # } db_config = { 'ENGINE': 'django.db.backends.postgresql', 'NAME': 'querybuilder', - 'USER': 'travis', - 'PORT': '5433', + 'USER': 'postgres', + 'PASSWORD': '', + 'HOST': 'db', } elif test_db == 'sqlite': db_config = { @@ -45,6 +40,14 @@ def configure_settings(): db_config2['NAME'] = f'{db_config2["NAME"]}2' db_config2['TEST_MIRROR'] = 'default' + # Check env for db override (used for github actions) + if os.environ.get('DB_SETTINGS'): + db_config = json.loads(os.environ.get('DB_SETTINGS')) + + # Check env for db override (used for github actions) + if os.environ.get('DB_SETTINGS2'): + db_config2 = json.loads(os.environ.get('DB_SETTINGS2')) + settings.configure( TEST_RUNNER='django_nose.NoseTestSuiteRunner', NOSE_ARGS=['--nocapture', '--nologcapture', '--verbosity=1'], From 67bafcacb54d398207231dbcc4f28c8022629cc0 Mon Sep 17 00:00:00 2001 From: Wes Okes Date: Mon, 22 Aug 2022 14:49:06 -0400 Subject: [PATCH 15/27] flake --- querybuilder/tests/update_tests.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/querybuilder/tests/update_tests.py b/querybuilder/tests/update_tests.py index 21ad1b2..07870c4 100644 --- a/querybuilder/tests/update_tests.py +++ b/querybuilder/tests/update_tests.py @@ -66,7 +66,8 @@ def test_update_single_row(self): "SET user_id = new_values.user_id, " "first_name = new_values.first_name, " "last_name = new_values.last_name " - f"FROM (VALUES (1{self.integer_cast_string}, 1::integer, 'Test''s'::varchar(64), '\"User\"'::varchar(64))) " + f"FROM (VALUES (1{self.integer_cast_string}, 1::integer, " + "'Test''s'::varchar(64), '\"User\"'::varchar(64))) " "AS new_values (id, user_id, first_name, last_name) " "WHERE querybuilder_tests_account.id = new_values.id" ) @@ -121,7 +122,8 @@ def test_update_multiple_rows(self): 'SET user_id = new_values.user_id, ' 'first_name = new_values.first_name, ' 'last_name = new_values.last_name ' - f'FROM (VALUES (%s{self.integer_cast_string}, %s::integer, %s::varchar(64), %s::varchar(64)), (%s, %s, %s, %s)) ' + f'FROM (VALUES (%s{self.integer_cast_string}, %s::integer, %s::varchar(64), %s::varchar(64)), ' + '(%s, %s, %s, %s)) ' 'AS new_values (id, user_id, first_name, last_name) ' 'WHERE querybuilder_tests_account.id = new_values.id' ) From 95bcb37174d861b7431aa48ef86aca47b9d30ff7 Mon Sep 17 00:00:00 2001 From: Wes Okes Date: Mon, 22 Aug 2022 14:53:48 -0400 Subject: [PATCH 16/27] more versions --- .github/workflows/tests.yml | 39 ++++++++++++++++++------------------- 1 file changed, 19 insertions(+), 20 deletions(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 27496ea..f057c67 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -12,33 +12,32 @@ jobs: strategy: fail-fast: false matrix: -# python: ['3.7', '3.8', '3.9'] - python: ['3.7'] + python: ['3.7', '3.8', '3.9'] # Time to switch to pytest or nose2? # nosetests is broken on 3.10 # AttributeError: module 'collections' has no attribute 'Callable' # https://github.com/nose-devs/nose/issues/1099 django: - 'Django~=2.2.0' -# - 'Django~=3.0.0' -# - 'Django~=3.1.0' -# - 'Django~=3.2.0' -# - 'Django~=4.0.0' -# - 'Django~=4.1.0' + - 'Django~=3.0.0' + - 'Django~=3.1.0' + - 'Django~=3.2.0' + - 'Django~=4.0.0' + - 'Django~=4.1.0' experimental: [false] -# include: -# - python: '3.9' -# django: 'https://github.com/django/django/archive/refs/heads/main.zip#egg=Django' -# experimental: true -# # NOTE this job will appear to pass even when it fails because of -# # `continue-on-error: true`. Github Actions apparently does not -# # have this feature, similar to Travis' allow-failure, yet. -# # https://github.com/actions/toolkit/issues/399 -# exclude: -# - python: '3.7' -# django: 'Django~=4.0.0' -# - python: '3.7' -# django: 'Django~=4.1.0' + include: + - python: '3.9' + django: 'https://github.com/django/django/archive/refs/heads/main.zip#egg=Django' + experimental: true + # NOTE this job will appear to pass even when it fails because of + # `continue-on-error: true`. Github Actions apparently does not + # have this feature, similar to Travis' allow-failure, yet. + # https://github.com/actions/toolkit/issues/399 + exclude: + - python: '3.7' + django: 'Django~=4.0.0' + - python: '3.7' + django: 'Django~=4.1.0' services: postgres: image: postgres:latest From 928d72f48d223b2713d57f0272502e874e381eb6 Mon Sep 17 00:00:00 2001 From: Wes Okes Date: Mon, 22 Aug 2022 15:02:08 -0400 Subject: [PATCH 17/27] handle 3.1 --- querybuilder/tests/json_tests.py | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/querybuilder/tests/json_tests.py b/querybuilder/tests/json_tests.py index bee8ff1..1638665 100644 --- a/querybuilder/tests/json_tests.py +++ b/querybuilder/tests/json_tests.py @@ -47,8 +47,8 @@ def test_one(self): ) ) - # Django 3.0 changes the raw queryset behavior so querybuilder isn't going to change that behavior - if VERSION[0] >= 3: + # Django 3.1 changes the raw queryset behavior so querybuilder isn't going to change that behavior + if self.is_31_or_above(): self.assertEqual(query.select(), [{'my_two_alias': '"two"'}]) else: self.assertEqual(query.select(), [{'my_two_alias': 'two'}]) @@ -64,8 +64,8 @@ def test_one(self): ) ) - # Django 3.0 changes the raw queryset behavior so querybuilder isn't going to change that behavior - if VERSION[0] >= 3: + # Django 3.1 changes the raw queryset behavior so querybuilder isn't going to change that behavior + if self.is_31_or_above(): self.assertEqual(query.select(), [{'my_one_alias': '1'}]) else: self.assertEqual(query.select(), [{'my_one_alias': 1}]) @@ -82,6 +82,13 @@ def test_one(self): ) self.assertEqual(query.select(), []) + def is_31_or_above(self): + if VERSION[0] == 3 and VERSION[1] >= 1: + return True + elif VERSION[0] > 3: + return True + return False + @override_settings(DEBUG=True) class JsonQuerysetTest(QuerybuilderTestCase): From 61323465f19579303f17af64323a3df06035c35d Mon Sep 17 00:00:00 2001 From: Wes Okes Date: Mon, 22 Aug 2022 15:07:28 -0400 Subject: [PATCH 18/27] only cast on 4.1 --- querybuilder/tests/update_tests.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/querybuilder/tests/update_tests.py b/querybuilder/tests/update_tests.py index 07870c4..c655d18 100644 --- a/querybuilder/tests/update_tests.py +++ b/querybuilder/tests/update_tests.py @@ -19,7 +19,7 @@ def setUp(self): # Starting on Django 4, the id field adds ::integer automatically self.integer_cast_string = '' - if VERSION[0] >= 4: + if (VERSION[0] == 4 and VERSION[1] >= 1) or VERSION[0] >= 5: self.integer_cast_string = '::integer' def test_update_single_row(self): From b2435ca125b8f0cfd60b310b0c2c9df49373f2a4 Mon Sep 17 00:00:00 2001 From: Wes Okes Date: Mon, 22 Aug 2022 15:15:29 -0400 Subject: [PATCH 19/27] update note --- docs/release_notes.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/release_notes.rst b/docs/release_notes.rst index 1cdbacd..d4928a9 100644 --- a/docs/release_notes.rst +++ b/docs/release_notes.rst @@ -9,6 +9,7 @@ v3.0.0 ------ * Add support for django 3.2, 4.0, 4.1 * Add support for python 3.9 +* Drop python 3.6 v2.0.0 ------ From 51312e9ccbb5b507b5681ba235f32c03c1b898cd Mon Sep 17 00:00:00 2001 From: Wes Okes Date: Mon, 22 Aug 2022 15:32:17 -0400 Subject: [PATCH 20/27] manifest and contributor --- CONTRIBUTORS | 1 + MANIFEST.in | 2 ++ 2 files changed, 3 insertions(+) diff --git a/CONTRIBUTORS b/CONTRIBUTORS index d5c8391..6e275fd 100644 --- a/CONTRIBUTORS +++ b/CONTRIBUTORS @@ -4,3 +4,4 @@ Andrew Plummer (https://github.com/plumdog) Jure Žvelc (https://github.com/jzvelc) Timothy J Laurent (https://github.com/timothyjlaurent) NickHilton (https://github.com/NickHilton) +John Vandenberg (https://github.com/jayvdb) diff --git a/MANIFEST.in b/MANIFEST.in index ddffd89..17fc0c4 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -1,3 +1,5 @@ include README.rst include LICENSE recursive-include requirements * +include *.py +recursive-include docs *.py From 57a32a09ad107289a3232daf3fb34f0bde6c3306 Mon Sep 17 00:00:00 2001 From: Wes Okes Date: Mon, 22 Aug 2022 15:43:05 -0400 Subject: [PATCH 21/27] debugging 4.1 --- .github/workflows/tests.yml | 39 +++++++++++++++--------------- querybuilder/query.py | 4 +++ querybuilder/tests/update_tests.py | 14 +++++------ 3 files changed, 31 insertions(+), 26 deletions(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index f057c67..f38e5f3 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -12,32 +12,33 @@ jobs: strategy: fail-fast: false matrix: - python: ['3.7', '3.8', '3.9'] + python: ['3.9'] +# python: ['3.7', '3.8', '3.9'] # Time to switch to pytest or nose2? # nosetests is broken on 3.10 # AttributeError: module 'collections' has no attribute 'Callable' # https://github.com/nose-devs/nose/issues/1099 django: - - 'Django~=2.2.0' - - 'Django~=3.0.0' - - 'Django~=3.1.0' - - 'Django~=3.2.0' - - 'Django~=4.0.0' +# - 'Django~=2.2.0' +# - 'Django~=3.0.0' +# - 'Django~=3.1.0' +# - 'Django~=3.2.0' +# - 'Django~=4.0.0' - 'Django~=4.1.0' experimental: [false] - include: - - python: '3.9' - django: 'https://github.com/django/django/archive/refs/heads/main.zip#egg=Django' - experimental: true - # NOTE this job will appear to pass even when it fails because of - # `continue-on-error: true`. Github Actions apparently does not - # have this feature, similar to Travis' allow-failure, yet. - # https://github.com/actions/toolkit/issues/399 - exclude: - - python: '3.7' - django: 'Django~=4.0.0' - - python: '3.7' - django: 'Django~=4.1.0' +# include: +# - python: '3.9' +# django: 'https://github.com/django/django/archive/refs/heads/main.zip#egg=Django' +# experimental: true +# # NOTE this job will appear to pass even when it fails because of +# # `continue-on-error: true`. Github Actions apparently does not +# # have this feature, similar to Travis' allow-failure, yet. +# # https://github.com/actions/toolkit/issues/399 +# exclude: +# - python: '3.7' +# django: 'Django~=4.0.0' +# - python: '3.7' +# django: 'Django~=4.1.0' services: postgres: image: postgres:latest diff --git a/querybuilder/query.py b/querybuilder/query.py index f3da935..3600d98 100644 --- a/querybuilder/query.py +++ b/querybuilder/query.py @@ -1171,6 +1171,10 @@ def get_update_sql(self, rows): field_object = self.tables[0].model._meta.get_field(field_names[field_index]) db_type = field_object.db_type(self.connection) + print('---------') + print('---------') + print('---------') + print('this is casting', db_type) # Don't cast the pk if db_type in SERIAL_DTYPES: placeholders.append('%s') diff --git a/querybuilder/tests/update_tests.py b/querybuilder/tests/update_tests.py index c655d18..f27e50a 100644 --- a/querybuilder/tests/update_tests.py +++ b/querybuilder/tests/update_tests.py @@ -18,9 +18,9 @@ def setUp(self): self.logger.start_logging() # Starting on Django 4, the id field adds ::integer automatically - self.integer_cast_string = '' - if (VERSION[0] == 4 and VERSION[1] >= 1) or VERSION[0] >= 5: - self.integer_cast_string = '::integer' + # self.integer_cast_string = '' + # if (VERSION[0] == 4 and VERSION[1] >= 1) or VERSION[0] >= 5: + # self.integer_cast_string = '::integer' def test_update_single_row(self): query = Query().from_table( @@ -46,7 +46,7 @@ def test_update_single_row(self): 'SET user_id = new_values.user_id, ' 'first_name = new_values.first_name, ' 'last_name = new_values.last_name ' - f'FROM (VALUES (%s{self.integer_cast_string}, %s::integer, %s::varchar(64), %s::varchar(64))) ' + f'FROM (VALUES (%s, %s::integer, %s::varchar(64), %s::varchar(64))) ' 'AS new_values (id, user_id, first_name, last_name) ' 'WHERE querybuilder_tests_account.id = new_values.id' ) @@ -66,7 +66,7 @@ def test_update_single_row(self): "SET user_id = new_values.user_id, " "first_name = new_values.first_name, " "last_name = new_values.last_name " - f"FROM (VALUES (1{self.integer_cast_string}, 1::integer, " + f"FROM (VALUES (1, 1::integer, " "'Test''s'::varchar(64), '\"User\"'::varchar(64))) " "AS new_values (id, user_id, first_name, last_name) " "WHERE querybuilder_tests_account.id = new_values.id" @@ -122,7 +122,7 @@ def test_update_multiple_rows(self): 'SET user_id = new_values.user_id, ' 'first_name = new_values.first_name, ' 'last_name = new_values.last_name ' - f'FROM (VALUES (%s{self.integer_cast_string}, %s::integer, %s::varchar(64), %s::varchar(64)), ' + f'FROM (VALUES (%s, %s::integer, %s::varchar(64), %s::varchar(64)), ' '(%s, %s, %s, %s)) ' 'AS new_values (id, user_id, first_name, last_name) ' 'WHERE querybuilder_tests_account.id = new_values.id' @@ -146,7 +146,7 @@ def test_update_multiple_rows(self): "SET user_id = new_values.user_id, " "first_name = new_values.first_name, " "last_name = new_values.last_name " - f"FROM (VALUES (1{self.integer_cast_string}, 1::integer, 'Test'::varchar(64), 'User'::varchar(64)), " + f"FROM (VALUES (1, 1::integer, 'Test'::varchar(64), 'User'::varchar(64)), " "(2, 2, 'Test2', 'User2')) " "AS new_values (id, user_id, first_name, last_name) " "WHERE querybuilder_tests_account.id = new_values.id" From fe4d596f1c86077507c08c8f9209e1228301a666 Mon Sep 17 00:00:00 2001 From: Wes Okes Date: Mon, 22 Aug 2022 15:54:30 -0400 Subject: [PATCH 22/27] include 4 to see update query --- .github/workflows/tests.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index f38e5f3..d2fac73 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -23,7 +23,7 @@ jobs: # - 'Django~=3.0.0' # - 'Django~=3.1.0' # - 'Django~=3.2.0' -# - 'Django~=4.0.0' + - 'Django~=4.0.0' - 'Django~=4.1.0' experimental: [false] # include: From c0f702c5137f113b9b4f0ebcc180afd0c7073a25 Mon Sep 17 00:00:00 2001 From: Wes Okes Date: Mon, 22 Aug 2022 16:35:54 -0400 Subject: [PATCH 23/27] possibly handle 4.1 casting --- querybuilder/query.py | 20 ++++++++++++++------ querybuilder/tests/update_tests.py | 6 ------ 2 files changed, 14 insertions(+), 12 deletions(-) diff --git a/querybuilder/query.py b/querybuilder/query.py index 3600d98..ea5a59d 100644 --- a/querybuilder/query.py +++ b/querybuilder/query.py @@ -1,5 +1,6 @@ from copy import deepcopy +from django import VERSION from django.db import connection as default_django_connection from django.db.models import Q, AutoField from django.db.models.query import QuerySet @@ -1119,6 +1120,17 @@ def get_insert_sql(self, rows): return self.sql, sql_args + def should_not_cast_value(self, db_type): + """ + In Django 4.1 on PostgreSQL, AutoField, BigAutoField, and SmallAutoField are now created as identity + columns rather than serial columns with sequences. + """ + if db_type in SERIAL_DTYPES: + return True + if (VERSION[0] == 4 and VERSION[1] >= 1) or VERSION[0] >= 5: + return True + return False + def get_update_sql(self, rows): """ Returns SQL UPDATE for rows ``rows`` @@ -1171,12 +1183,8 @@ def get_update_sql(self, rows): field_object = self.tables[0].model._meta.get_field(field_names[field_index]) db_type = field_object.db_type(self.connection) - print('---------') - print('---------') - print('---------') - print('this is casting', db_type) - # Don't cast the pk - if db_type in SERIAL_DTYPES: + # Don't cast serial types + if self.should_not_cast_value(db_type): placeholders.append('%s') else: # Cast the placeholder to the data type diff --git a/querybuilder/tests/update_tests.py b/querybuilder/tests/update_tests.py index f27e50a..4104282 100644 --- a/querybuilder/tests/update_tests.py +++ b/querybuilder/tests/update_tests.py @@ -1,6 +1,5 @@ import json -from django import VERSION from django.test.utils import override_settings from django_dynamic_fixture import G @@ -17,11 +16,6 @@ def setUp(self): self.logger = Logger() self.logger.start_logging() - # Starting on Django 4, the id field adds ::integer automatically - # self.integer_cast_string = '' - # if (VERSION[0] == 4 and VERSION[1] >= 1) or VERSION[0] >= 5: - # self.integer_cast_string = '::integer' - def test_update_single_row(self): query = Query().from_table( table=Account, From ee7901cbf2c40667e899aa72427f9c142ab74df8 Mon Sep 17 00:00:00 2001 From: Wes Okes Date: Mon, 22 Aug 2022 16:40:56 -0400 Subject: [PATCH 24/27] don't use fstring --- querybuilder/tests/update_tests.py | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/querybuilder/tests/update_tests.py b/querybuilder/tests/update_tests.py index 4104282..ec02ada 100644 --- a/querybuilder/tests/update_tests.py +++ b/querybuilder/tests/update_tests.py @@ -40,7 +40,7 @@ def test_update_single_row(self): 'SET user_id = new_values.user_id, ' 'first_name = new_values.first_name, ' 'last_name = new_values.last_name ' - f'FROM (VALUES (%s, %s::integer, %s::varchar(64), %s::varchar(64))) ' + 'FROM (VALUES (%s, %s::integer, %s::varchar(64), %s::varchar(64))) ' 'AS new_values (id, user_id, first_name, last_name) ' 'WHERE querybuilder_tests_account.id = new_values.id' ) @@ -60,8 +60,7 @@ def test_update_single_row(self): "SET user_id = new_values.user_id, " "first_name = new_values.first_name, " "last_name = new_values.last_name " - f"FROM (VALUES (1, 1::integer, " - "'Test''s'::varchar(64), '\"User\"'::varchar(64))) " + "FROM (VALUES (1, 1::integer, 'Test''s'::varchar(64), '\"User\"'::varchar(64))) " "AS new_values (id, user_id, first_name, last_name) " "WHERE querybuilder_tests_account.id = new_values.id" ) @@ -116,7 +115,7 @@ def test_update_multiple_rows(self): 'SET user_id = new_values.user_id, ' 'first_name = new_values.first_name, ' 'last_name = new_values.last_name ' - f'FROM (VALUES (%s, %s::integer, %s::varchar(64), %s::varchar(64)), ' + 'FROM (VALUES (%s, %s::integer, %s::varchar(64), %s::varchar(64)), ' '(%s, %s, %s, %s)) ' 'AS new_values (id, user_id, first_name, last_name) ' 'WHERE querybuilder_tests_account.id = new_values.id' @@ -140,7 +139,7 @@ def test_update_multiple_rows(self): "SET user_id = new_values.user_id, " "first_name = new_values.first_name, " "last_name = new_values.last_name " - f"FROM (VALUES (1, 1::integer, 'Test'::varchar(64), 'User'::varchar(64)), " + "FROM (VALUES (1, 1::integer, 'Test'::varchar(64), 'User'::varchar(64)), " "(2, 2, 'Test2', 'User2')) " "AS new_values (id, user_id, first_name, last_name) " "WHERE querybuilder_tests_account.id = new_values.id" From b081fd08e4ceb46cf03e972b58771e89f73992d1 Mon Sep 17 00:00:00 2001 From: Wes Okes Date: Mon, 22 Aug 2022 17:51:27 -0400 Subject: [PATCH 25/27] better logic for 4.1 --- querybuilder/query.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/querybuilder/query.py b/querybuilder/query.py index ea5a59d..1b75b79 100644 --- a/querybuilder/query.py +++ b/querybuilder/query.py @@ -1120,15 +1120,17 @@ def get_insert_sql(self, rows): return self.sql, sql_args - def should_not_cast_value(self, db_type): + def should_not_cast_value(self, field_object): """ In Django 4.1 on PostgreSQL, AutoField, BigAutoField, and SmallAutoField are now created as identity columns rather than serial columns with sequences. """ + db_type = field_object.db_type(self.connection) if db_type in SERIAL_DTYPES: return True if (VERSION[0] == 4 and VERSION[1] >= 1) or VERSION[0] >= 5: - return True + if getattr(field_object, 'primary_key', None) and getattr(field_object, 'serialized', None) is False: + return True return False def get_update_sql(self, rows): @@ -1184,7 +1186,7 @@ def get_update_sql(self, rows): db_type = field_object.db_type(self.connection) # Don't cast serial types - if self.should_not_cast_value(db_type): + if self.should_not_cast_value(field_object): placeholders.append('%s') else: # Cast the placeholder to the data type From de71eeba2f84f8b1b53b861a6431eab4b587cee7 Mon Sep 17 00:00:00 2001 From: Wes Okes Date: Mon, 22 Aug 2022 17:56:15 -0400 Subject: [PATCH 26/27] fix name --- querybuilder/query.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/querybuilder/query.py b/querybuilder/query.py index 1b75b79..7dfb681 100644 --- a/querybuilder/query.py +++ b/querybuilder/query.py @@ -1129,7 +1129,7 @@ def should_not_cast_value(self, field_object): if db_type in SERIAL_DTYPES: return True if (VERSION[0] == 4 and VERSION[1] >= 1) or VERSION[0] >= 5: - if getattr(field_object, 'primary_key', None) and getattr(field_object, 'serialized', None) is False: + if getattr(field_object, 'primary_key', None) and getattr(field_object, 'serialize', None) is False: return True return False From 924b32572c5b09d89ffd1ec593ff2aa4695b2e47 Mon Sep 17 00:00:00 2001 From: Wes Okes Date: Mon, 22 Aug 2022 17:59:54 -0400 Subject: [PATCH 27/27] test all versions --- .github/workflows/tests.yml | 37 ++++++++++++++++++------------------- 1 file changed, 18 insertions(+), 19 deletions(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index d2fac73..f057c67 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -12,33 +12,32 @@ jobs: strategy: fail-fast: false matrix: - python: ['3.9'] -# python: ['3.7', '3.8', '3.9'] + python: ['3.7', '3.8', '3.9'] # Time to switch to pytest or nose2? # nosetests is broken on 3.10 # AttributeError: module 'collections' has no attribute 'Callable' # https://github.com/nose-devs/nose/issues/1099 django: -# - 'Django~=2.2.0' -# - 'Django~=3.0.0' -# - 'Django~=3.1.0' -# - 'Django~=3.2.0' + - 'Django~=2.2.0' + - 'Django~=3.0.0' + - 'Django~=3.1.0' + - 'Django~=3.2.0' - 'Django~=4.0.0' - 'Django~=4.1.0' experimental: [false] -# include: -# - python: '3.9' -# django: 'https://github.com/django/django/archive/refs/heads/main.zip#egg=Django' -# experimental: true -# # NOTE this job will appear to pass even when it fails because of -# # `continue-on-error: true`. Github Actions apparently does not -# # have this feature, similar to Travis' allow-failure, yet. -# # https://github.com/actions/toolkit/issues/399 -# exclude: -# - python: '3.7' -# django: 'Django~=4.0.0' -# - python: '3.7' -# django: 'Django~=4.1.0' + include: + - python: '3.9' + django: 'https://github.com/django/django/archive/refs/heads/main.zip#egg=Django' + experimental: true + # NOTE this job will appear to pass even when it fails because of + # `continue-on-error: true`. Github Actions apparently does not + # have this feature, similar to Travis' allow-failure, yet. + # https://github.com/actions/toolkit/issues/399 + exclude: + - python: '3.7' + django: 'Django~=4.0.0' + - python: '3.7' + django: 'Django~=4.1.0' services: postgres: image: postgres:latest