Skip to content

Commit

Permalink
Merge pull request #109 from somewes/develop
Browse files Browse the repository at this point in the history
github actions and version support
  • Loading branch information
somewes authored Aug 22, 2022
2 parents c7abba6 + f921bd6 commit 79f1998
Show file tree
Hide file tree
Showing 21 changed files with 265 additions and 73 deletions.
93 changes: 93 additions & 0 deletions .github/workflows/tests.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
# 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']
# 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
21 changes: 17 additions & 4 deletions .travis.yml → .travis_old.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,24 +2,37 @@ 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
Expand All @@ -28,7 +41,7 @@ install:
- pip install tox-travis

before_script:
- psql -c 'CREATE DATABASE querybuilder;' -U postgres
- psql -c 'CREATE DATABASE querybuilder;' -U travis

script:
- tox
Expand Down
1 change: 1 addition & 0 deletions CONTRIBUTORS
Original file line number Diff line number Diff line change
Expand Up @@ -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)
2 changes: 2 additions & 0 deletions MANIFEST.in
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
include README.rst
include LICENSE
recursive-include requirements *
include *.py
recursive-include docs *.py
10 changes: 10 additions & 0 deletions docs/release_notes.rst
Original file line number Diff line number Diff line change
@@ -1,6 +1,16 @@
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
* Drop python 3.6

v2.0.1
------
* BUG: 'bigserial' dtype should not be a cast type - NickHilton
Expand Down
12 changes: 12 additions & 0 deletions querybuilder/helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
22 changes: 18 additions & 4 deletions querybuilder/query.py
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -10,7 +11,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

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

return self.sql, sql_args

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:
if getattr(field_object, 'primary_key', None) and getattr(field_object, 'serialize', None) is False:
return True
return False

def get_update_sql(self, rows):
"""
Returns SQL UPDATE for rows ``rows``
Expand Down Expand Up @@ -1171,8 +1185,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)

# Don't cast the pk
if db_type in SERIAL_DTYPES:
# Don't cast serial types
if self.should_not_cast_value(field_object):
placeholders.append('%s')
else:
# Cast the placeholder to the data type
Expand Down Expand Up @@ -1536,7 +1550,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
Expand Down
5 changes: 5 additions & 0 deletions querybuilder/tests/base.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
from django.test import TestCase


class QuerybuilderTestCase(TestCase):
databases = ['default', 'mock-second-database']
5 changes: 3 additions & 2 deletions querybuilder/tests/helper_tests.py
Original file line number Diff line number Diff line change
@@ -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
Expand Down
17 changes: 12 additions & 5 deletions querybuilder/tests/json_tests.py
Original file line number Diff line number Diff line change
@@ -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):
Expand Down Expand Up @@ -48,7 +48,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:
if self.is_31_or_above():
self.assertEqual(query.select(), [{'my_two_alias': '"two"'}])
else:
self.assertEqual(query.select(), [{'my_two_alias': 'two'}])
Expand All @@ -65,7 +65,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:
if self.is_31_or_above():
self.assertEqual(query.select(), [{'my_one_alias': '1'}])
else:
self.assertEqual(query.select(), [{'my_one_alias': 1}])
Expand All @@ -82,9 +82,16 @@ 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(TestCase):
class JsonQuerysetTest(QuerybuilderTestCase):

def test_one(self):
if get_postgres_version() < (9, 4):
Expand Down
6 changes: 3 additions & 3 deletions querybuilder/tests/logger_tests.py
Original file line number Diff line number Diff line change
@@ -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
"""
Expand All @@ -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
"""
Expand Down
1 change: 1 addition & 0 deletions querybuilder/tests/order_tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@


class OrderByTest(QueryTestCase):

def test_order_by_single_asc(self):
query = Query().from_table(
table='test_table'
Expand Down
8 changes: 4 additions & 4 deletions querybuilder/tests/query_tests.py
Original file line number Diff line number Diff line change
@@ -1,21 +1,20 @@
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


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):
"""
Expand Down Expand Up @@ -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()
Expand Down Expand Up @@ -221,6 +220,7 @@ def test_find_field_alias(self):


class FieldTest(QueryTestCase):

def test_cast(self):
query = Query().from_table(
table=Account,
Expand Down
Loading

0 comments on commit 79f1998

Please sign in to comment.