【问题标题】:django query based on dynamic property()基于动态属性的django查询()
【发布时间】:2011-06-12 20:40:51
【问题描述】:

我想知道是否有一种方法可以使用property() 动态生成的python 属性在查询集上使用Django 的filter()。我有每个用户的first_namelast_name,我想根据他们的串联名称first_name last_name 进行过滤。 (这背后的原因是,当我执行自动完成时,我会搜索查询是否匹配名字、姓氏或连接的一部分。例如,我希望 John S 匹配 John Smith

我创建了name的属性:

def _get_name(self):
    return self.first_name + " " + self.last_name
    name = property(_get_name)

这样我可以调用user.name 来获取串联名称。

但是,如果我尝试执行 User.objects.filter(name__istartswith=query),则会收到错误 Cannot resolve keyword 'name' into field.

关于如何做到这一点的任何想法?我是否必须在数据库中创建另一个字段来存储全名?

【问题讨论】:

    标签: python django django-queryset


    【解决方案1】:

    我遇到了类似的问题,正在寻找解决方案。理所当然地认为搜索引擎将是最佳选择(例如 django-haystackElasticsearch),这就是我将如何使用 Django ORM 实现一些代码以满足您的需求(您可以将 icontains 替换为 istartswith) :

    from django.db.models import Value
    from django.db.models.functions import Concat
    
    queryset = User.objects.annotate(full_name=Concat('first_name', Value(' '), 'last_name')
    return queryset.filter(full_name__icontains=value)
    

    就我而言,我不知道用户是否会插入“first_namelast_name”或反之亦然,所以我使用了以下代码。

    from django.db.models import Q, Value
    from django.db.models.functions import Concat
    
    queryset = User.objects.annotate(first_last=Concat('first_name', Value(' '), 'last_name'), last_first=Concat('last_name', Value(' '), 'first_name'))
    return queryset.filter(Q(first_last__icontains=value) | Q(last_first__icontains=value))
    

    使用 Django CONCAT 函数来使用 extra,如下所示:

    queryset.extra(where=['UPPER(CONCAT("auth_user"."last_name", \' \', "auth_user"."first_name")) LIKE UPPER(%s) OR UPPER(CONCAT("auth_user"."first_name", \' \', "auth_user"."last_name")) LIKE UPPER(%s)'], params=['%'+value+'%', '%'+value+'%'])
    

    【讨论】:

      【解决方案2】:

      接受的答案并不完全正确。

      在许多情况下,您可以将模型管理器中的 get() 覆盖为关键字参数中的 pop 动态属性,然后将要查询的实际属性添加到 kwargs 关键字参数字典中。请务必返回 super,这样任何常规的 get() 调用都会返回预期结果。

      我只是粘贴我自己的解决方案,但对于__startswith 和其他条件查询,您可以在split 双下划线中添加一些逻辑并适当处理。

      这是我允许通过动态属性查询的解决方法:

      class BorrowerManager(models.Manager):
          def get(self, *args, **kwargs):
              full_name = kwargs.pop('full_name', None)
              # Override #1) Query by dynamic property 'full_name'
              if full_name:
                  names = full_name_to_dict(full_name)
                  kwargs = dict(kwargs.items() + names.items())
              return super(BorrowerManager, self).get(*args, **kwargs)
      

      在models.py中:

      class Borrower(models.Model):
          objects = BorrowerManager()
      
          first_name = models.CharField(null=False, max_length=30)
          middle_name = models.CharField(null=True, max_length=30)
          last_name = models.CharField(null=False, max_length=30)
          created = models.DateField(auto_now_add=True)
      

      在 utils.py 中(为了上下文):

      def full_name_to_dict(full_name):
          ret = dict()
          values = full_name.split(' ')
          if len(values) == 1:
              raise NotImplementedError("Not enough names to unpack from full_name")
          elif len(values) == 2:
              ret['first_name'] = values[0]
              ret['middle_name'] = None
              ret['last_name'] = values[1]
              return ret
          elif len(values) >= 3:
              ret['first_name'] = values[0]
              ret['middle_name'] = values[1:len(values)-1]
              ret['last_name'] = values[len(values)-1]
              return ret
          raise NotImplementedError("Error unpacking full_name to first, middle, last names")
      

      【讨论】:

      • 好!顺便说一句,您可以使用 isinstance(getattr( self.model, attr ), property) 之类的东西来检查参数是否是属性。我正在以类似的方式做事..
      • kwargs = dict(kwargs.items() + names.items())可以写成kwargs.update(names)
      【解决方案3】:

      filter() 在数据库级别上运行(它实际上是编写 SQL),因此无法将它用于基于您的 python 代码(dynamic property in your question) 的任何查询。

      这是本部门许多其他答案的答案:)

      【讨论】:

      • 有趣,不,你不能过滤属性,但你可以对现有字段进行分组并按组过滤answer below
      【解决方案4】:

      认为在 django 中不可能过滤不作为数据库文件显示的属性,但是你可以做一些类似这样的自动完成搜索:

      if ' ' in query:
          query = query.split()
          search_results = list(chain(User.objects.filter(first_name__icontains=query[0],last_name__icontains=query[1]),
                                          User.objects.filter(first_name__icontains=query[1],last_name__icontains=query[0])))
      else:
          search_results =  User.objects.filter(Q(first_name__icontains=query)| Q(last_name__icontains=query))
      

      此代码使您的系统用户可以灵活地开始输入名字或姓氏,用户将感谢您允许这样做。

      【讨论】:

      • 考虑过,但是有一些问题。有些人的名字或姓氏中可能有空格,因此按空格分隔可能会被破坏。但是,否则,我已经让它搜索名字或姓氏。不过,我设置了一个信号来在创建时填充名称字段,所以它现在可以工作了。谢谢。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2017-07-30
      • 1970-01-01
      • 2017-10-20
      • 1970-01-01
      • 1970-01-01
      • 2019-04-30
      • 2016-10-31
      相关资源
      最近更新 更多