Django查询WHERE 1 IN(column1,column2)

时间:2015-10-15 10:54:31

标签: django postgresql django-models django-queryset

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

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

结果是0,2给出下表

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

如何在Django ORM中对其进行建模?

我考虑过自定义查找,但我需要生成'1__myin' = [column1, column2]的{​​{1}} 我想避免使用RAW SQL,因为我不想错误地在代码中添加一些漏洞。

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

2 个答案:

答案 0 :(得分:2)

尝试使用Q,然后检查每一列

number = 30
factors = []
for i in range(1, number+1):
  if number%i == 0:
     factors.append(i)

print factors

这将是一个类似

的案例
from django.db.models import Q

MyModel.objects.filter(Q(column1=1) | Q(column2=2))

答案 1 :(得分:0)

我最终实现了一个自定义过滤器,使其反转IN",以便您可以查询列而不是值。

过滤器的工作原理如下

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)