44enhance SQLAlchemy query construction. It implements type-safe, reusable filter patterns
55for common database query operations.
66
7- Args:
8- None
9-
10- Returns:
11- None
12-
137Features:
148 Type-safe filter construction, datetime range filtering, collection-based filtering,
159 pagination support, search operations, and customizable ordering.
3529 - :class:`sqlalchemy.sql.expression.Select`: Core SQLAlchemy select expression
3630 - :class:`sqlalchemy.orm.Query`: SQLAlchemy ORM query interface
3731 - :mod:`advanced_alchemy.base`: Base model definitions
32+
3833"""
3934
4035from __future__ import annotations
@@ -94,24 +89,6 @@ class StatementFilter(ABC):
9489 This class defines the interface for all filter types in the system. Each filter
9590 implementation must provide a method to append its filtering logic to an existing
9691 SQLAlchemy statement.
97-
98- Args:
99- None
100-
101- Attributes:
102- None
103-
104- Example:
105- Implementing a custom filter::
106-
107- class CustomFilter(StatementFilter):
108- def append_to_statement(self, statement, model):
109- return statement.where(model.column == "value")
110-
111- See Also:
112- - :class:`.BeforeAfter`: DateTime range filtering implementation
113- - :class:`.CollectionFilter`: Collection-based filtering implementation
114- - :class:`sqlalchemy.sql.expression.Select`: SQLAlchemy select expression
11592 """
11693
11794 @abstractmethod
@@ -175,22 +152,13 @@ class BeforeAfter(StatementFilter):
175152 after : datetime | None
176153 Filter results where field is later than this value
177154
178- Example:
179- --------
180- >>> filter = BeforeAfter(
181- ... field_name="created_at",
182- ... before=datetime(2024, 1, 1),
183- ... after=datetime(2023, 1, 1),
184- ... )
185- >>> statement = filter.append_to_statement(select(Model), Model)
186-
187155 Note:
188156 -----
189- If either `before` or `after` is None, that boundary condition is not applied.
157+ If either `before` or `after` is None, that boundary condition is not applied.
190158
191159 See Also:
192- --------
193- :class:`OnBeforeAfter` : Inclusive datetime range filtering
160+ ---------
161+ :class:`OnBeforeAfter` : Inclusive datetime range filtering
194162 """
195163
196164 field_name : str
@@ -237,7 +205,7 @@ class OnBeforeAfter(StatementFilter):
237205 Filter results where field is on or later than this value
238206
239207 Example:
240- --------
208+ -------
241209 >>> filter = OnBeforeAfter(
242210 ... field_name="updated_at",
243211 ... on_or_before=datetime(2024, 1, 1),
@@ -246,7 +214,7 @@ class OnBeforeAfter(StatementFilter):
246214 >>> statement = filter.append_to_statement(select(Model), Model)
247215
248216 Note:
249- -----
217+ ----
250218 If either `on_or_before` or `on_or_after` is None, that boundary condition
251219 is not applied.
252220
@@ -286,11 +254,10 @@ def append_to_statement(self, statement: StatementTypeT, model: type[ModelT]) ->
286254
287255
288256class InAnyFilter (StatementFilter , ABC ):
289- """Abstract base class for statement filters that support the `prefer_any` flag .
257+ """Base class for filters using IN or ANY operators .
290258
291- This class serves as a foundation for filters utilizing the ``prefer_any``
292- parameter to select between different comparisons (e.g., using SQLAlchemy's
293- any_ operator vs. in_ operator).
259+ This abstract class provides common functionality for filters that check
260+ membership in a collection using either the SQL IN operator or the ANY operator.
294261 """
295262
296263
@@ -300,28 +267,23 @@ class CollectionFilter(InAnyFilter, Generic[T]):
300267
301268 This filter restricts records based on a field's presence in a collection of values.
302269
303- Parameters
304- ----------
305- field_name : str
306- Name of the model attribute to filter on
307- values : abc.Collection[T] | None
308- Values for the ``IN`` clause. If this is None, no filter is applied.
309- An empty list will force an empty result set (WHERE 1=-1).
270+ The filter supports both ``IN`` and ``ANY`` operators for collection membership testing.
271+ Use ``prefer_any=True`` in ``append_to_statement`` to use the ``ANY`` operator.
310272 """
311273
312274 field_name : str
313275 """Name of the model attribute to filter on."""
314276 values : abc .Collection [T ] | None
315- """Values for the IN clause. If None, this filter is not applied.
316- If an empty list, an empty result set is returned. """
277+ """Values for the ``IN`` clause. If this is None, no filter is applied.
278+ An empty list will force an empty result set (WHERE 1=-1) """
317279
318280 def append_to_statement (
319281 self ,
320282 statement : StatementTypeT ,
321283 model : type [ModelT ],
322284 prefer_any : bool = False ,
323285 ) -> StatementTypeT :
324- """Apply a WHERE ... IN or WHERE ... ANY(...) clause to the statement.
286+ """Apply a WHERE ... IN or WHERE ... ANY (...) clause to the statement.
325287
326288 Parameters
327289 ----------
@@ -330,8 +292,8 @@ def append_to_statement(
330292 model : type[ModelT]
331293 The SQLAlchemy model class
332294 prefer_any : bool, optional
333- If True, uses the SQLAlchemy any_ operator instead of in_
334- for the filter condition
295+ If True, uses the SQLAlchemy :func:` any_` operator instead of
296+ :func:`in_` for the filter condition
335297
336298 Returns:
337299 --------
@@ -355,19 +317,23 @@ class NotInCollectionFilter(InAnyFilter, Generic[T]):
355317
356318 This filter restricts records based on a field's absence in a collection of values.
357319
320+ The filter supports both ``NOT IN`` and ``!= ANY`` operators for collection exclusion.
321+ Use ``prefer_any=True`` in ``append_to_statement`` to use the ``ANY`` operator.
322+
358323 Parameters
359324 ----------
360325 field_name : str
361326 Name of the model attribute to filter on
362327 values : abc.Collection[T] | None
363328 Values for the ``NOT IN`` clause. If this is None or empty,
364329 the filter is not applied.
330+
365331 """
366332
367333 field_name : str
368334 """Name of the model attribute to filter on."""
369335 values : abc .Collection [T ] | None
370- """Values for the NOT IN clause. If None or empty, no filter is applied."""
336+ """Values for the `` NOT IN`` clause. If None or empty, no filter is applied."""
371337
372338 def append_to_statement (
373339 self ,
@@ -384,8 +350,8 @@ def append_to_statement(
384350 model : type[ModelT]
385351 The SQLAlchemy model class
386352 prefer_any : bool, optional
387- If True, uses the SQLAlchemy any_ operator instead of notin_
388- for the filter condition
353+ If True, uses the SQLAlchemy :func:` any_` operator instead of
354+ :func:`notin_` for the filter condition
389355
390356 Returns:
391357 --------
@@ -416,20 +382,6 @@ class LimitOffset(PaginationFilter):
416382 Implements traditional pagination using SQL LIMIT and OFFSET clauses.
417383 Only applies to SELECT statements; other statement types are returned unmodified.
418384
419- Args:
420- limit: Maximum number of rows to return
421- offset: Number of rows to skip before returning results
422-
423- Attributes:
424- limit: Maximum number of rows to return
425- offset: Number of rows to skip before returning results
426-
427- Example:
428- Basic pagination usage::
429-
430- filter = LimitOffset(limit=10, offset=20)
431- statement = filter.append_to_statement(select(Model), Model)
432-
433385 Note:
434386 This filter only modifies SELECT statements. For other statement types
435387 (UPDATE, DELETE), the statement is returned unchanged.
@@ -440,7 +392,9 @@ class LimitOffset(PaginationFilter):
440392 """
441393
442394 limit : int
395+ """Maximum number of rows to return."""
443396 offset : int
397+ """Number of rows to skip before returning results."""
444398
445399 def append_to_statement (self , statement : StatementTypeT , model : type [ModelT ]) -> StatementTypeT :
446400 """Apply LIMIT/OFFSET pagination to the statement.
@@ -470,25 +424,6 @@ class OrderBy(StatementFilter):
470424 Appends an ORDER BY clause to SELECT statements, sorting records by the
471425 specified field in ascending or descending order.
472426
473- Args:
474- field_name: Name of the model attribute to sort on
475- sort_order: Sort direction ("asc" or "desc"), defaults to "asc"
476-
477- Attributes:
478- field_name: Name of the model attribute to sort on
479- sort_order: Sort direction ("asc" or "desc")
480-
481- Example:
482- Basic sorting usage::
483-
484- # Ascending order (default)
485- filter = OrderBy(field_name="created_at")
486- statement = filter.append_to_statement(select(Model), Model)
487-
488- # Descending order
489- filter = OrderBy(field_name="priority", sort_order="desc")
490- statement = filter.append_to_statement(select(Model), Model)
491-
492427 Note:
493428 This filter only modifies SELECT statements. For other statement types,
494429 the statement is returned unchanged.
@@ -500,7 +435,9 @@ class OrderBy(StatementFilter):
500435 """
501436
502437 field_name : str
438+ """Name of the model attribute to sort on."""
503439 sort_order : Literal ["asc" , "desc" ] = "asc"
440+ """Sort direction ("asc" or "desc")."""
504441
505442 def append_to_statement (self , statement : StatementTypeT , model : type [ModelT ]) -> StatementTypeT :
506443 """Append an ORDER BY clause to the statement.
@@ -533,33 +470,6 @@ class SearchFilter(StatementFilter):
533470 Implements text search using SQL LIKE or ILIKE operators. Can search across
534471 multiple fields using OR conditions.
535472
536- Args:
537- field_name: Name or set of names of model attributes to search on
538- value: Text to match within the field(s)
539- ignore_case: If True, uses ILIKE for case-insensitive matching
540-
541- Attributes:
542- field_name: Name or set of names of model attributes to search on
543- value: Text to match within the field(s)
544- ignore_case: Whether to use case-insensitive matching
545-
546- Example:
547- Single field search::
548-
549- filter = SearchFilter(
550- field_name="name", value="john", ignore_case=True
551- )
552- statement = filter.append_to_statement(select(Model), Model)
553-
554- Multi-field search::
555-
556- filter = SearchFilter(
557- field_name={"name", "email"},
558- value="example",
559- ignore_case=True,
560- )
561- statement = filter.append_to_statement(select(Model), Model)
562-
563473 Note:
564474 The search pattern automatically adds wildcards before and after the search
565475 value, equivalent to SQL pattern '%value%'.
@@ -571,8 +481,11 @@ class SearchFilter(StatementFilter):
571481 """
572482
573483 field_name : str | set [str ]
484+ """Name or set of names of model attributes to search on."""
574485 value : str
486+ """Text to match within the field(s)."""
575487 ignore_case : bool | None = False
488+ """Whether to use case-insensitive matching."""
576489
577490 @property
578491 def _operator (self ) -> Callable [..., ColumnElement [bool ]]:
@@ -656,23 +569,6 @@ class NotInSearchFilter(SearchFilter):
656569 value: Text to exclude from the field(s)
657570 ignore_case: If True, uses NOT ILIKE for case-insensitive matching
658571
659- Example:
660- Single field exclusion::
661-
662- filter = NotInSearchFilter(
663- field_name="name", value="test", ignore_case=True
664- )
665- statement = filter.append_to_statement(select(Model), Model)
666-
667- Multi-field exclusion::
668-
669- filter = NotInSearchFilter(
670- field_name={"name", "description"},
671- value="temp",
672- ignore_case=True,
673- )
674- statement = filter.append_to_statement(select(Model), Model)
675-
676572 Note:
677573 Uses AND for multiple fields, meaning records matching any field will be excluded.
678574
0 commit comments