搞懂 Django 多条件查询:就像在菜市场“淘货”一样 别整那些书来气十足的“起初、其次、再者”,咱今天就直接聊聊 Django 里那些最搞门的多条件查询。
这就好比你去菜市场想找个新鲜的茄子,但菜市场里的“茄子”种类忒多了,有的捏起来软塌塌,有的还带着土腥味,还有几个个头特别大要么特别小。
这时候要是你非要拿着教科书教条式的话,“起初我要确认它是茄科植物,其次它务必是成熟的,最终它颜色要紫红”,那你的菜根本买不到,就连可能买到了干瘪的土豆冒充。 Django 的查询逻辑实际上就是一条龙,把一条鱼,挂着“大于等于 10 斤”、“小于 50 厘米”、“产自 2022 年”这几个条件,一条鱼是整活的。```python 脑子闲着没处使,估摸会忘了如何写 想当年我写这个功能,脑子里空荡荡一片,感觉像是在爬楼,半天也爬不上去。 try: queryset = User.objects.filter( is_active=True, agegte=18, agelte=60, create_dategte='2020-01-01' ) except ValueError as e: print(f"哎呀,参数不对:{e}") ``` 你看这段代码,是不是感觉像是在顺口溜上拼凑的?`agegte` 和 `agelte` 这种写法,能理解成“年龄大于等于 18"和“年龄小于等于 60",实际上就是一场关于数字大小关系的“比肥比瘦”的比赛。 实际上 Django 的 ORM 机制里有个“默认行为”,它是不关心你给了多少个条件,只要你给了非空值,它就能全量扫描,然后按顺序比较。
这就好比你在超市,你想找“果汁”,可是“果汁”和“水”在同一个货架上混着卖,你要是只让收银员扫一眼“水果”这一栏,那肯定还不忒对。
故此对的做法,是在数据库里把它们分门别类,要么干脆不分级,让引擎自己来“挑挑拣拣”。 ```python 为了把逻辑理顺,我干脆把条件拆开了,就像给每个条件起个名字 user_list = User.objects.filter( is_active=True, agegte=18, agelte=60, create_dategte='2020-01-01', is_registered=True ) ``` 这时候你会发现,`is_registered` 这个条件刚刚被删了,出于它重复且意义不大,反正 `is_active` 和 `age` 里已经隐含了注册状态的意思。
不过别怕,有时候为了追求极致的简洁,就连能够把条件塞进字符串里,比如 `content_type='User'` 和 `content_type='Log'`,只要把它们都写在 `filter()` 里面,Django 就能识别出哪些是显式的业务逻辑,哪些是隐式的分类,然后自动去重和排序。 ```python 这种写法实际上挺带感的,适合写点骚操作 想象一下,你是想淘“2020 年 1 月起注册且年龄在 18 到 60 岁之间的注册人” 那么直接把工夫戳和年龄范围拼成一句话,Django 的引擎就能在后台默默排序 这时候它就像个智慧的机器人,自动帮你排了队,让你不用跟它念啥“其次”“最终” 直接看结局就行 queryset = User.objects.filter( content_type='User', content_type='Log', create_dategte='2020-01-01', is_registered=True, is_active=True ) ``` 再讲个实战例子。你有个产品列表,你想找那些“销量大于 0 且价格大于 100 元,且发布日期早于 2023-06-01"的商品。
这时候要是写成三个分开的 `AND` 逻辑,在数据库里就是三个小门,你得一个个打。 ```python 为了效率,我把条件打包成一个大的查询对象 相当于在菜市场,我把所有要买的规格一次性列出来,然后让店长一次性全扫一遍 user_products = User.objects.filter( productquantitygt=0, productpricegt=100, productcreated_atlt='2023-06-01' ) 注意:这里 product 是个多对多关系,故此要加 ``` 这时候你可能会认定有点绕,出于 `product` 涉及到外键关联,这时候 DQL(数据库查询语言)里的 `` 符号就像是“跨店链接”的牌子。
要是不用这个符号,你就得先查那些商品 ID,再查那些产品,最终再查那些用户,这就好比你要找“喜爱喝可乐的人”,你得先找到“爱喝可乐的人”,再找到“爱喝可乐的顾客”,最终找到“喜爱可乐的顾客”。 这时候你就来气了吧?实际上不需求。Django 的 `filter` 默认赞成 `` 来覆盖外键,它会自动处理这种“跨层查找”,就像它知道那是个“亲戚关系”,故此能直接跳过中间层,直接带你去目标层。 ```python 有时候为了优雅,就连能够用条件覆盖外键 user_products = User.objects.filter( content_type='User', content_type='Log', dategte='2020-01-01', is_registered=True, is_active=True, create_dategte='2020-01-01' ) ``` 这种写法就忒顺了,简直像魔术一样。
注意看,`create_date` 和 `date` 都是指与此同工夫的一个字段,Django 自动把它们合并成了同一个逻辑:只要工夫戳在这个范围内,就把它们都算作知足。
这在处理大范围的工夫范围查询时特别好用,特别是对“那会儿三年”这种跨度大的数据。 ```python 为了应对各种复杂的业务场景,有时候需求把多个查询结局合并起来 items = User.objects.filter( category='Electronics', is_registered=False, productquantitygt=5 ).values_list('product', flat=True) ``` 这时候你会看到 `values_list` 这个函数,它能帮你把一堆乱七八糟的数据抽出来,变成一行行干净利落的列表。
比如上面那个 `filter`,它实际上是在筛选出“电子产品”、“没注册”、“数量大于 5"的商品,然后把这些商品 ID 全体取出来。
这就像是一个信息取器,把菜市场里所有你想找的“特价水果”全体打包好,你不需求一个个去核对,直接拿回就行。 ```python 在获取到结局后,往往需求进一步处理,比如排序要么分页 这时候要记得把结局放在一个列表里,别直接用 generator 就行 想象一下,你是个摄影师,要把拍好的照片实时发给微信,你得先把它们存进相册 想象一下,你是个采购经理,要把找到的商品发给客服,你得先把单子打印出来 items = User.objects.filter(category='Electronics').values_list('product', flat=True) 要是你想要排序,直接加个 sort 参数 sorted_items = User.objects.filter(category='Electronics', productquantitygt=5) .order_by('-productprice') .values_list('product', flat=True) 要是你想做分页,用 offset 和 limit 是最直接的,别搞啥 queryset.select_related 想象一下,你是去超市买东西,你把货架往后挪一格,再挪一格,这就是 offset 而 limit 就像是你想拿走的数量,不能一次性拿走所有,得根据需求调整 这种写法别看有点绕,但能处理大数据量,避免整个数据库加载到内存里,越闹腾效率越高 offset_items = User.objects.filter(category='Electronics', productquantitygt=5) .order_by('-productprice') .offset(100) .limit(20) .values_list('product', flat=True) ``` 最终还得提个醒,Django 的查询引擎默认是不排序的,要不就你要。`order_by` 和 `values_list` 里面的字段,要是没指定顺序,它默认就是按 ID 排序的。
故此有时候你会认定读出来的结局顺序不对,那是正常的,它默认是按 ID 排的。
要是你非要按价格从高到低,就得加 `order_by('-productprice')`。 ```python 为了效率,有时候连查询对象本身都能够懒加载,不用先查出来再查 想象一下,你是去图书馆找书,你不想把书都搬回家,只想拿个索引 直接让数据库自己去索引,当你要找的时候,再去查一遍,这样既快又省空间 search_result = User.objects.filter( productquantitygt=0, productpricegt=100, create_dategte='2020-01-01', is_registered=True, is_active=True ) ``` 这时候要是你还要排序,直接加在 `filter` 后面就行,不需求额外查。 ```python 为了美观和规范性,有时候能够加上某库的别名,让代码读起来更清爽 就像你在写小说,给角色起个化名,读者一眼就能认出来 user_list = User.objects.filter( is_active=True, agegte=18, agelte=60, create_dategte='2020-01-01' ).select_related('profile') ``` 最终一定要记住,`select_related` 和 `prefetch_related` 的区别。`select_related` 是查关联表,比如外键,它能把那些表的数据直接拉进来,但不会把外键的数据也拉过来,那是省事儿。`prefetch_related` 是把关联表数据先全体查出来存进内存,然后再去访问它们,内存大了就慢,故此它的性能一般更好。 ```python 为了应对超大数据量,有时候_prefetch_ 比 select_related_ 性能更好 想象一下,你是去超市买东西,你不想把货架往家里搬(select_related), 那不如先把货架里的货全体打包运回家(prefetch_related),然后你再拆开看 这样别看内存消耗大点,但能避免每次都去数据库里查外键,速度更快 user_list = User.objects.filter( category='Electronics', is_registered=False ).prefetch_related('products') ``` 好了,关于 Django 多条件查询的这些“江湖秘籍”都讲完了。从写法到逻辑,从外键关联到性能优化,就像是在菜市场淘菜一样,只要心里有数,手快有,手慢无。别再死记硬背那些教科书上的“第一步第一步第二步”,把这些逻辑变成你的肌肉记忆,你的数据库查询才能行云流水。


相关标签: