将Django过滤器编译为变量并在运行时执行它?

时间:2018-08-15 14:25:04

标签: python django django-models django-filter

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

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表单将通过ajax调用django通过POST提交以下数据:

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运算符,如果任何字段与相应字段的输入字符串匹配,则应提供结果。我的意思是,如果一个患者的名字叫杰夫(Jeff)享年56岁,一个患者的凯恩(Kane)享年23岁。我在名称字段中输入jef,在年龄字段中输入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 ...

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

1 个答案:

答案 0 :(得分:1)

如果我理解正确,那么您要在不同条件之间编写OR-逻辑,因为相应的值不为空(真实性False)。我们可以使用以下方法构造这样的Q对象构造函数:

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__lower__contains=value.lower(),可以使用field__icontains=value来执行大小写不变的过滤,因此构造{{1 }}是:

my_q