from django.db.backends.signals import connection_created from django.db.models import CharField, Transform from django.dispatch import receiver from rest_framework.filters import SearchFilter SQLITE_CASEFOLD_FUNCTION = "EUSRR_CASEFOLD" def _casefold(value): return None if value is None else str(value).casefold() @receiver(connection_created, dispatch_uid="commerce.sqlite_unicode_casefold") def register_sqlite_casefold(sender, connection, **kwargs): if connection.vendor != "sqlite": return connection.connection.create_function( SQLITE_CASEFOLD_FUNCTION, 1, _casefold, deterministic=True, ) class UnicodeCasefold(Transform): lookup_name = "unicode_casefold" function = "LOWER" output_field = CharField() def as_sqlite(self, compiler, connection, **extra_context): return super().as_sql( compiler, connection, function=SQLITE_CASEFOLD_FUNCTION, **extra_context, ) CharField.register_lookup(UnicodeCasefold) class UnicodeCasefoldSearchFilter(SearchFilter): """DRF substring search with Unicode-aware case normalization.""" def get_search_terms(self, request): return [term.casefold() for term in super().get_search_terms(request)]