__icontains是大小写不敏感的参数化包含匹配,非SQL LIKE直拼;__startswith可走索引实现高效前缀查询;大小写敏感场景应改用__contains或原生SQL;模糊查询易致索引失效,须用explain验证执行计划。

__icontains 是大小写不敏感的包含匹配,不是正则也不是 SQL LIKE 直接拼接
很多人以为 __icontains 就是把字符串塞进 %value% 里完事,其实 Django 会先做参数化处理、再交给数据库驱动转义。这意味着你传 "%admin%" 进去,结果查的是字面量「%admin%」,不是通配含义。
- 正确写法:
User.objects.filter(username__icontains="admin")→ 查 username 含 "admin"(不区分大小写) - 错误写法:
User.objects.filter(username__icontains="%admin%")→ 查含字面量百分号的用户名 - 注意:PostgreSQL 和 MySQL 对 Unicode 的处理略有差异,比如带重音字符时,
__icontains在 PostgreSQL 上依赖数据库 collation,MySQL 则看字段 charset 和 collation 是否支持 utf8mb4_unicode_ci
__startswith 匹配开头,但不等价于字符串切片或 Python startswith()
__startswith 是数据库层执行的前缀扫描,能用上索引(前提是字段有 B-tree 索引且没函数包裹),而 Python 层的 str.startswith() 是查完再过滤,数据一多就卡死。
- 推荐:
Product.objects.filter(name__startswith="iPhone")→ 走索引,快 - 危险:
Product.objects.all().filter(lambda p: p.name.startswith("iPhone"))→ 全表拉到内存再筛,OOM 风险 - 注意:
__startswith对 NULL 值返回 False,哪怕字段允许为空;如果要包含 NULL,得显式加| Q(name__isnull=True)
大小写敏感场景下别硬套 __icontains,该换就换
有些业务要求严格区分大小写,比如 API token、base64 编码字段、JWT payload 中的 kid,这时候用 __icontains 反而引入逻辑漏洞。
强戈环形山: q15040409-刻画家功能概述强戈环形山: q15040409-刻画家是一项面向实际任务的技能,主要用于平顶山: 维西翁: 1:1.0. 塔格斯: rap.github: 自动化。它将相关步骤、工具调用和结果整理方式集中到统一流程中,帮助使用者更快完成目标并减少重复操作。核心要点使用时应结合输入条件选择合适的执行方式,核对必要参数、依赖环境与输出内容,并按原始要求处理异常情况。该技能适合需要稳定复用相关能力的场景,可作为自动化工作流的一部分,也便于后续检查、调整和扩展。从功能定位来看,该
- 大小写敏感包含:用
__contains(注意少一个 i)→Token.objects.filter(key__contains="ABC") - 大小写敏感开头:用
__startswith本身就不敏感?错 —— 它默认仍受数据库 collation 影响;稳妥做法是换__istartswith(不敏感)或确认 DB collation 是 case-sensitive,否则直接用extra()或原生 SQL - SQLite 默认不区分大小写,但加了
BINARY修饰符后可强制区分;Django 不自动加,得自己extra(where=["name LIKE BINARY %s"], params=["iPhone%"])
性能陷阱:模糊查询容易让索引失效,尤其组合条件时
__icontains 在大多数数据库上无法使用标准 B-tree 索引的范围扫描,基本等于全表扫;__startswith 可以,但一旦前面加了 NOT、或者和 OR 混用、或者字段被函数包裹(如 F("name") + "_tmp"),索引就废了。
- 安全组合:
status="active", name__startswith="A"→ 可走联合索引(status, name) - 危险组合:
name__icontains="x" | Q(email__endswith="@example.com")→ 很可能放弃索引,改用临时表或 filesort - 验证方法:在 manage.py shell 里用
.explain()看执行计划,例如User.objects.filter(username__icontains="adm").explain()
实际用的时候,别光盯着双下划线写法顺不顺手,得看它背后是不是真走索引、字段值有没有 NULL、数据库用的是啥 collation——这些地方不动手查 explain、不看 query log,很容易上线后才发现慢得没法忍。










