Django的JSONField过滤查询集

8

我正在使用Python 3.6.3、Django 2.0和PostgreSQL 9.4来完成这个项目。 在我的Ticket类中,我有一个JSONField类型的passenger字段。

    passenger = JSONField(blank=True)

我的乘客JSON如下所示:
{
    "email": null, 
    "mobile": "21312", 
    "passport": "2141241", 
    "sms_sent": false, 
    "full_name": "something"
},
{
    "email": null, 
    "mobile": null, 
    "passport": "1231231", 
    "sms_sent": false, 
    "full_name": "Irfan"
},
{
    "email": null, 
    "mobile": null, 
    "passport": "1231231", 
    "sms_sent": true, 
    "full_name": "Irfan"
}

现在我有一个Django命令,我想过滤掉手机号不为空或者为None,并且sms_sent是False的票。
    tickets = Ticket.objects.filter(
        date=tomorrow, trip__bus_company=bus_company,
        passenger__sms_sent=False
    ).not_cancelled()

现在,乘客短信发送状态为False的筛选器已经生效,仅返回短信发送状态为False的车票。但是乘客手机号码筛选器没有生效。 我尝试了以下所有方法:

    tickets = tickets.exclude(passenger__mobile=None)
    tickets = tickets.exclude(passenger__mobile=None).exclude(passenger__mobile='')
    tickets = tickets.exclude(passenger__mobile__isnull=True)
    tickets = tickets.exclude(passenger__exact={'mobile': None})
    tickets = tickets.exclude(passenger__mobile__isnull=True).exclude(passenger__mobile='')
    tickets = tickets.exclude(passenger__mobile__isnull=False).exclude(passenger__mobile='')
    tickets = tickets.exclude(Q(passenger__mobile__isnull=True) | Q(passenger__mobile=''))

同时在第一个筛选器中加入passenger__mobile,但我无法排除掉乘客手机号为空的票据,我要么得到所有的车票,要么得到空的查询结果。

现在我能做到这一点:

for ticket in tickets:
            if ticket.passenger['mobile'] is not None:
                print(ticket.passenger['mobile'])

但这不是我要寻找的内容。我想使用筛选或排除来获取那些票。我做错了什么吗? 附:not_cancelled() 是我的经理,与乘客字段无关。


你确定数据库中存在空值记录吗?blank=True将允许表单中的空值,但我认为你还需要使用null=True来允许Django通过空值。 - Alan
@Alan,问题在于我的模型已经是blank=True,null=True了,但即使这样也不起作用,所以我将其更改为仅blank=True,这样它就不允许null和None。而在数据库中,它是“mobile”:null。 - Irfan Dzankovic
1个回答

9
根据https://code.djangoproject.com/ticket/25718(请看最后一条评论),以下内容应该有效model.objects.filter(field__key=None) (但是您应该使用已修复的Django版本)。
Django文档https://docs.djangoproject.com/en/2.0/ref/contrib/postgres/fields/#querying-jsonfield指出:

由于任何字符串都可以是JSON对象中的键,除了下面列出的查找之外的任何查找都将被解释为键查找。不会引发错误。要特别小心打字错误,并始终检查查询是否按照您的意图工作。

这里是https://docs.djangoproject.com/en/2.0/ref/contrib/postgres/fields/#containment-and-key-operations

问题是,如果我理解你的意思,filter(passenger__mobile=None)应该可以工作,但在我的情况下却不行。它给了我两张票(第三张已经被sms_sent=True过滤掉了)。它给了我一个"mobile": null和一个"mobile": '"21312"的车票。 - Irfan Dzankovic
好的,在Django 2.1中将会实现这一点,但是现在,我可以通过以下方式进行管理:如果ticket.passenger['mobile']不为None: - Irfan Dzankovic

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接