diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..b948985 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +*.swp +*.pyc diff --git a/django_sphinx_db/backend/models.py b/django_sphinx_db/backend/models.py index ae1935f..1ba3ed6 100644 --- a/django_sphinx_db/backend/models.py +++ b/django_sphinx_db/backend/models.py @@ -28,7 +28,7 @@ def using(self, alias): class SphinxManager(models.Manager): use_for_related_fields = True - def get_query_set(self): + def get_queryset(self): # Determine which fields are sphinx fields (full-text data) and # defer loading them. Sphinx won't return them. # TODO: we probably need a way to keep these from being loaded diff --git a/django_sphinx_db/backend/sphinx/base.py b/django_sphinx_db/backend/sphinx/base.py index e4e94bc..d6103ce 100644 --- a/django_sphinx_db/backend/sphinx/base.py +++ b/django_sphinx_db/backend/sphinx/base.py @@ -1,19 +1,36 @@ +from django.db.backends.mysql.base import * from django.db.backends.mysql.base import DatabaseWrapper as MySQLDatabaseWrapper from django.db.backends.mysql.base import DatabaseOperations as MySQLDatabaseOperations from django.db.backends.mysql.creation import DatabaseCreation as MySQLDatabaseCreation +# import features class from sphinx backend module: +from .features import DatabaseFeatures # isort:skip +from .introspection import DatabaseIntrospection +from .validation import DatabaseValidation + + class SphinxOperations(MySQLDatabaseOperations): compiler_module = "django_sphinx_db.backend.sphinx.compiler" + def quote_name(self, name): + # TODO: remove this function altogether when no longer needed. + # In one of the releases Sphinx fails with "SELECT `id`" - alike clauses + # http://sphinxsearch.com/bugs/view.php?id=1487 + if name == 'id': + return name + if name.startswith("`") and name.endswith("`"): + return name # Quoting once is enough. + return "`%s`" % name + def fulltext_search_sql(self, field_name): return 'MATCH (%s)' class SphinxCreation(MySQLDatabaseCreation): - def create_test_db(self, verbosity=1, autoclobber=False): + def create_test_db(self, verbosity=1, autoclobber=False, serialize=False, keepdb=False): # NOOP, test using regular sphinx database. - if self.connection.settings_dict['TEST_NAME']: + if 'TEST_NAME' in self.connection.settings_dict: test_name = self.connection.settings_dict['TEST_NAME'] self.connection.close() self.connection.settings_dict['NAME'] = test_name @@ -21,12 +38,15 @@ def create_test_db(self, verbosity=1, autoclobber=False): return test_name return self.connection.settings_dict['NAME'] - def destroy_test_db(self, old_database_name, verbosity=1): + def destroy_test_db(self, old_database_name, verbosity=1, keepdb=False): # NOOP, we created nothing, nothing to destroy. return class DatabaseWrapper(MySQLDatabaseWrapper): + vendor = 'sphinx' + display_name = 'sphinx' + features_class = DatabaseFeatures def __init__(self, *args, **kwargs): super(DatabaseWrapper, self).__init__(*args, **kwargs) self.ops = SphinxOperations(self) @@ -39,4 +59,9 @@ def __init__(self, *args, **kwargs): # transactions ARE. Therefore, we can just set this to True, and Django will # use transactions for clearing data between tests when all OTHER backends # support it. - self.features.supports_transactions = True + # + # deprecated in Django 1.10 + #self.features.supports_transactions = True + self.features = DatabaseFeatures(self) + self.introspection = DatabaseIntrospection(self) + self.validation = DatabaseValidation(self) diff --git a/django_sphinx_db/backend/sphinx/compiler.py b/django_sphinx_db/backend/sphinx/compiler.py index 3f95a65..694047a 100644 --- a/django_sphinx_db/backend/sphinx/compiler.py +++ b/django_sphinx_db/backend/sphinx/compiler.py @@ -1,7 +1,7 @@ from django.db.models.sql import compiler from django.db.models.sql.where import WhereNode -from django.db.models.sql.where import EmptyShortCircuit, EmptyResultSet -from django.db.models.sql.expressions import SQLEvaluator +from django.db.models.sql.where import EmptyResultSet +#from django.db.models.sql.expressions import SQLEvaluator class SphinxWhereNode(WhereNode): @@ -30,8 +30,11 @@ def make_atom(self, child, qn, connection): if hasattr(lvalue, 'process'): try: lvalue, params = lvalue.process(lookup_type, params_or_value, connection) - except EmptyShortCircuit: - raise EmptyResultSet + except: + raise +# note EmptyShortCircuit was removed in 1.9, will leave a raise: +# except EmptyShortCircuit: +# raise EmptyResultSet if isinstance(lvalue, tuple): # A direct database column lookup. field_sql = self.sql_for_columns(lvalue, qn, connection) @@ -102,8 +105,9 @@ def as_sql(self): else: placeholder = '%s' - if hasattr(val, 'evaluate'): - val = SQLEvaluator(val, self.query, allow_joins=False) + # deprecated #14030 + #if hasattr(val, 'evaluate'): + # val = SQLEvaluator(val, self.query, allow_joins=False) name = field.column if hasattr(val, 'as_sql'): sql, params = val.as_sql(qn, self.connection) @@ -123,6 +127,6 @@ def as_sql(self): class SQLAggregateCompiler(compiler.SQLAggregateCompiler, SphinxQLCompiler): pass - -class SQLDateCompiler(compiler.SQLDateCompiler, SphinxQLCompiler): - pass +# not in Django 1.8.6: +#class SQLDateCompiler(compiler.SQLDateCompiler, SphinxQLCompiler): + #pass diff --git a/django_sphinx_db/backend/sphinx/features.py b/django_sphinx_db/backend/sphinx/features.py new file mode 100644 index 0000000..e6cf834 --- /dev/null +++ b/django_sphinx_db/backend/sphinx/features.py @@ -0,0 +1,8 @@ +from django.db.backends.mysql.features import DatabaseFeatures as MYSQLDatabaseFeatures +from django.utils.functional import cached_property + +class DatabaseFeatures(MYSQLDatabaseFeatures): + + @cached_property + def is_sql_auto_is_null_enabled(self): + return 0 diff --git a/django_sphinx_db/backend/sphinx/introspection.py b/django_sphinx_db/backend/sphinx/introspection.py new file mode 100644 index 0000000..65badce --- /dev/null +++ b/django_sphinx_db/backend/sphinx/introspection.py @@ -0,0 +1,16 @@ +from django.db.backends.mysql.introspection import * +from django.db.backends.mysql.introspection import DatabaseIntrospection as MYSQLDatabaseIntrospection +from django.utils.functional import cached_property + + +class DatabaseIntrospection(MYSQLDatabaseIntrospection): + + def get_table_list(self, cursor): + """ + Returns a list of table and view names in the current database. + """ + cursor.execute("SHOW TABLES") + return [TableInfo(row[0], {'BASE TABLE': 't', 'VIEW': 'v'}.get(row[1])) + for row in cursor.fetchall()] + + diff --git a/django_sphinx_db/backend/sphinx/validation.py b/django_sphinx_db/backend/sphinx/validation.py new file mode 100644 index 0000000..ac6813a --- /dev/null +++ b/django_sphinx_db/backend/sphinx/validation.py @@ -0,0 +1,7 @@ +from django.db.backends.mysql.validation import * +from django.db.backends.mysql.validation import DatabaseValidation as MYSQLDatabaseIntrospection + +class DatabaseValidation(MYSQLDatabaseIntrospection): + def _check_sql_mode(self, **kwargs): + '''sphinx does not appear to support this sql_mode validation, skipped''' + return [] diff --git a/django_sphinx_db/routers.py b/django_sphinx_db/routers.py index 09dcabd..75384f5 100644 --- a/django_sphinx_db/routers.py +++ b/django_sphinx_db/routers.py @@ -18,3 +18,8 @@ def db_for_write(self, model, **kwargs): def allow_relation(self, obj1, obj2, **kwargs): # Allow all relations... return True + + def allow_migrate(self, db, app_label, model_name=None, model=None): + dbname = getattr(settings, 'SPHINX_DATABASE_NAME', 'sphinx') + if db == dbname: + return False diff --git a/setup.py b/setup.py index e44c27a..f3a676f 100644 --- a/setup.py +++ b/setup.py @@ -10,7 +10,7 @@ readme = os.path.join(os.path.dirname(__file__), 'README.rst') download_url = 'https://github.com/downloads/smartfile/django-sphinx-db' \ '/' + name + '-' + versrel + '.tar.gz' -long_description = file(readme).read() +long_description = open(readme).read() setup( name = name,