【问题标题】:Django filter with replaceDjango过滤器替换
【发布时间】:2021-07-03 20:37:30
【问题描述】:

假设我有以下模型,它有一个方法 variables():

class Example(models.Model):
    text = models.CharField(max_length=255)

    def variants(self):
        return Example.objects.filter(text=remove('xy', self.text))

这个想法是在从文本中删除某些字符后获取文本相同的所有对象。例如,如果 self.text 是 'axxyy',它应该与具有文本 'a' 的对象匹配。函数 remove() 不涉及数据库,它返回一个已删除给定字符的新字符串。这很好用。

但是,我需要在比较的两边都执行相同的操作,以便 variables() 的行为如下:

    def variants(self):
        return Example.objects.filter(remove('xy', text)=remove('xy', self.text))

在这种情况下,如果 self.txt 是 'axxyy',它应该与 'a'、'ax、'axx'、'xayy' 等匹配,但不应与 'aa' 匹配,例如,因为 ' a' != 'aa' 删除后。再一次,我不想从数据库中删除“xy”,只是为了比较。

我可以用 Python 做到这一点,但我想知道是否有办法在数据库级别做到这一点?我一直在阅读有关 Func() 表达式的文档,例如 Replace,但尚未找到解决方案。

【问题讨论】:

    标签: django postgresql filtering


    【解决方案1】:

    我正在回答我自己的问题,因为我能够在 Tim Nyborg 的回答的帮助下完成这项工作。我不确定这是否是最漂亮和最有效的方法,但它似乎对我正在处理的数据量非常快,并且它可以很好地扩展到越来越多的替换模式。此解决方案仅适用于 Postgres,因为它使用 'regexp_replace' 进行替换。

    这里是示例实现:

    def variants(self):
        patterns = [
            ('[xyz️w]', ''),
            ('[\u00A0]', ' '),
            # etc.
        ]
    
        # Replace characters in self.text
        text_i = self.text
        for old, new in patterns:
            text_i = re.sub(old, new, text_i)
    
        # Replace the same characters in all other objects
        queryset = Example.objects.all()
        for i, value in enumerate(patterns):
            old, new = value
            queryset = queryset.annotate(
                **{f'text_{i}': Func(F(f'text_{i - 1}' if i > 0 else 'text'),
                                     Value(old), Value(new), Value('g'),
                                     function='regexp_replace')})
        else:
            # Filter all where replaced texts are the same
            queryset = queryset.filter(**{f'text_{i}': text_i}).exclude(pk=self.pk)
    
        return queryset
    

    正则表达式本身解决了大部分问题,因为它可以一次替换多个字符,但我的模式相当复杂,所以我将它们拆分为列表并使用动态构建的字段名称进行注释。

    话虽如此,我还不是 100%,如果我真的继续使用这个解决方案,或者更确切地说向模型添加一个字段,该字段存储每个对象的“text_i”值(已经完成替换)然后就做像这样:

    def variants(self):
        return Example.objects.filter(text_i=self.text_i).exclude(pk=self.pk)
    

    我只需要考虑“tex​​t_i”对于我的目的是否足够稳定,这样我就不必在初始创建后不断更新它们。

    【讨论】:

      【解决方案2】:

      使用 django 的 Replace 函数进行注释,然后对该注释进行过滤。

      from django.db.models.functions import Replace
      from django.db.models import Value
      ...
      
          def variants(self):
              return Example.objects.annotate(
                  removed_x=Replace('text', Value('x'), Value('')),
                  removed_xy=Replace('removed_x', Value('y'), Value('')),
              ).filter(
                  removed_xy=self.text.replace('x' , '').replace('y', '')
              )
      

      请注意,替换参数 Value('') 是可选的,因为这实际上是 Replace() 的默认值,但在示例中更明确。

      随着字符数量的增长,这不能很好地扩展,但如果您的数据库支持(Postgres),使用正则表达式可能会有更好的解决方案

      【讨论】:

      • 这确实有效,如果您只有一两个字符串要替换,这是一个很好的解决方案。我必须删除至少五个字符,所以它变得有点复杂。我一直在寻找像 Replace('text', Value('xyzw'), Value('')) 之类的东西,以使其更简单。无论如何,即使这个确切的解决方案对我来说不是最佳解决方案,它也可能是其他类似用例的好解决方案,您的回答也让我知道如何继续。我正在使用 Postgres,所以正则表达式可能是要走的路。我会接受这个答案,谢谢你的帮助!
      猜你喜欢
      • 2014-12-02
      • 1970-01-01
      • 2022-01-06
      • 2018-12-26
      • 1970-01-01
      • 2019-10-16
      • 2016-07-31
      • 1970-01-01
      • 2021-02-14
      相关资源
      最近更新 更多