【问题标题】:Querying full name in Django在 Django 中查询全名
【发布时间】:2011-12-02 15:51:59
【问题描述】:

如何在 Django 中查询全名?

为了澄清,我基本上想创建一个临时列,结合 first_name 和 last_name 给出一个全名,然后对其进行 LIKE,如下所示:

select [fields] from Users where CONCAT(first_name, ' ', last_name) LIKE '%John Smith%";

上述查询将返回所有名为 John Smith 的用户。如果可能的话,我想避免使用原始 SQL 调用。

我所说的模型是股票 django.contrib.auth.models 用户模型。直接对模型进行更改不是问题。

例如,如果用户要搜索“John Paul Smith”,它应该匹配名字为“John Paul”和姓氏“Smith”的用户,以及名字为“John”和姓“保罗·史密斯”。

【问题讨论】:

  • 请包含您的 Django 模型。如果您想要一个“派生值”(如 SQL 中的 CONCAT(first_name, ' ', last_name)),您将不得不将其添加到模型中。因此,请在您的问题中包含模型。
  • 你知道吗,顺便说一句,CONCAT(first_name, ' ', last_name) LIKE '%John Smith%" 效率非常低?使用first_name LIKE '%John' AND last_name LIKE 'Smith%'可以更高效吗?当有非 CONCAT 方法时,为什么要使用 CONCAT?
  • 这是标准的 django.contrib.auth.models 用户模型。我们已经在此模型中添加/更改字段,因此修改不是问题。
  • "在这个模型中添加/更改字段" 你为什么不使用配置文件扩展? docs.djangoproject.com/en/1.3/topics/auth/…
  • @S.Lott 我不知道效率问题,感谢您指出这一点。对于我们的应用程序,查询用户是一个单一的字段,所以我不能只在空格上分割。

标签: python django


【解决方案1】:

这个问题是很久以前发布的,但我遇到了类似的问题,在这里找到的答案很糟糕。接受的答案只允许您通过 first_name 和 last_name 找到完全匹配。第二个答案稍微好一点,但仍然很糟糕,因为你打数据库的次数和单词一样多。 这是我的解决方案,它连接 first_name 和 last_name 对其进行注释并在此字段中搜索:

from django.db.models import Value as V
from django.db.models.functions import Concat   

users = User.objects.annotate(full_name=Concat('first_name', V(' '), 'last_name')).\
                filter(full_name__icontains=query)

例如,如果此人的名字是 John Smith,您可以通过键入 john smith、john、smith、hn smi 等来找到他。它只命中数据库。而且我认为这将是您在公开帖子中想要的确切 SQL。

【讨论】:

  • "但仍然很糟糕,因为你访问数据库的次数和访问次数一样多" -> django 查询是lazy,它只访问数据库一次。 Concat 注释会产生一个非常复杂的 sql 语句。从功能上讲,唯一的区别是您的答案允许在串联中包含(如您所述,“hn smi”fe),而我的答案将允许在任一列中包含(例如,搜索“John Smith”或“Smith John”两者都会匹配与first_name“John Fitzgerald”和last_name“Smith”排在一起)。
  • 这应该是公认的答案。其他答案没有正确回答问题。
  • 祝福你。在最初的问题 7 年后发布更好的解决方案值得尊重。
  • 这是一个非常优雅的解决方案??虽然不确定磨蚀性的相关性是什么。 ?
  • 完美,谢谢!以防万一这对其他人有帮助:我最初收到“无法设置属性错误”并发现这是因为我的用户模型上已经有一个全名@property。所以把上面的代码改成使用 the_full_name 就可以解决了。
【解决方案2】:

更简单:

from django.db.models import Q 

def find_user_by_name(query_name):
   qs = User.objects.all()
   for term in query_name.split():
     qs = qs.filter( Q(first_name__icontains = term) | Q(last_name__icontains = term))
   return qs

其中 query_name 可以是“John Smith”(但也会检索用户 Smith John,如果有的话)。

【讨论】:

  • 我已经开始使用它,只要有多个搜索词,http 响应时间就会从 100 毫秒变为 5 秒。任何想法为什么会这样?
  • 你必须先查明问题出在哪里。数据库?奥姆?模板?在sql客户端执行查询(print qs.query),看看问题是否出在db中。如果不是,通过排除(删除代码片段),尝试猜测它是否在模板中。如果没有,它可能在 ORM 上(尝试返回较少行的查询)。您将面临 ORM N+1 问题(使用 select_relatedprefetch_related 修复它)。通过使用 EXPLAIN (mysql/postgres) 和查询(来自 sql 客户端),你能猜出什么错误吗?你的桌子很大吗?如果是这样,索引 first_name 和 last_name 是否可以缓解问题?
  • 感谢您的提示
【解决方案3】:
class User( models.Model ):
    first_name = models.CharField( max_length=64 )
    last_name = models.CharField( max_length=64 )
    full_name = models.CharField( max_length=128 )
    def save( self, *args, **kw ):
        self.full_name = '{0} {1}'.format( first_name, last_name )
        super( User, self ).save( *args, **kw )

【讨论】:

    【解决方案4】:

    除非我遗漏了什么,否则您可以使用 python 来查询您的数据库,如下所示:

    from django.contrib.auth.models import User
    x = User.objects.filter(first_name='John', last_name='Smith') 
    

    编辑: 回答你的问题:

    如果您需要在用户搜索“John Smith”时返回“John Paul Smith”,那么您可以使用“contains”,它转换为 SQL LIKE。如果您只需要存储名称“John Paul”的容量,请将两个名称都放在 first_name 列中。

    User.objects.filter(first_name__contains='John', last_name__contains='Smith') 
    

    这转化为:

    SELECT * FROM USERS
    WHERE first_name LIKE'%John%' 
    AND last_name LIKE'%Smith%'
    

    【讨论】:

    • 如果查询是“John Paul Smith”会发生什么?
    • 如果 first_name == "John Paul" 或 last_name == "Paul Smith",它仍然会以这种方式匹配。
    • 我的意思是,如果用户输入 John Paul Smith 会怎样?
    • (first_name like '%John' or first_name like '%John Paul') and (last_name like 'Paul Smith%' or last_name like 'Smith%') 通过在 first_name likes 中执行 words[:-1] 和在 last_name likes 中执行 words[1:],它可以轻松扩展到 n 个单词。它避免了昂贵且不可索引的 CONCAT。
    【解决方案5】:

    我使用此查询来搜索名字、姓氏以及全名。

    它解决了我的问题。

    from django.db.models import Q, F
    from django.db.models import Value as V
    from django.db.models.functions import Concat 
    
    user_list = models.User.objects.annotate(
                            full_name=Concat('first_name', V(' '), 'last_name')
                        ).filter(   
                            Q(full_name__icontains=keyword) | 
                            Q(first_name__icontains=keyword) | 
                            Q(last_name__icontains=keyword)
                        )
    

    【讨论】:

      【解决方案6】:

      这个呢:

      query = request.GET.get('query')
      users = []
      
      try:
          firstname = query.split(' ')[0]
          lastname  = query.split(' ')[1]
          users += Users.objects.filter(firstname__icontains=firstname,lastname__icontains=lastname)
          users += Users.objects.filter(firstname__icontains=lastname,lastname__icontains=firstname)
      
      users = set(users)
      

      久经考验!

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2017-07-22
        • 1970-01-01
        • 2013-02-06
        • 2014-07-24
        • 2020-12-13
        • 2011-07-31
        相关资源
        最近更新 更多