查询Django-返回仅一个字段不为null的对象

时间:2019-04-15 11:30:34

标签: python django django-queryset

使用Django 1.8

我正在尝试筛选是否仅填充了所选字段中的一个的模型。如果我有这样的模型,我希望能够按如下所示对其进行过滤。

class MyModel(models.Model):
    field_a = models.IntegerField(null=True)
    field_b = models.IntegerField(null=True)
    field_c = models.IntegerField(null=True)
    field_d = models.IntegerField(null=True)
(field_a__isnull=False, field_b__isnull=True, field_c__isnull=True, field_d__isnull=True)
OR
(field_a__isnull=True, field_b__isnull=False, field_c__isnull=True, field_d__isnull=True)
OR
(field_a__isnull=True, field_b__isnull=True, field_c__isnull=False, field_d__isnull=True)
OR
(field_a__isnull=True, field_b__isnull=True, field_c__isnull=True, field_d__isnull=False)

因此,查询集应返回仅填充了模型中的一个字段且其余字段为null的所有对象。有没有办法通过Django查询来实现这一目标?

2 个答案:

答案 0 :(得分:1)

我能够使用Q动态构建它

fields = ['field_a', 'field_b', 'field_c', 'field_d']

q_query = reduce(operator.or_, (
    (reduce(operator.and_, (
        eval('Q({}__isnull={})'.format(field, False if fields[i] == field else True))
        for field in fields
    )))
    for i in range(len(fields))
))

MyModel.objects.filter(q_query)

这将构建一个Q对象,该对象具有与OR过滤器嵌套的AND过滤器并对其进行查询

<Q: (
  OR: (
    AND: ('field_a__isnull', False), ('field_b__isnull', True), ('field_c__isnull', True), ('field_d__isnull', True)
  ), (
    AND: ('field_a__isnull', True), ('field_b__isnull', False), ('field_c__isnull', True), ('field_d__isnull', True)
  ), (
    AND: ('field_a__isnull', True), ('field_b__isnull', True), ('field_c__isnull', False), ('field_d__isnull', True)
  ), (
    AND: ('field_a__isnull', True), ('field_b__isnull', True), ('field_c__isnull', True), ('field_d__isnull', False)
  )
)>

答案 1 :(得分:0)

如果您需要快速而又肮脏的解决方案,则可以尝试向模型中添加属性,然后使用该属性来查询模型中是否只有一个填充字段。

class MyModel(models.Model):
    field_a = models.IntegerField(null=True)
    field_b = models.IntegerField(null=True)
    field_c = models.IntegerField(null=True)
    field_d = models.IntegerField(null=True)

    @property
    def populated_field_count(self):
        fields = [self.field_a, self.field_b, self.field_c, self.field_d]
        return len([f for f in fields if f != None])
objects_with_one_field = [obj for obj in MyModel.objects.all() if obj.populated_field_count == 1)]

请注意,populated_field_count属性不能在Django查询中使用,这意味着它无法在数据库级别运行,这对性能造成了打击。

# THIS WILL NOT WORK IF USING OPTION ONE
objs = MyModel.objects.filter(populated_field_count=1)

编辑:作为选项二,如果您需要通过查询完成此操作,则可以使用Django Q进行高级查询。该选项的答案已经给出。

作为选择三,如果您不想使用Django Q,则可以将populated_field_count添加为Model的IntegerField属性。然后覆盖保存功能,以计算每次保存时填充的字段数。然后,您可以使用以下方式进行查询:

MyModel.objects.filter(populated_field_count=1)