diff --git a/.travis.yml b/.travis.yml index a2ae2616..3abb014f 100644 --- a/.travis.yml +++ b/.travis.yml @@ -3,7 +3,7 @@ sudo: required language: python python: - - "3.7" + - "3.8" before_script: - echo "deb http://www.apache.org/dist/cassandra/debian 40x main" | sudo tee -a /etc/apt/sources.list.d/cassandra.sources.list @@ -14,10 +14,12 @@ before_script: env: matrix: - - TOX_ENV=py37-django21 - - TOX_ENV=py37-django22 - - TOX_ENV=py37-django30 - - TOX_ENV=py37-djangomaster + - TOX_ENV=django21 + - TOX_ENV=django22 + - TOX_ENV=django30 + - TOX_ENV=django31 + - TOX_ENV=django32 + - TOX_ENV=djangomaster global: - CASS_DRIVER_NO_CYTHON=1 - CASS_HOST=127.0.0.1 @@ -44,4 +46,4 @@ install: - pip3 install tox-travis lz4 script: - - wait-for-it 127.0.0.1:9042 -- tox -e $TOX_ENV + - wait-for-it 127.0.0.1:9042 -t 120 -- tox -e $TOX_ENV diff --git a/Dockerfile b/Dockerfile index 2c680e71..18dbfd45 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,4 @@ -FROM python:3.7 +FROM python:3.8 ENV PYTHONUNBUFFERED=1 ENV CASS_HOST=cassandra RUN apt-get -y update diff --git a/django_cassandra_engine/__init__.py b/django_cassandra_engine/__init__.py index 009d1947..1aa0e737 100644 --- a/django_cassandra_engine/__init__.py +++ b/django_cassandra_engine/__init__.py @@ -2,7 +2,7 @@ # -*- coding: utf-8 -*- # Do not forget to change version number in mkdocs.yml also! -__version__ = (1, 6, 1) +__version__ = (1, 6, 2) __author__ = "Rafał Furmański" __contact__ = "r.furmanski@gmail.com" __homepage__ = "http://github.com/r4fek/django-cassandra-engine" diff --git a/django_cassandra_engine/base/operations.py b/django_cassandra_engine/base/operations.py index 5dabc54d..9083676c 100644 --- a/django_cassandra_engine/base/operations.py +++ b/django_cassandra_engine/base/operations.py @@ -1,4 +1,5 @@ from django.db.backends.base.operations import BaseDatabaseOperations + from django_cassandra_engine.utils import get_cassandra_connection @@ -29,10 +30,9 @@ def prep_for_iexact_query(self, value): """ return value - def sql_flush(self, style, tables, sequences, allow_cascade=False): + def sql_flush(self, style, tables, *args, **kwargs): """ Truncate all existing tables in current keyspace. - :returns: an empty list """ @@ -44,8 +44,11 @@ def sql_flush(self, style, tables, sequences, allow_cascade=False): return cql_list return [] - def execute_sql_flush(self, using, cql_list): - for cql in cql_list: + def execute_sql_flush(self, *args): + # In previous django versions first parameter was `using` and second `sql_list`. + # In Django 3.1 only one parameter `sql_list` is present. + # One thing is certain though: last parameter is `sql_list` + for cql in args[-1]: self.connection.connection.execute(cql) def prepare_sql_script(self, sql): diff --git a/django_cassandra_engine/models/__init__.py b/django_cassandra_engine/models/__init__.py index 23f05180..360787e0 100644 --- a/django_cassandra_engine/models/__init__.py +++ b/django_cassandra_engine/models/__init__.py @@ -1,27 +1,36 @@ -import logging -import inspect -import copy -import warnings -from operator import attrgetter -import collections from functools import partial from itertools import chain +from operator import attrgetter +import collections +import copy +import inspect +import logging +import warnings -import six -from django.conf import settings from django.apps import apps +from django.conf import settings from django.core import validators -from django.db.models.base import ModelBase -from django.utils.translation import ugettext_lazy as _ from django.db.models import options +from django.db.models.base import ModelBase +from django.utils.translation import gettext_lazy as _ +import six -from ..compat import (BaseModel, ColumnDescriptor, ModelDefinitionException, - ModelException, ModelMetaClass, OrderedDict, columns, - query) -from .constants import ORDER_BY_WARN, ORDER_BY_ERROR_HELP, PK_META_MISSING_HELP -from . import django_field_methods -from . import django_model_methods - +from ..compat import ( + BaseModel, + ColumnDescriptor, + ModelDefinitionException, + ModelException, + ModelMetaClass, + OrderedDict, + columns, + query, +) +from . import django_field_methods, django_model_methods +from .constants import ( + ORDER_BY_ERROR_HELP, + ORDER_BY_WARN, + PK_META_MISSING_HELP, +) log = logging.getLogger(__name__) _django_manager_attr_names = ('objects', 'default_manager', '_default_manager', diff --git a/django_cassandra_engine/models/django_field_methods.py b/django_cassandra_engine/models/django_field_methods.py index e55fb7f2..0057b9fb 100644 --- a/django_cassandra_engine/models/django_field_methods.py +++ b/django_cassandra_engine/models/django_field_methods.py @@ -3,13 +3,13 @@ import warnings from django import forms -from django.utils.functional import Promise -from django.utils.text import capfirst from django.core import checks, exceptions -from django.utils.encoding import smart_text from django.utils.deprecation import ( RemovedInNextVersionWarning as RemovedInDjango20Warning, ) +from django.utils.encoding import smart_str +from django.utils.functional import Promise +from django.utils.text import capfirst NOT_IMPL_MSG = 'Method not available on Cassandra model fields' @@ -21,7 +21,7 @@ def value_from_object(self, obj): def value_to_string(self, obj): # Taken from django.db.models.fields.__init__ - return smart_text(self.value_from_object(obj)) + return smart_str(self.value_from_object(obj)) def get_attname(self): diff --git a/django_cassandra_engine/sessions/backends/db.py b/django_cassandra_engine/sessions/backends/db.py index 2546a82b..b30bee6f 100644 --- a/django_cassandra_engine/sessions/backends/db.py +++ b/django_cassandra_engine/sessions/backends/db.py @@ -1,16 +1,17 @@ from datetime import datetime import logging +from django.contrib.sessions.backends import db from django.contrib.sessions.backends.db import ( - SessionStore as DjangoSessionStore + SessionStore as DjangoSessionStore, ) from django.core.exceptions import SuspiciousOperation -from django.utils.encoding import force_text +from django.utils.encoding import force_str from django.utils.functional import cached_property -from django.contrib.sessions.backends import db # monkey patch for Django versions older than 1.9 from django_cassandra_engine.sessions.models import Session as CassandraSession + db.Session = CassandraSession @@ -52,7 +53,7 @@ def load(self): if isinstance(e, SuspiciousOperation): logger = logging.getLogger('django.security.%s' % e.__class__.__name__) - logger.warning(force_text(e)) + logger.warning(force_str(e)) self.create() return {} diff --git a/django_cassandra_engine/utils.py b/django_cassandra_engine/utils.py index 0209e292..f6d08529 100644 --- a/django_cassandra_engine/utils.py +++ b/django_cassandra_engine/utils.py @@ -1,8 +1,8 @@ import inspect -import django from django.conf import settings from django.db import DEFAULT_DB_ALIAS +import django from .compat import cqlengine @@ -59,18 +59,13 @@ def get_installed_apps(): """ Return list of all installed apps """ - if django.VERSION >= (1, 7): - from django.apps import apps - - return [ - a.models_module - for a in apps.get_app_configs() - if a.models_module is not None - ] - else: - from django.db import models - - return models.get_apps() + from django.apps import apps + + return [ + a.models_module + for a in apps.get_app_configs() + if a.models_module is not None + ] def get_cql_models(app, connection=None, keyspace=None): diff --git a/requirements.txt b/requirements.txt index bf32c063..ed0d2f02 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,3 +1,3 @@ -cassandra-driver>=3.13 +cassandra-driver>=3.25 Django>=2.0 six>=1.6 diff --git a/testproject/app/tests/test_databasewrapper.py b/testproject/app/tests/test_databasewrapper.py index dc2e4ed7..9839cf77 100644 --- a/testproject/app/tests/test_databasewrapper.py +++ b/testproject/app/tests/test_databasewrapper.py @@ -1,17 +1,17 @@ from datetime import datetime +from app.models import ExampleModel, ExampleModel2 from django.core.management import call_command from django.core.management.sql import sql_flush from mock import Mock -from django_cassandra_engine.test import TestCase from django_cassandra_engine.connection import CassandraConnection +from django_cassandra_engine.test import TestCase from django_cassandra_engine.utils import ( + get_cassandra_connection, get_cql_models, get_installed_apps, - get_cassandra_connection, ) -from app.models import ExampleModel, ExampleModel2 class DatabaseWrapperTestCase(TestCase): @@ -52,7 +52,6 @@ def test_sql_flush_works(self): interactive=False, database=self.connection.alias, skip_checks=True, - reset_sequences=False, allow_cascade=False, inhibit_post_migrate=True, ) diff --git a/testproject/common/tests/test_django_cassandra_models.py b/testproject/common/tests/test_django_cassandra_models.py index e9c4c0a1..c067b961 100644 --- a/testproject/common/tests/test_django_cassandra_models.py +++ b/testproject/common/tests/test_django_cassandra_models.py @@ -230,7 +230,6 @@ def test_values_list_with_pk_can_return_multiple_pks(self): ] self.assertEqual(len(all_things.values_list('pk')), len(expected)) - @skipIf(django.VERSION[1] < 10, "For Django>1.10 only") def test_private_fields_are_set(self): private_fields = [ f.name for f in CassandraFamilyMember._meta.private_fields diff --git a/testproject/common/tests/test_drf.py b/testproject/common/tests/test_drf.py index 86729194..c392a0ef 100644 --- a/testproject/common/tests/test_drf.py +++ b/testproject/common/tests/test_drf.py @@ -1,24 +1,25 @@ -import uuid from datetime import datetime from unittest import skipIf +import uuid +from rest_framework.test import APITestCase import django import six.moves.http_client -from rest_framework.test import APITestCase try: from django.core.urlresolvers import reverse except ImportError: from django.urls import reverse -from django_cassandra_engine.test import TestCase as CassandraTestCase from common.models import ( + CassandraFamilyMember, CassandraThing, CassandraThingMultiplePK, - CassandraFamilyMember, ) from common.serializers import CassandraFamilyMemberSerializer +from django_cassandra_engine.test import TestCase as CassandraTestCase + class TestModelViewSet(APITestCase): @@ -34,7 +35,6 @@ def setUp(self): 'data_abstract': 'TeXt', } - @skipIf(django.VERSION[1] < 10, "For Django>1.10 only") def test_create_thing2a(self): response = self.client.post(self.url, self.data2a, format='json') self.assertEqual(response.status_code, six.moves.http_client.CREATED) @@ -47,7 +47,6 @@ def test_create_thing2a(self): self.assertDictEqual(response.json(), self.data2a) self.assertEqual(response.status_code, six.moves.http_client.OK) - @skipIf(django.VERSION[1] < 10, "For Django>1.10 only") def test_create_thing2b(self): response = self.client.post(self.url, self.data2b, format='json') self.assertEqual(response.status_code, six.moves.http_client.CREATED) diff --git a/testproject/common/urls.py b/testproject/common/urls.py index 40f5053a..652924a8 100644 --- a/testproject/common/urls.py +++ b/testproject/common/urls.py @@ -1,19 +1,19 @@ -from django.conf.urls import include, url +from django.urls import include, re_path from rest_framework import routers from .views import ( - ThingMultiplePKViewSet, - ThingMultiplePKListCreateAPIView, + ThingModelViewSet, ThingMultiplePKListAPIView, - ThingModelViewSet + ThingMultiplePKListCreateAPIView, + ThingMultiplePKViewSet, ) router = routers.DefaultRouter() router.register(r'thing-modelviewset', ThingModelViewSet) urlpatterns = [ - url(r'^thing-viewset/$', ThingMultiplePKViewSet.as_view({'get': 'list'}), name='thing_viewset_api'), - url(r'^thing-listcreate/$', ThingMultiplePKListCreateAPIView.as_view(), name='thing_listcreate_api'), - url(r'^thing-listview/$', ThingMultiplePKListAPIView.as_view(), name='thing_listview_api'), - url(r'^', include(router.urls)), + re_path(r'^thing-viewset/$', ThingMultiplePKViewSet.as_view({'get': 'list'}), name='thing_viewset_api'), + re_path(r'^thing-listcreate/$', ThingMultiplePKListCreateAPIView.as_view(), name='thing_listcreate_api'), + re_path(r'^thing-listview/$', ThingMultiplePKListAPIView.as_view(), name='thing_listview_api'), + re_path(r'^', include(router.urls)), ] diff --git a/testproject/model_meta/tests/tests.py b/testproject/model_meta/tests/tests.py index ac58b9f6..bb8d5a04 100644 --- a/testproject/model_meta/tests/tests.py +++ b/testproject/model_meta/tests/tests.py @@ -1,16 +1,13 @@ -from unittest import skipIf - -import django +from cassandra.cqlengine import columns as cassandra_columns from django.apps import apps from django.contrib.contenttypes.fields import GenericForeignKey from django.core.exceptions import FieldDoesNotExist from django.db.models.fields import Field from django.db.models.options import IMMUTABLE_WARNING from django.test import SimpleTestCase -from cassandra.cqlengine import columns as cassandra_columns - from model_meta.models import CassandraThing from model_meta.results import TEST_RESULTS +import django class OptionsBaseTests(SimpleTestCase): @@ -157,7 +154,6 @@ def test_related_objects_include_hidden_local_only(self): ) -@skipIf(django.VERSION[1] < 10, "For Django>1.10 only") class PrivateFieldsTests(OptionsBaseTests): def test_private_fields(self): diff --git a/testproject/sessionsapp/tests/test_sessions.py b/testproject/sessionsapp/tests/test_sessions.py index 4418fe4b..0504d184 100644 --- a/testproject/sessionsapp/tests/test_sessions.py +++ b/testproject/sessionsapp/tests/test_sessions.py @@ -8,7 +8,7 @@ from django.core.cache import InvalidCacheBackendError from django.test.utils import override_settings from django.utils import timezone -from django.utils.encoding import force_text +from django.utils.encoding import force_str from mock import Mock import six @@ -344,7 +344,7 @@ def test_session_str(self): session_key = self.session.session_key s = self.model.objects.get(session_key=session_key) - self.assertEqual(force_text(s), session_key) + self.assertEqual(force_str(s), session_key) def test_session_get_decoded(self): """ diff --git a/testproject/urls.py b/testproject/urls.py index b8aeddd5..a1fb6052 100644 --- a/testproject/urls.py +++ b/testproject/urls.py @@ -1,11 +1,8 @@ -from django.conf.urls import include, url +from django.urls import include, re_path from testproject.app import views - urlpatterns = [ - # Examples: - url(r'^$', views.home, name='home'), - # url(r'^blog/', include('blog.urls')), - url(r'^common/', include('common.urls')), + re_path(r'^$', views.home, name='home'), + re_path(r'^common/', include('common.urls')), ] diff --git a/tox.ini b/tox.ini index c81500ab..baa7c1e9 100644 --- a/tox.ini +++ b/tox.ini @@ -1,9 +1,11 @@ [tox] envlist = - {py37}-django21, - {py37}-django22, - {py37}-django30, - {py37}-django{master} + django21, + django22, + django30, + django31, + django32, + django{master} [testenv] passenv = CASS_HOST @@ -11,12 +13,12 @@ commands = python {toxinidir}/testproject/runtests.py deps = django21: Django==2.1 django22: Django==2.2 - django30: Django>=3.0.3,<4 - djangomaster: https://github.com/django/django/archive/master.tar.gz + django30: Django>=3.0.3,<3.1 + django31: Django>=3.1,<3.2 + django32: Django>=3.2,<3.3 + djangomaster: https://github.com/django/django/archive/main.tar.gz mock freezegun==0.3.6 tox - cassandra-driver>=3.22.0 + cassandra-driver>=3.25.0 djangorestframework -basepython = - py37: python3.7