【问题标题】:Django query WHERE 1 IN (column1, column2)Django 查询 WHERE 1 IN (column1, column2)
【发布时间】:2016-01-13 19:08:39
【问题描述】:

在 PostgreSQL 中,此查询有效并产生正确的结果

SELECT ID FROM table WHERE 1 in (column1, column2);

结果为@​​987654322@ 给出下表

+----+---------+---------+
| ID | column1 | column2 |
+----+---------+---------+
| 0  |    1    |    0    |
+----+---------+---------+
| 1  |    2    |    3    |
+----+---------+---------+
| 2  |    2    |    1    |
+----+---------+---------+
| 3  |    0    |    4    |
+----+---------+---------+ 

如何在 Django ORM 中建模?

我考虑过自定义查找,但我需要 '1__myin' = [column1, column2] 生成 Cannot resolve keyword '1' into field. 我想避免使用 RAW SQL,因为我不想在我的代码中错误地放置一些漏洞。

在同一行,所有的column_x都有不同的值

【问题讨论】:

    标签: django postgresql django-models django-queryset


    【解决方案1】:

    我最终实现了一个自定义过滤器来“反向输入”,这样您就可以查询列而不是值。

    过滤器是这样工作的

    col_name_1__revin=[value, 'col_name_2', 'col_name_3']

    它会生成

    value IN ("table_name"."col_name_1", "table_name"."col_name_2", "table_name"."col_name_3")

    仅在 IntegerField 上测试过,但也应该适用于其他字段,只要您提供正确的值类型。

    from django.db.models import lookups
    import re
    
    class ReverseIn(lookups.In):
        lookup_name = 'revin'
    
        def get_prep_lookup(self):
            # We will always call this on a ChampionIntegerField with lookup_name = revin
            if not hasattr(self.rhs, '__iter__'):
                raise ValueError('ReverseIn only works with iterables. Got {} of type {}.'.format(self.rhs),type(self.rhs))
            self.table_name = self.lhs.field.model._meta.db_table
            self.output_field = self.lhs.output_field
            rhs = list(self.rhs)
            lhs = self.lhs.field.column
            try:
                self.lhs = self.lhs.output_field.get_prep_lookup("exact", rhs[0])
            except (TypeError, ValueError) as e:
                raise ValueError('The type of the first item of the argument must be compatible with the type of the column', e)
            return [lhs] + rhs[1:]
    
        def process_rhs(self, compiler, connection):
            if self.rhs_is_direct_value():
                # Do the quoting yourself, as Django outputs ' instead of " for parameters, and PostgreSQL complains
                # THIS IS DANGEROUS!!!!! I wish I could do it with parameters...
                def sql_sanitize(value):
                    # Check if the value respects the rules for column names
                    if re.match(r'^[a-zA-Z_][a-zA-Z0-9_]*', value):
                        return value
                    else:
                        raise ValueError("The argument {} is not a valid column name".format(value))
    
                sql = ", ".join('"{}"."{}"'.format(self.table_name, sql_sanitize(value)) for value in set(self.rhs))
                params = []
                return sql, params
            else:
                raise NotImplementedError("ReverseIn only supports direct values")
    
        def as_sql(self, compiler, connection):
            rhs, rhs_params = self.process_rhs(compiler, connection)
            sql = "%s IN ({rhs})".format(rhs=rhs)
            params = [self.lhs]
            params.extend(rhs_params)
            return sql, params
    
    IntegerField.register_lookup(ReverseIn)
    

    【讨论】:

      【解决方案2】:

      尝试使用Q 并检查每一列

      from django.db.models import Q
      
      MyModel.objects.filter(Q(column1=1) | Q(column2=2))
      

      这将是 sql 之类的情况

      SELECT * FROM sometable WHERE column1=1 OR column2=1
      

      【讨论】:

      • 我有 5 个这样的列,我想匹配最多 4 个值。这意味着 20 个 AND/OR,而使用 IN 我只得到 4 个 AND。没有办法翻转IN子句吗?
      • values = [1,2,3,4], .filter(Q(column1__in=values) | Q(column2__in=values) | ...) ?
      • 返回任何行,其中至少有一列在值中,不是吗?我想返回其中 4 列等于值的每一行,其中一个是任何值。使用 values = [1,2,3,4] 我想要的列是 [1,2,3,4,X] 的任何排列
      • 那应该是您的问题:P 如果所有列都不同(如您的问题中所述),则只需使用 & 而不是 |
      • 你是对的。抱歉,我很难表达这个概念 :) 我希望代码可以表达它。我不能只使用 5 & 因为一列不会在值中。所以我需要做 5 OR 里面我有 4 AND 我检查 column[1,2,3,4], column[1,2,3,5], column[1,2,4,5], column [1,3,4,5],列 [2,3,4,5]。如果我需要 3 个值,组合会增加更多..
      猜你喜欢
      • 2018-02-20
      • 2021-06-06
      • 1970-01-01
      • 2015-07-31
      • 2011-01-29
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多