【问题标题】:Compiling django filters into a variable and executing it at runtime?将 django 过滤器编译成变量并在运行时执行?
【发布时间】:2019-01-22 10:38:13
【问题描述】:

我正在尝试为我的模型编写搜索功能。 客户具有以下字段:

cstid = models.AutoField(primary_key=True, unique=True)
name = models.CharField(max_length=35)
age=models.IntegerField()
gender = models.CharField(max_length=10, default='')
mobile = models.CharField(max_length=15, default='')
email = models.CharField(max_length=50, default='')
address = models.CharField(max_length=80, default='')
city = models.CharField(max_length=25, default='')

我的 html 表单将通过 POST 通过对 django 的 ajax 调用提交以下数据:

var data = {
    "name": name,
    "age": age,
    "email": email,
    "address": address,
    "phone": phone,
    "city": city
};
data = $(this).serialize() + "&" + $.param(data);

这些将在 django 视图中进行验证,并执行搜索查询,如下所示:

def searchpat_for_docwise_appt (request):
    from django.core.exceptions import ObjectDoesNotExist
    from django.db.models import Q
    from django.db.models import CharField
    from django.db.models.functions import Lower
    CharField.register_lookup(Lower, "lower")
    if request.method == 'POST':
        name = request.POST.get('name')
        age = request.POST.get('age')
        # gender = request.POST.get('gender')
        phone = request.POST.get('phone')
        email = request.POST.get('email').lower()
        address = request.POST.get('address')
        city = request.POST.get('city')
        if age.isdigit():            
            ANDSearchResult = customer.objects.filter(name__lower__contains=name.lower(), age=age, mobile__contains=phone, email__lower__contains=email.lower(
        ), address__lower__contains=address.lower(), city__lower__contains=city.lower())
        else:
            ANDSearchResult = customer.objects.filter(name__lower__contains=name.lower(), mobile__contains=phone, email__lower__contains=email.lower(
            ), address__lower__contains=address.lower(), city__lower__contains=city.lower())
        if len(ANDSearchResult) < 1:
            errmsg = 'No search results for AND search'
            print("Error message is <%s>" % errmsg)
        else:
            print ("ANDSearchResult is <%s>" % ANDSearchResult)
        errmsg = ''

        if age.isdigit():
            ORSearchResult = customer.objects.filter(
            Q(name__lower__contains=name.lower()) | Q(age=age) | Q(
                mobile__contains=phone) | Q(email__lower__contains=email.lower()) | Q(address__lower__contains=address.lower()) | Q(city__lower__contains=city.lower()))
        else:
            ORSearchResult = customer.objects.filter(
                Q(name__lower__contains=name.lower()) | Q(
                    mobile__contains=phone) | Q(email__lower__contains=email.lower()) | Q(address__lower__contains=address.lower()) | Q(city__lower__contains=city.lower()))
        if len(ORSearchResult) < 1:
            errmsg = errmsg + 'No search results for OR search'
            print("Error message is <%s>" % errmsg)
        else:
            print ("ORSearchResult is <%s>" % ORSearchResult)
        print(errmsg)
        return HttpResponse(errmsg)
    else:
        errmsg = 'No correct POST request. No valid response.'
        print("Error message is <%s>" % errmsg)
        return HttpResponse(errmsg)

正如您在上面提到的,我同时执行两个不同的查询。我想要一个类似于查询的 AND 运算符,其中搜索必须满足所有输入参数。我还需要一个 OR 运算符,如果任何字段与相应字段的输入字符串匹配,它应该提供结果。我的意思是,如果一个病人的名字是 56 岁的 Jeff,一个 23 岁的病人 Kane;我在 name 字段中输入 jef,在 age 字段中输入 23,两者都应该被列出。

当搜索框中有空字符串时,就会出现问题。使用空字符串的 OR 搜索将所有对象作为结果。显然,我不想仅仅因为我的搜索字符串为空就返回患者记录。

除了像这样检查所有搜索查询组合的冗长解决方案:

if (name != '' and age != '' and phone != '' and email != '' and address != '' and city != '':
    ORSearchResult = customer.objects.filter(Q(name__lower__contains=name.lower()) | Q(age=age) | Q(mobile__contains=phone) | Q(email__lower__contains=email.lower()) | Q(address__lower__contains=address.lower()) | Q(city__lower__contains=city.lower()))
elif (age != '' and phone != '' and email != '' and address != '' and city != '':
    ORSearchResult = customer.objects.filter(Q(age=age) | Q(mobile__contains=phone) | Q(email__lower__contains=email.lower()) | Q(address__lower__contains=address.lower()) | Q(city__lower__contains=city.lower()))        
elif (phone != '' and email != '' and address != '' and city != '':
    ORSearchResult = customer.objects.filter(Q(mobile__contains=phone) | Q(email__lower__contains=email.lower()) | Q(address__lower__contains=address.lower()) | Q(city__lower__contains=city.lower()))            

.... Other permutations ...

什么是更好的解决方案? 一个明显的想法是,尝试将这些过滤器编译成一个变量并将其传递给过滤器函数。我在正确的轨道上吗?我该怎么做?

【问题讨论】:

    标签: python django django-models django-filter


    【解决方案1】:

    如果我理解正确,您想在不同条件之间编写 OR 逻辑,因为相应的值不为空(真实性 False)。我们可以构造这样一个Q-object 构造函数:

    from django.db.models import Q
    from functools import reduce
    from operator import or_
    
    def or_q_if_truthfull(**kwargs):
        filtered = [Q(**{k: v}) for k, v in kwargs.items() if v]
        if filtered:
            return reduce(or_,filtered)
        else:
            return Q()

    然后我们可以构造所需的Q 对象:

    my_q = or_q_if_truthfull(
        name__lower__contains=name.lower(),
        age=age,
        mobile__contains=phone,
        email__lower__contains=email.lower()
        address__lower__contains=address.lower(),
        city__lower__contains=city.lower(),
    )

    所以我们可以过滤:

    ORSearchResult = customer.objects.filter(my_q)

    注意:可以使用field__icontains=value代替field__lower__contains=value.lower(),它执行不区分大小写的过滤,因此构造my_q的更安全和优雅的方法是:

    my_q = or_q_if_truthfull(
        name__icontains=name,
        age=age,
        mobile__contains=phone,
        email__icontains=email
        address__icontains=address,
        city__icontains=city,
    )

    【讨论】:

    • 由于不区分大小写的搜索和 mysql 的问题,我没有使用 icontains:stackoverflow.com/questions/51789688/…
    • 您能解释一下 **kwargs、**{k: v} 或_等中的语言结构吗?我开始使用 python 已经一个月了。
    • @Droidzone:是的,但我个人认为这是“解决错误的问题”。通常最好先修复排序规则。实际上使用foo.lower() == bar.lower()不是不区分大小写的比较,因为有些字符没有小写不变量。
    • @Droidzone: **kwargs 接受所有 named 参数,并将它们构造成字典。所以如果你调用or_q_if_truthfull(foo=bar),那么kwargs就是{'foo': bar},用**调用则相反。
    • @Droidzone:例如参见:saltycrane.com/blog/2008/01/…
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-09-27
    • 1970-01-01
    • 2013-02-06
    • 2011-08-19
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多