Skip to content

Commit 6b2ba25

Browse files
authored
Merge pull request #110 from ambitioninc/develop
3.0.1
2 parents 81bb470 + 79f1998 commit 6b2ba25

21 files changed

+265
-73
lines changed

.github/workflows/tests.yml

+93
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
# copied from django-cte
2+
name: querybuilder tests
3+
on:
4+
push:
5+
branches: [master]
6+
pull_request:
7+
branches: [master,develop]
8+
9+
jobs:
10+
tests:
11+
runs-on: ubuntu-latest
12+
strategy:
13+
fail-fast: false
14+
matrix:
15+
python: ['3.7', '3.8', '3.9']
16+
# Time to switch to pytest or nose2?
17+
# nosetests is broken on 3.10
18+
# AttributeError: module 'collections' has no attribute 'Callable'
19+
# https://github.com/nose-devs/nose/issues/1099
20+
django:
21+
- 'Django~=2.2.0'
22+
- 'Django~=3.0.0'
23+
- 'Django~=3.1.0'
24+
- 'Django~=3.2.0'
25+
- 'Django~=4.0.0'
26+
- 'Django~=4.1.0'
27+
experimental: [false]
28+
include:
29+
- python: '3.9'
30+
django: 'https://github.com/django/django/archive/refs/heads/main.zip#egg=Django'
31+
experimental: true
32+
# NOTE this job will appear to pass even when it fails because of
33+
# `continue-on-error: true`. Github Actions apparently does not
34+
# have this feature, similar to Travis' allow-failure, yet.
35+
# https://github.com/actions/toolkit/issues/399
36+
exclude:
37+
- python: '3.7'
38+
django: 'Django~=4.0.0'
39+
- python: '3.7'
40+
django: 'Django~=4.1.0'
41+
services:
42+
postgres:
43+
image: postgres:latest
44+
env:
45+
POSTGRES_DB: postgres
46+
POSTGRES_PASSWORD: postgres
47+
POSTGRES_USER: postgres
48+
ports:
49+
- 5432:5432
50+
options: >-
51+
--health-cmd pg_isready
52+
--health-interval 10s
53+
--health-timeout 5s
54+
--health-retries 5
55+
steps:
56+
- uses: actions/checkout@v2
57+
- uses: actions/setup-python@v2
58+
with:
59+
python-version: ${{ matrix.python }}
60+
- name: Setup
61+
run: |
62+
python --version
63+
pip install --upgrade pip wheel
64+
pip install -r requirements/requirements.txt
65+
pip install -r requirements/requirements-testing.txt
66+
pip install "${{ matrix.django }}"
67+
pip freeze
68+
- name: Run tests
69+
env:
70+
DB_SETTINGS: >-
71+
{
72+
"ENGINE":"django.db.backends.postgresql_psycopg2",
73+
"NAME":"querybuilder",
74+
"USER":"postgres",
75+
"PASSWORD":"postgres",
76+
"HOST":"localhost",
77+
"PORT":"5432"
78+
}
79+
DB_SETTINGS2: >-
80+
{
81+
"ENGINE":"django.db.backends.postgresql_psycopg2",
82+
"NAME":"querybuilder2",
83+
"USER":"postgres",
84+
"PASSWORD":"postgres",
85+
"HOST":"localhost",
86+
"PORT":"5432"
87+
}
88+
run: |
89+
coverage run manage.py test querybuilder
90+
coverage report --fail-under=90
91+
continue-on-error: ${{ matrix.experimental }}
92+
- name: Check style
93+
run: flake8 querybuilder

.travis.yml .travis_old.yml

+17-4
Original file line numberDiff line numberDiff line change
@@ -2,24 +2,37 @@ dist: xenial
22
language: python
33
sudo: false
44

5+
services:
6+
- postgresql
7+
58
python:
6-
- "3.6"
79
- "3.7"
810
- "3.8"
11+
- "3.9"
912

1013
env:
14+
global:
15+
- PGPORT=5433
16+
- PGUSER=travis
1117
matrix:
1218
- DJANGO=2.2
1319
- DJANGO=3.0
1420
- DJANGO=3.1
21+
- DJANGO=3.2
22+
- DJANGO=4.0
23+
- DJANGO=4.1
1524
- DJANGO=master
1625

1726
addons:
18-
postgresql: '9.6'
27+
postgresql: '13'
28+
apt:
29+
packages:
30+
- postgresql-13
31+
- postgresql-client-13
1932

2033
matrix:
2134
include:
22-
- { python: "3.6", env: TOXENV=flake8 }
35+
- { python: "3.7", env: TOXENV=flake8 }
2336

2437
allow_failures:
2538
- env: DJANGO=master
@@ -28,7 +41,7 @@ install:
2841
- pip install tox-travis
2942

3043
before_script:
31-
- psql -c 'CREATE DATABASE querybuilder;' -U postgres
44+
- psql -c 'CREATE DATABASE querybuilder;' -U travis
3245

