Field Operations¶
Field operations extend dynamic queries with comparison, membership, and pattern-matching operators. Add a suffix to any field name in a method signature to change the generated SQL condition.
Supported operations¶
| Operation | Suffix | SQL | Example Method |
|---|---|---|---|
| Equals | (default) | field = ? |
find_by_name |
| Greater than | _gt |
field > ? |
find_by_age_gt |
| Greater or equal | _gte |
field >= ? |
find_by_age_gte |
| Less than | _lt |
field < ? |
find_by_age_lt |
| Less or equal | _lte |
field <= ? |
find_by_age_lte |
| Not equals | _ne |
field != ? |
find_by_status_ne |
| In | _in |
field IN (?) |
find_by_status_in |
| Not in | _not_in |
field NOT IN (?) |
find_by_category_not_in |
| Like | _like |
field LIKE ? |
find_by_name_like |
| Not like | _not_like |
field NOT LIKE ? |
find_by_name_not_like |
| Between | _between |
field BETWEEN ? AND ? |
find_all_by_age_between |
| Is null | _is_null |
field IS NULL |
find_all_by_email_is_null |
| Is not null | _is_not_null |
field IS NOT NULL |
find_all_by_email_is_not_null |
| Starts with | _starts_with |
field LIKE 'val%' |
find_by_name_starts_with |
| Ends with | _ends_with |
field LIKE '%val' |
find_by_name_ends_with |
| Contains | _contains |
field LIKE '%val%' |
find_by_name_contains |
Comparison operations¶
class UserRepository(CrudRepository[int, User]):
# Greater than
def find_by_age_gt(self, age: int) -> Optional[User]: ...
def find_all_by_age_gt(self, age: int) -> List[User]: ...
# Greater or equal
def find_all_by_age_gte(self, age: int) -> List[User]: ...
# Less than
def find_by_age_lt(self, age: int) -> Optional[User]: ...
# Less or equal
def find_by_age_lte(self, age: int) -> Optional[User]: ...
# Not equals
def find_by_status_ne(self, status: str) -> Optional[User]: ...
Usage:
adults = user_repo.find_all_by_age_gte(age=18)
young_user = user_repo.find_by_age_lt(age=25)
non_active = user_repo.find_by_status_ne(status="active")
Membership operations¶
IN¶
The parameter type should be List:
class UserRepository(CrudRepository[int, User]):
def find_all_by_status_in(self, status: List[str]) -> List[User]: ...
def find_all_by_id_in(self, id: List[int]) -> List[User]: ...
Usage:
users = user_repo.find_all_by_status_in(status=["active", "pending"])
users = user_repo.find_all_by_id_in(id=[1, 2, 3, 5])
NOT IN¶
class UserRepository(CrudRepository[int, User]):
def find_all_by_category_not_in(self, category: List[str]) -> List[User]: ...
Usage:
Pattern matching¶
LIKE¶
class UserRepository(CrudRepository[int, User]):
def find_by_name_like(self, name: str) -> Optional[User]: ...
def find_all_by_name_like(self, name: str) -> List[User]: ...
Usage:
johns = user_repo.find_all_by_name_like(name="%John%")
gmail_users = user_repo.find_all_by_email_like(email="%@gmail.com")
Use SQL wildcard characters (% for any sequence, _ for single character) in the parameter value.
NOT LIKE¶
class UserRepository(CrudRepository[int, User]):
def find_all_by_name_not_like(self, name: str) -> List[User]: ...
Usage:
STARTS WITH¶
Automatically appends % to the parameter value:
class UserRepository(CrudRepository[int, User]):
def find_all_by_name_starts_with(self, name: str) -> List[User]: ...
Usage:
ENDS WITH¶
Automatically prepends % to the parameter value:
class UserRepository(CrudRepository[int, User]):
def find_all_by_email_ends_with(self, email: str) -> List[User]: ...
Usage:
gmail_users = user_repo.find_all_by_email_ends_with(email="@gmail.com")
# → WHERE email LIKE '%@gmail.com'
CONTAINS¶
Automatically wraps the parameter value with %:
class UserRepository(CrudRepository[int, User]):
def find_all_by_name_contains(self, name: str) -> List[User]: ...
Usage:
Tip
_starts_with, _ends_with, and _contains handle the % wildcards for you — unlike _like, you pass plain strings without wildcards.
Range operations¶
BETWEEN¶
Requires two parameters — min_{field} and max_{field}:
class UserRepository(CrudRepository[int, User]):
def find_all_by_age_between(self, min_age: int, max_age: int) -> List[User]: ...
Usage:
The parameter names must follow the min_ / max_ prefix convention matching the field name.
Null checks¶
IS NULL¶
No parameter required — the field is checked for NULL:
class UserRepository(CrudRepository[int, User]):
def find_all_by_email_is_null(self) -> List[User]: ...
Usage:
IS NOT NULL¶
class UserRepository(CrudRepository[int, User]):
def find_all_by_email_is_not_null(self) -> List[User]: ...
Usage:
Note
_is_null and _is_not_null operations do not take any parameters — the method signature should not include the corresponding field as a parameter.
Combining operations¶
Field operations can be combined with and / or connectors:
class UserRepository(CrudRepository[int, User]):
# AND combination
def find_by_age_gt_and_status_in(
self, age: int, status: List[str]
) -> Optional[User]: ...
# OR combination
def find_by_salary_gte_or_category_in(
self, salary: float, category: List[str]
) -> Optional[User]: ...
# Multiple comparisons
def find_all_by_age_gte_and_age_lte(
self, age: int, age: int
) -> List[User]: ...
Usage:
Summary¶
- Add suffixes (
_gt,_in,_like,_between,_is_null, etc.) to field names in method signatures - PySpring parses the suffix and generates the correct SQL operator
- Combine with
_and_/_or_for multi-condition queries - Use
Listparameter types for_inand_not_inoperations - Use SQL wildcards with
_likeand_not_like - Use
_starts_with,_ends_with,_containsfor automatic wildcard handling - Use
min_/max_prefixed parameters for_betweenoperations _is_nulland_is_not_nullrequire no parameters