如何使用动态过滤器制作 django 查询集?

Posted

技术标签:

【中文标题】如何使用动态过滤器制作 django 查询集?【英文标题】:How to make django querysets with dynamic filters? 【发布时间】:2015-06-20 17:21:20 【问题描述】:

所以基本上我在 Django 中有一个网站,它是一个店面,最终用户可以使用三个过滤器。产品类型过滤器(裤子、鞋子、衬衫等)、配送过滤器(是/否)和位置/人气过滤器。

目前在我的views.py中我有这个方法。

if request.is_ajax():
    if request.GET.get('filter') == 'shirts':
        latest_entries = Entry.objects.filter(entrytype="shirts")
        context = 'latest_entries': latest_entries
        return render(request, 'storefrontload.html', context)
    if request.GET.get('filter') == 'pants':
        latest_entries = Entry.objects.filter(entrytype="pants")
        context = 'latest_entries': latest_entries
        return render(request, 'storefrontload.html', context)
    if request.GET.get('filter') == 'shoes':
        latest_entries = Entry.objects.filter(entrytype="shoes")
        context = 'latest_entries': latest_entries
        return render(request, 'storefrontload.html', context)

如您所见,它处理第一个过滤器。我遇到的问题是,如果我选择,比如说“裤子”,它会按裤子过滤,但忽略在其他两个过滤器中选择的内容。另一个例子,假设我选择了裤子,页面会填充该过滤器的结果。但是,如果我随后转到交付过滤器并选择“是”,页面将填充仅可交付的项目,但忘记了“裤子”过滤器。

我想知道如何在我的视图中创建一个查询集,以记住其他两个查询集(如果这有意义的话)。

我能想到的唯一方法是为每个过滤器中的每个值创建真/假标志,然后添加大约 100 行 if/then 语句检查每个标志。一定有更好的方法。

更新:

这就是我从模板中传递过滤器值的方式。

function filter(type) 
  $.get("/storefront/?filter="+type, function(data) 
     var $data = data;
     $('.grid').children().remove();
     $('.grid').append( $data ).masonry( 'appended', $data, true ).masonry( 'layout' ); 
  );



  //Product Filter    
$("#shirts").unbind().click(function ()  
  filter("shirts");
  return false;
);


$("#pants").unbind().click(function ()  
  filter("pants");
  return false;
);

$("#shoes").unbind().click(function ()  
  filter("shoes");
  return false;
);



  //Delivery Filter
$("#deliveryyes").unbind().click(function ()  
  filter("deliveryyes");
  return false;
);

$("#deliveryno").unbind().click(function ()  
  filter("deliveryno");
  return false;
);

在我的views.py中,这不起作用:

 entry_types = request.GET.getlist('filter')
 latest_entries = Entry.objects.filter(entrytype__in=entry_types)

因为我需要按 entrytype('pants', 'shirts', shoes') 和 deliveryoption ('deliveryyes', 'deliveryno') 进行过滤。每个过滤器在我的模型中都有自己的列。

models.py

class Entry(models.Model):
    headline= models.CharField(max_length=200,)
    body_text = models.TextField()
    author=models.ForeignKey(settings.AUTH_USER_MODEL, related_name='entryauthors')
    pub_date=models.DateTimeField(auto_now_add=True)
    zipcode =models.IntegerField(null=True, max_length=10)
    !!! entrytype = models.CharField(null=True, max_length=10)
    !!! deliveryoption=models.CharField(null=True, max_length=5)

【问题讨论】:

【参考方案1】:

您可以传递filter 值的逗号分隔列表:

/mylist/filter=shirts,pants

然后使用__in 查找获取条目:

entry_types = request.GET.get('filter', '').split(',')
latest_entries = Entry.objects.filter(entrytype__in=entry_types)

或者使用QueryDictgetlist()方法:

/mylist/filter=shirts&filter=pants

使用相同的 ORM 调用:

entry_types = request.GET.getlist('filter')
latest_entries = Entry.objects.filter(entrytype__in=entry_types)

更新:要将多个类型传递给视图,将它们保存在数组中,并使用join() 方法获取它们的逗号分隔字符串:

var types = []; // currently shown entry types

function filter(type) 
  // add or remove the items from the grid
  var index = types.indexOf(type);
  if (index > -1) 
    types.splice(index, 1); // remove the type from the list
   else 
    types.push(type); // add the type to the filter
  
  $.get("/storefront/?filter="+types.join(","), function(data) 
    ...
  

更新 2:如果您使用两个字段来过滤查询集,那么您必须在视图中创建两个单独的列表:

filters = request.GET.getlist('filter')
entry_types = [f for f in filters if not f.startswith('delivery')]
delivery_types = [f for f in filters if f.startswith('delivery')]

latest_entries = Entry.objects.all()
if entry_types:
    latest_entries = latest_entries.filter(entrytype__in=entry_types)
if delivery_types:
    latest_entries = latest_entries.filter(deliverytype__in=delivery_types)

javascript 代码无需任何接触即可工作。

【讨论】:

我不确定如何首先传递多个过滤器的值。我更新了我的帖子并包含了模板中的一些代码。 将数组中的类型列表保存在全局范围内,并从您的click 事件中更改此数组。查看更新的答案。 好的,我想我会走你建议的这条路线。我还有一个关于如何在 javascript 中从类型数组中添加/删除的问题。我会在一秒钟内更新OP.. 好的,您能解释一下您在上面发布的 JavaScript 吗?具体来说,我不明白当你点击鞋子时如何从数组中删除过滤器,比如衬衫、裤子? 另外,我更新了我的帖子,如果你查看它的底部,你可以看到我的 models.py 我的 db 表中有单独的列用于“entrytype”和“deliveryoption”。这不是说 Entry.objects.filter(entrytype__in=entry_types) 不起作用吗?【参考方案2】:

一个参数在请求中多次出现是完全有效的。使用QueryDict.getlist() 获取包含所有值的列表。

http://example.com/?foo=12&foo=34

...

print >>sys.stderr, request.GET.getlist('foo')

【讨论】:

以上是关于如何使用动态过滤器制作 django 查询集?的主要内容,如果未能解决你的问题,请参考以下文章

如何使用 SQL 的“IN”等字段上的数组过滤 django 查询集?

如何使用 SQL 的“IN”等字段上的数组过滤 django 查询集?

如何在 Django 查询集过滤中执行不等于?

您将如何在 Django 中制作动态表单集?

如何使用 django 和 htmx 对查询集进行排序?

如何在 django 中过滤查询集的多对多