3346
script:
3447
- tox

CONTRIBUTORS

+1
Original file line numberDiff line numberDiff line change
@@ -4,3 +4,4 @@ Andrew Plummer (https://github.com/plumdog)
44
Jure Žvelc (https://github.com/jzvelc)
55
Timothy J Laurent (https://github.com/timothyjlaurent)
66
NickHilton (https://github.com/NickHilton)
7+
John Vandenberg (https://github.com/jayvdb)

MANIFEST.in

+2
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
11
include README.rst
22
include LICENSE
33
recursive-include requirements *
4+
include *.py
5+
recursive-include docs *.py

docs/release_notes.rst

+10
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,16 @@
11
Release Notes
22
=============
33

4+
v3.0.1
5+
------
6+
* Switch to github actions
7+
8+
v3.0.0
9+
------
10+
* Add support for django 3.2, 4.0, 4.1
11+
* Add support for python 3.9
12+
* Drop python 3.6
13+
414
v2.0.1
515
------
616
* BUG: 'bigserial' dtype should not be a cast type - NickHilton

querybuilder/helpers.py

+12
Original file line numberDiff line numberDiff line change
@@ -51,3 +51,15 @@ def set_value_for_keypath(item, keypath, value, create_if_needed=False, delimete
5151
return item
5252
else:
5353
return None
54+
55+
56+
class Empty:
57+
pass
58+
59+
60+
def copy_instance(instance):
61+
obj = Empty()
62+
obj.__class__ = instance.__class__
63+
# Copy references to everything.
64+
obj.__dict__ = instance.__dict__.copy()
65+
return obj

querybuilder/query.py

+18-4
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
from copy import deepcopy
22

3+
from django import VERSION
34
from django.db import connection as default_django_connection
45
from django.db.models import Q, AutoField
56
from django.db.models.query import QuerySet
@@ -10,7 +11,7 @@
1011

1112

1213
from querybuilder.fields import FieldFactory, CountField, MaxField, MinField, SumField, AvgField
13-
from querybuilder.helpers import set_value_for_keypath
14+
from querybuilder.helpers import set_value_for_keypath, copy_instance
1415
from querybuilder.tables import TableFactory, ModelTable, QueryTable
1516

1617
SERIAL_DTYPES = ['serial', 'bigserial']
@@ -1119,6 +1120,19 @@ def get_insert_sql(self, rows):
11191120

11201121
return self.sql, sql_args
11211122

1123+
def should_not_cast_value(self, field_object):
1124+
"""
1125+
In Django 4.1 on PostgreSQL, AutoField, BigAutoField, and SmallAutoField are now created as identity
1126+
columns rather than serial columns with sequences.
1127+
"""
1128+
db_type = field_object.db_type(self.connection)
1129+
if db_type in SERIAL_DTYPES:
1130+
return True
1131+
if (VERSION[0] == 4 and VERSION[1] >= 1) or VERSION[0] >= 5:
1132+
if getattr(field_object, 'primary_key', None) and getattr(field_object, 'serialize', None) is False:
1133+
return True
1134+
return False
1135+
11221136
def get_update_sql(self, rows):
11231137
"""
11241138
Returns SQL UPDATE for rows ``rows``
@@ -1171,8 +1185,8 @@ def get_update_sql(self, rows):
11711185
field_object = self.tables[0].model._meta.get_field(field_names[field_index])
11721186
db_type = field_object.db_type(self.connection)
11731187

1174-
# Don't cast the pk
1175-
if db_type in SERIAL_DTYPES:
1188+
# Don't cast serial types
1189+
if self.should_not_cast_value(field_object):
11761190
placeholders.append('%s')
11771191
else:
11781192
# Cast the placeholder to the data type
@@ -1536,7 +1550,7 @@ def wrap(self, alias=None):
15361550
:return: The wrapped query
15371551
"""
15381552
field_names = self.get_field_names()
1539-
query = Query(self.connection).from_table(deepcopy(self), alias=alias)
1553+
query = Query(self.connection).from_table(copy_instance(self), alias=alias)
15401554
self.__dict__.update(query.__dict__)
15411555

15421556
# set explicit field names

querybuilder/tests/base.py

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
from django.test import TestCase
2+
3+
4+
class QuerybuilderTestCase(TestCase):
5+
databases = ['default', 'mock-second-database']

querybuilder/tests/helper_tests.py

+3-2
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,12 @@
1-
from django.test import TestCase
21
from querybuilder.helpers import value_for_keypath, set_value_for_keypath
2+
from querybuilder.tests.base import QuerybuilderTestCase
33

44

5-
class HelperTest(TestCase):
5+
class HelperTest(QuerybuilderTestCase):
66
"""
77
Tests the helper functions
88
"""
9+
910
def test_value_for_keypath(self):
1011
"""
1112
Tests all cases of value_for_keypath

querybuilder/tests/json_tests.py

+12-5
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,15 @@
11
import unittest
22
from django import VERSION
3-
from django.test.testcases import TestCase
43
from django.test.utils import override_settings
54
from querybuilder.fields import JsonField
65
from querybuilder.query import Query, JsonQueryset
6+
from querybuilder.tests.base import QuerybuilderTestCase
77
from querybuilder.tests.models import MetricRecord
88
from querybuilder.tests.utils import get_postgres_version
99

1010

1111
@override_settings(DEBUG=True)
12-
class JsonFieldTest(TestCase):
12+
class JsonFieldTest(QuerybuilderTestCase):
1313

1414
def test_one(self):
1515
if get_postgres_version() < (9, 4):
@@ -48,7 +48,7 @@ def test_one(self):
4848
)
4949

5050
# Django 3.1 changes the raw queryset behavior so querybuilder isn't going to change that behavior
51-
if VERSION[0] == 3 and VERSION[1] == 1:
51+
if self.is_31_or_above():
5252
self.assertEqual(query.select(), [{'my_two_alias': '"two"'}])
5353
else:
5454
self.assertEqual(query.select(), [{'my_two_alias': 'two'}])
@@ -65,7 +65,7 @@ def test_one(self):
6565
)
6666

6767
# Django 3.1 changes the raw queryset behavior so querybuilder isn't going to change that behavior
68-
if VERSION[0] == 3 and VERSION[1] == 1:
68+
if self.is_31_or_above():
6969
self.assertEqual(query.select(), [{'my_one_alias': '1'}])
7070
else:
7171
self.assertEqual(query.select(), [{'my_one_alias': 1}])
@@ -82,9 +82,16 @@ def test_one(self):
8282
)
8383
self.assertEqual(query.select(), [])
8484

85+
def is_31_or_above(self):
86+
if VERSION[0] == 3 and VERSION[1] >= 1:
87+
return True
88+
elif VERSION[0] > 3:
89+
return True
90+
return False
91+
8592

8693
@override_settings(DEBUG=True)
87-
class JsonQuerysetTest(TestCase):
94+
class JsonQuerysetTest(QuerybuilderTestCase):
8895

8996
def test_one(self):
9097
if get_postgres_version() < (9, 4):

querybuilder/tests/logger_tests.py

+3-3
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,14 @@
11
from django.db import connection
2-
from django.test import TestCase
32
from django.test.utils import override_settings
43

54
from querybuilder.logger import Logger, LogManager
65
from querybuilder.query import Query
6+
from querybuilder.tests.base import QuerybuilderTestCase
77
from querybuilder.tests.models import Account
88

99

1010
@override_settings(DEBUG=True)
11-
class LogManagerTest(TestCase):
11+
class LogManagerTest(QuerybuilderTestCase):
1212
"""
1313
Includes functions to test the LogManager
1414
"""
@@ -35,7 +35,7 @@ def test_log_manager(self):
3535

3636

3737
@override_settings(DEBUG=True)
38-
class LoggerTest(TestCase):
38+
class LoggerTest(QuerybuilderTestCase):
3939
"""
4040
Includes functions to test the Logger
4141
"""

querybuilder/tests/order_tests.py

+1
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33

44

55
class OrderByTest(QueryTestCase):
6+
67
def test_order_by_single_asc(self):
78
query = Query().from_table(
89
table='test_table'

querybuilder/tests/query_tests.py

+4-4
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,20 @@
11
import datetime
22

33
from django.db import connections
4-
from django.test import TestCase
54
from django_dynamic_fixture import G
65
import six
76

87
from querybuilder.fields import CountField
98
from querybuilder.query import Query
9+
from querybuilder.tests.base import QuerybuilderTestCase
1010
from querybuilder.tests.models import User, Account, Order
1111

1212

1313
def get_comparison_str(item1, item2):
1414
return 'Items are not equal.\nGot:\n{0}\nExpected:\n{1}'.format(item1, item2)
1515

1616

17-
class QueryConstructorTests(TestCase):
18-
databases = ['default', 'mock-second-database']
17+
class QueryConstructorTests(QuerybuilderTestCase):
1918

2019
def test_init_with_connection(self):
2120
"""
@@ -47,7 +46,7 @@ def test_get_cursor_for_connection(self):
4746
self.assertEqual(query3.get_cursor().db, connections['default'])
4847

4948

50-
class QueryTestCase(TestCase):
49+
class QueryTestCase(QuerybuilderTestCase):
5150

5251
def setUp(self):
5352
super(QueryTestCase, self).setUp()
@@ -221,6 +220,7 @@ def test_find_field_alias(self):
221220

222221

223222
class FieldTest(QueryTestCase):
223+
224224
def test_cast(self):
225225
query = Query().from_table(
226226
table=Account,

0 commit comments

Comments
 (0)