使用django-filter通过外键上的CharFilter进行过滤

时间:2015-12-31 00:04:13

标签: django python-3.x django-filter

我尝试使用django-filter显示已过滤的订单列表。每个订单都有一个设备字段,它是一个ForeignKey,如下所示:

class Order(models.Model):
    device = models.ForeignKey(Device)
    BLACK = 'BLACK'
    CYAN = 'CYAN'
    MAGENTA = 'MAGENTA'
    YELLOW = 'YELLOW'
    COLOR_CHOICES = (
        (BLACK, 'black'),
        (CYAN, 'cyan'),
        (MAGENTA, 'magenta'),
        (YELLOW, 'yellow'),
    )
    toner = models.CharField(max_length=200, choices=COLOR_CHOICES)
    order_date = models.DateTimeField('order triggered date')

    class Meta:
        ordering = ['-order_date']

    def __str__(self):
        return self.toner

默认过滤器类型是ModelChoiceFilter,但它显示了可能数千个设备的很长列表。我希望能够使用CharFilter过滤文本,如下所示:filters.py:

class OrderFilter(df.FilterSet):
    device = df.CharFilter(lookup_type='icontains')
    order_date = df.DateFilter()

class Meta:
    model = Order
    fields = ['device', 'toner', 'order_date']
    order_by = ['order_date']

但是,在尝试通过设备过滤器字段上的类型文本进行过滤时,我发现了以下错误。

Traceback:
File "/home/mwood/auto_toner_django/venv_auto_toner/lib/python3.4/site-packages/django/core/handlers/base.py" in get_response
  132.                     response = wrapped_callback(request, *callback_args, **callback_kwargs)
File "/usr/lib/python3.4/contextlib.py" in inner 
  30.                 return func(*args, **kwds)
File "/home/mwood/auto_toner_django/venv_auto_toner/lib/python3.4/site-packages/django/contrib/auth/decorators.py" in _wrapped_view
  22.                 return view_func(request, *args, **kwargs)
File "/home/mwood/auto_toner_django/auto_toner/auto_toner/views.py" in OrderView
  50.   return render(request, 'auto_toner/order_filter.html', context, context_instance=RequestContext(request))
File "/home/mwood/auto_toner_django/venv_auto_toner/lib/python3.4/site-packages/django/shortcuts.py" in render
  89.             using=using)
File "/home/mwood/auto_toner_django/venv_auto_toner/lib/python3.4/site-packages/django/template/loader.py" in render_to_string
  115.                         template_name, context, context_instance, dirs, dictionary)
File "/home/mwood/auto_toner_django/venv_auto_toner/lib/python3.4/site-packages/django/template/engine.py" in render_to_string
  221.             return t.render(context_instance)
File "/home/mwood/auto_toner_django/venv_auto_toner/lib/python3.4/site-packages/django/template/base.py" in render
  209.                     return self._render(context)
File "/home/mwood/auto_toner_django/venv_auto_toner/lib/python3.4/site-packages/django/test/utils.py" in instrumented_test_render
  96.     return self.nodelist.render(context)
File "/home/mwood/auto_toner_django/venv_auto_toner/lib/python3.4/site-packages/django/template/base.py" in render
  903.                 bit = self.render_node(node, context)
File "/home/mwood/auto_toner_django/venv_auto_toner/lib/python3.4/site-packages/django/template/debug.py" in render_node
  79.             return node.render(context)
File "/home/mwood/auto_toner_django/venv_auto_toner/lib/python3.4/site-packages/django/template/loader_tags.py" in render
  135.         return compiled_parent._render(context)
File "/home/mwood/auto_toner_django/venv_auto_toner/lib/python3.4/site-packages/django/test/utils.py" in instrumented_test_render
  96.     return self.nodelist.render(context)
File "/home/mwood/auto_toner_django/venv_auto_toner/lib/python3.4/site-packages/django/template/base.py" in render
  903.                 bit = self.render_node(node, context)
File "/home/mwood/auto_toner_django/venv_auto_toner/lib/python3.4/site-packages/django/template/debug.py" in render_node
  79.             return node.render(context)
File "/home/mwood/auto_toner_django/venv_auto_toner/lib/python3.4/site-packages/django/template/loader_tags.py" in render
  65.                 result = block.nodelist.render(context)
File "/home/mwood/auto_toner_django/venv_auto_toner/lib/python3.4/site-packages/django/template/base.py" in render
  903.                 bit = self.render_node(node, context)
File "/home/mwood/auto_toner_django/venv_auto_toner/lib/python3.4/site-packages/django/template/debug.py" in render_node
  79.             return node.render(context)
File "/home/mwood/auto_toner_django/venv_auto_toner/lib/python3.4/site-packages/endless_pagination/templatetags/endless.py" in render
  296.         objects = self.objects.resolve(context)
File "/home/mwood/auto_toner_django/venv_auto_toner/lib/python3.4/site-packages/django/template/base.py" in resolve
  787.             value = self._resolve_lookup(context)
File "/home/mwood/auto_toner_django/venv_auto_toner/lib/python3.4/site-packages/django/template/base.py" in _resolve_lookup
  839.                                                        (bit, current))  # missing attribute

Exception Type: VariableDoesNotExist at /devices/orders/
Exception Value: Failed lookup for key [qs] in '<auto_toner.filters.OrderFilter object at 0x7fd721fa32e8>'

知道我做错了什么,或者是否有更好的方法来解决这个问题?感谢

1 个答案:

答案 0 :(得分:1)

正如评论中所建议的那样,使用q对象确实变得更简单,并且一旦我花时间去做它就具有更少的依赖性。我实际上从帖子here学到了如何做到这一点,这篇文章描述了用q对象实现一个简单的搜索功能。使用相同的原则从URL中获取请求参数,我很容易想出一个排序/过滤功能。

为了防止链接腐烂,请在此处发布链接内容。

  

搜索是一种功能,或者至少应该在包含动态或大型内容的大多数网站上展示。

     

有一些项目可以解决这个问题。这是一个非详尽的清单:djangosearch,django-search(带破折号),django-sphinx。

     

这些搜索引擎很棒,但如果您只需要CMS或博客的简单搜索功能,那么它们似乎有点过分。

     

为了解决这个问题,我想出了一个通用而简单的技巧。您只需将以下代码段复制/粘贴到项目中的任何位置:

import re

from django.db.models import Q

def normalize_query(query_string,
                findterms=re.compile(r'"([^"]+)"|(\S+)').findall,
                normspace=re.compile(r'\s{2,}').sub):
''' Splits the query string in invidual keywords, getting rid of unecessary spaces
    and grouping quoted words together.
    Example:

    >>> normalize_query('  some random  words "with   quotes  " and   spaces')
    ['some', 'random', 'words', 'with quotes', 'and', 'spaces']

'''
return [normspace(' ', (t[0] or t[1]).strip()) for t in findterms(query_string)] 

def get_query(query_string, search_fields):
''' Returns a query, that is a combination of Q objects. That combination
    aims to search keywords within a model by testing the given search fields.

'''
query = None # Query to search for every search term        
terms = normalize_query(query_string)
for term in terms:
    or_query = None # Query to search for a given term in each field
    for field_name in search_fields:
        q = Q(**{"%s__icontains" % field_name: term})
        if or_query is None:
            or_query = q
        else:
            or_query = or_query | q
    if query is None:
        query = or_query
    else:
        query = query & or_query
return query
  

上面的内容是生成一个django.db.models.Q对象(请参阅doc),根据查询字符串和您要搜索的模型字段搜索模型。重要的是,它还通过拆分关键字并允许按引号分组来分析查询字符串。例如,以下查询字符串...

     

&#39;一些随机的单词&#34;带引号&#34;和空间&#39;   ......单词&#39;,&#39;随机&#39;,&#39;单词&#39;,&#39;带引号&#39;,&#39;和&#39;,& #39;空格&#39;实际上会被搜查。它使用所有给定的单词执行AND搜索,但您可以轻松地对其进行自定义以进行不同类型的搜索。

     

然后,您的搜索视图将变得如此简单:

def search(request):
query_string = ''
found_entries = None
if ('q' in request.GET) and request.GET['q'].strip():
    query_string = request.GET['q']

    entry_query = get_query(query_string, ['title', 'body',])

    found_entries = Entry.objects.filter(entry_query).order_by('-pub_date')

return render_to_response('search/search_results.html',
                      { 'query_string': query_string, 'found_entries': found_entries },
                      context_instance=RequestContext(request))
  

就是这样!我在一个有大约10,000个新闻项目的网站上使用它,它的工作速度非常快......

     

现在您没有理由不在您的网站上添加搜索框! ;)