简体   繁体   中英

How to create link for django filter

Scenario:

Products can have multiple attributes defined in the database, I want to be able to filter by those attributes.

Since i have not found a way to use dynamic attributes using django-filter, this is currently achieved by using django filter MethodFilter which parses the attributes passed as query string as:

/products?attribute=size_2&attribute=color_red

url like this is parsed and it works.

The problem is building the url:

I couldn't find a reasonable way to build the url, that would take current search parameters into account and add/replace those parameters.

Django seems to be forcing me to use urlconf but django-filter uses query string parameters.

What i try to achieve is this:

The user is on page /products?attribute=size_10 which display all products with that size. When he clicks th link "color red" the new url becomes: /products?attribute=size_10&attribute=color_red

Can you point me to the django way of implementing this?

if you include "django.core.context_processors.request", in your middleware then the request.get is accessible in your templates.

you could then build a filter that will return what 'GET' variables you want when building the link you are talking about.

here is code for one that i did:

@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)

and then in the template i use it like this:

{% lessonapp_preserved_filters adm_url '1' %}

maybe you try use some js solution.

  1. just get all the current attributes from url by using js ,build dict.

  2. when clicking other attribute button,you can add or replace the attribute in the dict.

  3. use this dict to form your url.

At the end I solved, although the solution is pretty verbose.


Context

I want a multiple choice filter, where the user can filter the brand of the products. Note that it is ok if the page refreshes every time I click on the multiple choice field.

Main idea

I build a different link for each field in the multiple choice, by manually adding the search parameters in a view for each link in the filters. Every time the page refreshes new link are created, depending on which choice in the multiple choice is selected.

Code

The views.py looks like this

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)

basically, every filter is a url, that contains the query parameters. So I build a different url for every link by adding or removing parameters, depending if the link is active or not.

search.html looks like this:

        <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 %}

where my_index is a template filter

from django import template
register = template.Library()

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM