You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Django+PostgreSQL:如何基于纯数字手机号实现模糊查询?

解决PostgreSQL中带格式手机号的纯数字片段搜索问题

针对你遇到的手机号搜索需求,这里提供两种可行的解决方案:

方案一:新增纯数字存储字段(推荐,性能最优)

这种方法通过预存纯数字格式的手机号,让查询直接利用数据库索引,效率最高。

1. 修改模型代码

在JobSite模型中添加一个用于存储纯数字手机号的字段,并重写save方法自动生成该字段的值:

import re
from django.db import models

class JobSite(models.Model):
    # 原有的字段保持不变...
    phone_number = models.CharField(max_length=30, null=True, blank=True)
    # 新增纯数字手机号字段
    phone_digits = models.CharField(max_length=20, null=True, blank=True)

    # 重写save方法自动生成纯数字手机号
    def save(self, *args, **kwargs):
        if self.phone_number:
            self.phone_digits = re.sub("[^0-9]", "", self.phone_number)
        else:
            self.phone_digits = None
        super().save(*args, **kwargs)

    # 原有的方法保持不变...

2. 更新视图查询逻辑

在搜索视图中,先将用户输入的搜索词转换为纯数字,再用phone_digits__icontains进行匹配:

import re
from django.db.models import Q

def search_system(request, search_term=False):
    if search_term:
        # 过滤搜索词中的非数字字符
        search_term_digits = re.sub("[^0-9]", "", search_term)
        job_sites = JobSite.objects.filter(
            Q(first_name__icontains=search_term) |
            Q(last_name__icontains=search_term) |
            Q(quickbooks_id__icontains=search_term) |
            Q(email__icontains=search_term) |
            # 使用纯数字字段进行模糊匹配
            Q(phone_digits__icontains=search_term_digits)
        )
    else:
        job_sites = JobSite.objects.all().prefetch_related('customer')

    # 原有的数据组装逻辑保持不变...

注意事项

  • 执行makemigrations和migrate命令,将新增字段同步到数据库。
  • 对于已存在的历史数据,可以编写批量更新脚本,调用phone_digits_only方法生成phone_digits的值。
  • 可以给phone_digits字段添加db_index=True,进一步提升搜索性能。

方案二:实时用PostgreSQL函数处理(无需修改数据库结构)

如果不想新增字段,可以利用PostgreSQL的regexp_replace函数,在查询时实时去除手机号中的非数字字符,再进行匹配。

修改视图查询逻辑

import re
from django.db.models import Q, Func, Value
from django.db import models

def search_system(request, search_term=False):
    if search_term:
        search_term_digits = re.sub("[^0-9]", "", search_term)
        job_sites = JobSite.objects.filter(
            Q(first_name__icontains=search_term) |
            Q(last_name__icontains=search_term) |
            Q(quickbooks_id__icontains=search_term) |
            Q(email__icontains=search_term) |
            # 实时转换手机号为纯数字后匹配
            Func(
                'phone_number',
                Value(r'[^\d]'),
                Value(''),
                function='regexp_replace',
                output_field=models.CharField()
            ).__icontains(search_term_digits)
        )
    else:
        job_sites = JobSite.objects.all().prefetch_related('customer')

    # 原有的数据组装逻辑保持不变...

替代简化写法(用正则匹配)

也可以直接用PostgreSQL的正则匹配,跳过字符转换步骤:

Q(phone_number__iregex=rf'[^\d]*{re.escape(search_term_digits)}[^\d]*')

这个正则会忽略手机号中的非数字字符,直接匹配用户输入的数字片段。

两种方案对比

  • 方案一:性能好,适合数据量大、搜索频繁的场景;需要修改数据库结构,历史数据需要批量更新。
  • 方案二:无需修改数据库,实现简单;但每次查询都要实时处理字符串,数据量大时性能会下降。

内容的提问来源于stack exchange,提问作者beach_it

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.24 14:24:19