如何为 Django 过滤器创建 link

How to create link for django filter

场景:

产品可以在数据库中定义多个属性,我希望能够按这些属性进行过滤。

由于我还没有找到使用 django-filter 使用动态属性的方法,目前这是通过使用 django 过滤器 MethodFilter 来实现的,它将作为查询字符串传递的属性解析为:

/products?attribute=size_2&attribute=color_red

url 这样的解析成功了

问题是构建 url:

我找不到构建 url 的合理方法,它会考虑当前搜索参数和 add/replace 这些参数。

Django 似乎强迫我使用 urlconf 但 django-filter 使用查询字符串参数。

我试图实现的是:

用户在页面 /products?attribute=size_10 上显示该尺寸的所有产品。 当他点击 link "color red" 时,新的 url 变成: /products?attribute=size_10&attribute=color_red

你能告诉我 django 的实现方式 吗?

如果您在中间件中包含 "django.core.context_processors.request", 然后 request.get 可在您的模板中访问。

然后您可以构建一个过滤器,它将 return 在构建您正在谈论的 link 时需要的 'GET' 变量。

这是我做的代码:

@register.simple_tag(takes_context=True)
def lessonapp_preserved_filters(context, url, dayofweek):
    opts = context.get('opts')
    preserved_filters = context.get('preserved_filters')

    parsed_url = list(urlparse(url))
    parsed_qs = dict(parse_qsl(parsed_url[4]))
    merged_qs = dict()

    if opts and preserved_filters:
        preserved_filters = dict(parse_qsl(preserved_filters))

        match_url = '/%s' % url.partition(get_script_prefix())[2]
        try:
            match = resolve(match_url)
        except Resolver404:
            pass
        else:
            current_url = '%s:%s' % (match.app_name, match.url_name)
            changelist_url = 'admin:%s_%s_changelist' % (opts.app_label, opts.model_name)
            if changelist_url == current_url and '_changelist_filters' in preserved_filters:
                preserved_filters = dict(parse_qsl(preserved_filters['_changelist_filters']))

        preserved_filters['dayofweek__exact'] = dayofweek

        merged_qs.update(preserved_filters)


    merged_qs.update(parsed_qs)

    parsed_url[4] = urlencode(merged_qs)
    return urlunparse(parsed_url)

然后在模板中我这样使用它:

{% lessonapp_preserved_filters adm_url '1' %}

也许你可以尝试使用一些 js 解决方案。

  1. 只需使用 js 构建字典从 url 获取所有当前属性。

  2. 当点击其他属性按钮时,可以添加或替换dict中的属性。

  3. 使用这个字典来组成你的url。

最后我解决了,虽然解决方案很冗长。


上下文

我想要一个多选过滤器,用户可以在其中过滤产品的品牌。 请注意,如果我每次点击多选框时页面都刷新就可以了。

主要思想

我为多项选择中的每个字段构建了不同的 link,方法是在视图中为过滤器中的每个 link 手动添加搜索参数。每次页面刷新都会创建新的link,具体取决于多选中的哪个选项被选中。

代码

views.py看起来像这样

from django.shortcuts import get_object_or_404, render
from django.core.paginator import EmptyPage, PageNotAnInteger, Paginator
from .models import Product, Brand
from django.db.models import Q

def search(request):
    queryset_list = Product.objects.order_by('price')
    # Keyworks
    if 'search' in request.GET:
        search = request.GET['search']

        if search:
            queryset_list = queryset_list.filter(Q(name__icontains=search) | Q(description__icontains=search))
            path = request.path
            link = path + '?' + 'search=' + search

            # Brands filter
            brands_list = []
            for query in queryset_list:
                if query.brand.name not in brands_list:
                    brands_list.extend([query.brand.name])
            brand_urls = {}
            brand_active = [False] * len(brands_list)
            if 'brand' not in request.GET:
                for brand in brands_list:
                    brand_urls[brand] = link + '&' + 'brand=' + brand
            else:
                active_filters = [f for f in request.GET['brand'].split(',')]
                if len(active_filters) > 1:
                    queryset_list = queryset_list.filter(brand__name__in=active_filters)
                for k, brand in enumerate(brands_list):
                    new_active_filters = active_filters.copy()

                    if brand in new_active_filters:
                        brand_active[k] = True
                        new_active_filters.remove(brand)
                        brand_urls[brand] = link + '&' + 'brand=' + ','.join(new_active_filters)
                    else:
                        brand_active[k] = False
                        new_active_filters.append(brand)
                        brand_urls[brand] = link + '&' + 'brand=' + ','.join(new_active_filters)

    paginator = Paginator(queryset_list, 12)
    page = request.GET.get('page')
    paged_products = paginator.get_page(page)

    context = {
        'products': paged_products,
        'values': request.GET,
        'brand_urls': brand_urls,
        'brand_active': brand_active,
    }
    return render(request, 'products/search.html', context)

基本上,每个过滤器都是一个 url,其中包含查询参数。因此,我通过添加或删除参数为每个 link 构建一个不同的 url,具体取决于 link 是否处于活动状态。

search.html 看起来像这样:

        <h2>Brands</h2>
        {% if brand_urls %}
          <ol>
            {% for brand in brand_urls.items %}
              {% if brand_active|my_index:forloop.counter0 is True %}
                <li><input type="checkbox" checked><a href="{{ brand.1 }}">{{ brand.0 }}</a></li>
              {% else %}
                <li><input type="checkbox"><a href="{{ brand.1 }}">{{ brand.0 }}</a></li>
              {% endif %}
            {% endfor %}
          </ol>
        {% endif %}

其中 my_index 是模板过滤器

from django import template
register = template.Library()

@register.filter
def my_index(List, i):
    return List[int(i)]