diff --git a/.github/workflows/python-tests.yml b/.github/workflows/python-tests.yml index fdc70d1..069c0bd 100644 --- a/.github/workflows/python-tests.yml +++ b/.github/workflows/python-tests.yml @@ -11,23 +11,11 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - python-version: ["3.6", "3.7", "3.8", "3.9", "3.10"] + python-version: ["3.7", "3.8", "3.9", "3.10"] postgres-version: ["9.4", "9.5", "9.6", "10", "11", "12", "13"] django-version: ["1.8", "1.9", "1.10", "1.11", "2.0", "2.1", "2.2", "3.0", "3.1", "3.2", "4.0"] exclude: - # Django 3.0+ doesn't support python 3.5 - - python-version: "3.5" - django-version: "3.0" - - python-version: "3.5" - django-version: "3.1" - - python-version: "3.5" - django-version: "3.2" - - python-version: "3.5" - django-version: "4.0" - # Django 4.0+ doesn't support python 3.6, 3.7 - - python-version: "3.6" - django-version: "4.0" - python-version: "3.7" django-version: "4.0" @@ -53,12 +41,6 @@ jobs: # python 3.6+ has deprecated issue with django before 1.11 # https://stackoverflow.com/questions/41343263/provide-classcell-example-for-python-3-6-metaclass\ - - python-version: "3.6" - django-version: "1.8" - - python-version: "3.6" - django-version: "1.9" - - python-version: "3.6" - django-version: "1.10" - python-version: "3.7" django-version: "1.8" - python-version: "3.7" diff --git a/Dockerfile b/Dockerfile index a507b6c..7760183 100644 --- a/Dockerfile +++ b/Dockerfile @@ -33,10 +33,12 @@ WORKDIR /app/src # Install dependencies # set -eu "breaks" pipeline on first error COPY ./requirements-test.txt /app/requirements-test.txt -RUN --mount=type=cache,target=/root/.cache/pip \ - set -eu && \ + +RUN set -eu && \ apt-get update && \ - apt-get install -y libpq-dev python3-dev && \ + apt-get install -y libpq-dev python3-dev + +RUN --mount=type=cache,target=/root/.cache/pip \ python3 -m pip install --upgrade pip setuptools wheel && \ python3 -m pip install --upgrade --requirement /app/requirements-test.txt diff --git a/requirements-test.txt b/requirements-test.txt index ea89891..0de4d32 100644 --- a/requirements-test.txt +++ b/requirements-test.txt @@ -1,7 +1,7 @@ django>=1.7 # FIXME On later versions I get strange error 'database connection isn't set to UTC' -psycopg2-binary<=2.8.6 +psycopg2<=2.8.6 pytz; python_version < '3.3' typing; python_version < '3.5' diff --git a/runtests.py b/runtests.py index a762f7b..6e01327 100644 --- a/runtests.py +++ b/runtests.py @@ -9,12 +9,14 @@ import sys import django +import psycopg2 from django.conf import settings from django.test.utils import get_runner if __name__ == "__main__": print('Django: ', django.VERSION) print('Python: ', sys.version) + print('Psycopg2: ', psycopg2.__version__) # Add the src directory to sys.path curdir = os.path.dirname(os.path.realpath(__file__)) diff --git a/setup.py b/setup.py index 660edaa..3fda75a 100644 --- a/setup.py +++ b/setup.py @@ -13,7 +13,7 @@ setup( name='django-pg-bulk-update', - version='3.7.0', + version='3.7.1', packages=['django_pg_bulk_update'], package_dir={'': 'src'}, url='https://github.com/M1hacka/django-pg-bulk-update', diff --git a/src/django_pg_bulk_update/query.py b/src/django_pg_bulk_update/query.py index 709c48d..7ddf60f 100644 --- a/src/django_pg_bulk_update/query.py +++ b/src/django_pg_bulk_update/query.py @@ -15,6 +15,7 @@ from django.db.models.sql import UpdateQuery from django.db.models.sql.where import WhereNode +from tests.compatibility import get_empty_q_object from .compatibility import get_postgres_version, get_model_fields, returning_available, string_types, Iterable from .set_functions import AbstractSetFunction, NowSetFunction from .types import TOperators, TFieldNames, TUpdateValues, TSetFunctions, TOperatorsValid, TUpdateValuesValid, \ @@ -305,8 +306,7 @@ def pdnf_clause(key_fields, field_values, key_fields_ops=()): field_values = list(field_values) if len(field_values) == 0: - # Empty condition should return empty result - return ~Q() + return get_empty_q_object() or_cond = Q() for values_item in field_values: diff --git a/tests/compatibility.py b/tests/compatibility.py index 48832d4..575c4cd 100644 --- a/tests/compatibility.py +++ b/tests/compatibility.py @@ -1,6 +1,9 @@ from datetime import date +from django.db.models import Value, Q, BooleanField +from django.template.backends import django from django.utils.timezone import now +from django.utils.tree import Node from django_pg_bulk_update.compatibility import get_postgres_version @@ -16,3 +19,34 @@ def get_auto_now_date(key_is_unique=True): # type: (bool) -> date :return: Date object """ return date.today() if not key_is_unique or get_postgres_version() < (9, 5) else now().date() + + +class EmptyQ(Q): + """ + Empty condition should return empty result + See https://stackoverflow.com/questions/35893867/always-false-q-object + """ + def __init__(self): + Node.__init__(self, children=[ + ("pk", Value(False, output_field=BooleanField())) + ], connector=None, negated=False) + + def __str__(self): + # Django before 3.0 raises 'TypeError: cannot unpack non-iterable Value object' + # when trying to insert Q(Value(False, output_field=BooleanField())) to + return 'FALSE' + + +def get_empty_q_object() -> Q: + """ + Generates Q-Object, which leads to empty QuerySet. + See https://stackoverflow.com/questions/35893867/always-false-q-object + """ + import django + if django.VERSION >= (3,): + return Q(Value(False, output_field=BooleanField())) + + # Django before 3.0 doesn't work with not binary conditions and expects field name to be always present. + # It raises TypeError: cannot unpack non-iterable Value object. + # This condition raises EmptyResultSet while forming query and doesn't even execute it + return Q(pk__in=[]) diff --git a/tests/test_pdnf_clause.py b/tests/test_pdnf_clause.py index 284b811..8a29e50 100644 --- a/tests/test_pdnf_clause.py +++ b/tests/test_pdnf_clause.py @@ -89,6 +89,9 @@ def test_lte(self): def test_between(self): self._test_filter({2, 3, 4}, ['id'], [[[2, 4]]], operations=['between']) + def test_empty_data(self): + self._test_filter(set(), ['id'], []) + class TestReadmeExample(TestCase): def test_example(self):