Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CHANGES.rst
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ Unreleased
- Add support for Python 3.13 and 3.14
- Add support for PostgreSQL 17 and 18
- Use f-strings instead of percent format
- Fix mutable default argument antipattern in ``SearchManager.__init__()`` and
``make_searchable()`` that could cause shared state bugs

2.1.0 (2024-02-19)
^^^^^^^^^^^^^^^^^^
Expand Down
54 changes: 37 additions & 17 deletions sqlalchemy_searchable/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -88,15 +88,19 @@ def __init__(self, tsvector_column, indexed_columns=None, options=None):
self.indexed_columns = None

def init_options(self, options=None):
if not options:
options = {}
for key, value in SearchManager.default_options.items():
try:
option = self.tsvector_column.type.options[key]
except (KeyError, AttributeError):
option = value
options.setdefault(key, option)
return options
# Priority order (highest to lowest):
# 1. Column-specific options from tsvector_column.type.options
# 2. Passed options parameter
# 3. SearchManager.default_options
result = SearchManager.default_options.copy()
if options:
result.update(options)
# Column-specific options override everything
if hasattr(self.tsvector_column.type, "options"):
for key in result:
if key in self.tsvector_column.type.options:
result[key] = self.tsvector_column.type.options[key]
return result

@property
def table_name(self):
Expand Down Expand Up @@ -212,9 +216,10 @@ class SearchManager:
"auto_index": True,
}

def __init__(self, options={}):
self.options = self.default_options
self.options.update(options)
def __init__(self, options=None):
self.options = self.default_options.copy()
if options:
self.options.update(options)
self.processed_columns = []
self.listeners = []

Expand Down Expand Up @@ -274,13 +279,25 @@ def attach_ddl_listeners(self):
column
):
self.add_listener(
(table, "after_create", CreateSearchFunctionSQL(column))
(
table,
"after_create",
CreateSearchFunctionSQL(column, options=self.options),
)
)
self.add_listener(
(table, "after_drop", DropSearchFunctionSQL(column))
(
table,
"after_drop",
DropSearchFunctionSQL(column, options=self.options),
)
)
self.add_listener(
(table, "after_create", CreateSearchTriggerSQL(column))
(
table,
"after_create",
CreateSearchTriggerSQL(column, options=self.options),
)
)


Expand Down Expand Up @@ -478,14 +495,17 @@ def downgrade():
sql_expressions = DDL(file.read())


def make_searchable(metadata, mapper=sa.orm.Mapper, manager=search_manager, options={}):
def make_searchable(
metadata, mapper=sa.orm.Mapper, manager=search_manager, options=None
):
"""
Configure SQLAlchemy-Searchable for given SQLAlchemy metadata object.

:param metadata: SQLAlchemy metadata object
:param options: Dictionary of configuration options
"""
manager.options.update(options)
if options:
manager.options.update(options)
event.listen(mapper, "instrument_class", manager.process_mapper)
event.listen(mapper, "after_configured", manager.attach_ddl_listeners)
event.listen(metadata, "before_create", sql_expressions)
Expand Down