Skip to content

Instantly share code, notes, and snippets.

@rhenter
Last active September 18, 2020 21:28
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save rhenter/5ed2a262a87ad26749cf00f103394b63 to your computer and use it in GitHub Desktop.
Save rhenter/5ed2a262a87ad26749cf00f103394b63 to your computer and use it in GitHub Desktop.
Mixins de Busca e Filtros e exemplo de como fazer uma buscar e um filtro pegando da URL usando Django
import operator
from functools import reduce
from django.contrib.auth.mixins import LoginRequiredMixin
from django.db import models
from django.views.generic import ListView
from django.views.generic.base import ContextMixin
from django_stuff.utils import remove_special_characters
from quotation_project.utils import clean_url
class PageTitleMixin(ContextMixin):
page_title = ''
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['page_title'] = self.page_title
return context
class PaginationContextMixin(ContextMixin):
paginate_by = 80
page_base_url = ''
def get_page_base_url(self):
return self.page_base_url
def get_paginate_by(self, queryset):
"""
Get the number of items to paginate by, or ``None`` for no pagination.
"""
return int(self.request.GET.get('paginate_by', self.paginate_by))
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
search_url = clean_url(self.request.get_full_path())
context['search'] = self.request.GET.get('search', '')
context['search_url'] = search_url
context['page_base_url'] = self.get_page_base_url()
context['range_pagination'] = [x for x in range(20, 220, 20)]
context['paginate_by'] = self.get_paginate_by(self.get_queryset())
pagination_params = ['search', 'paginate_by', 'ordering']
context['append_param'] = '&' if any([True for x in pagination_params if x in search_url]) else '?'
return context
class BaseMixin(LoginRequiredMixin, PageTitleMixin):
pass
class ListSearchPaginationMixin(PaginationContextMixin, ListView):
search_fields = []
def get_search_term(self):
return self.request.GET.get('search', '').strip()
def do_search(self, queryset):
search_term = self.get_search_term()
if search_term and self.search_fields:
conditions = []
queries = [
models.Q(**{orm_lookup: remove_special_characters(search_term)})
for orm_lookup in self.search_fields
]
conditions.append(reduce(operator.or_, queries))
queryset = queryset.filter(reduce(operator.and_, conditions))
return queryset.distinct()
return queryset
def get_queryset(self):
queryset = super().get_queryset()
return self.do_search(queryset)
class ListFilterPaginationMixin(PaginationContextMixin, ListView):
filter_fields = []
filter_key_name = 'filter_by'
filter_field_name = None
def get_filter_fields(self):
return self.filter_fields
def get_filter_term(self):
return self.request.GET.get(self.filter_key_name, '').strip()
def do_filter(self, queryset):
filter_fields = self.get_filter_fields()
if not filter_fields or not self.filter_field_name:
return queryset
filter_term = self.get_filter_term()
return queryset.filter(**{self.filter_field_name: filter_term})
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
filter_fields = self.get_filter_fields()
context['filter_by'] = self.get_filter_term()
context['filter_key_name'] = self.filter_key_name
context['filter_fields'] = [{'id': k, 'name': v} for field in filter_fields for k, v in
field.items()]
return context
def get_queryset(self):
queryset = super().get_queryset()
return self.do_filter(queryset)
class BaseListView(BaseMixin, ListFilterPaginationMixin, ListSearchPaginationMixin):
def get_queryset(self):
queryset = super().get_queryset()
search_term = self.get_search_term()
if search_term:
queryset = self.do_search(queryset)
filter_term = self.get_filter_term()
if filter_term:
queryset = self.do_filter(queryset)
return queryset
'''
Altera para o edereço de onde vc colocou os seus 2 arquivos acima arquivos,
recomendo que seja um local global para todo o seu projeto
Imagina que tenho uma loja com os atributos:
Loja:
- nome
- cnpj
- estado
- cidade
search_fields: Atributo para você colocar uma lista de campos que deseja buscar. Os campos devem ser o mesmos usados para filtrar.
- nome_do_campo__icontains - Contem um valor com case insentivity (não importa a diferença entre maiuscula e minuscula)
- nome_do_campo__contains - Contem um valor com case sentivity (importa a diferença entre maiuscula e minuscula)
- nome_do_campo__iexact - Valor exato com case insentivity (não importa a diferença entre maiuscula e minuscula)
- nome_do_campo__exact - Valor exato com case sentivity (importa a diferença entre maiuscula e minuscula)
Entre outros
Exemplo de uma url com busca:
http://localhost:/lojas/?search=test
filter_fields: Atributo para você colocar uma lista de campos que deseja filtrar
Exemplo de uma url com filtro:
http://localhost:/lojas/?nome_do_filtro=test
'''
from .mixins import BaseListView
class TestListView(UpdateQuoteStatusMixin, ListSearchPaginationMixin):
template_name = "test/test_list.html"
page_base_url = reverse_lazy('test:list')
queryset = Quote.objects.all()
search_fields = (
'nome__icontains',
'cnpj',
)
filter_fields = (
'cidade'
)
'''
Função para pegar a URL limpa, sem caracteres especiais
'''
from urllib.parse import (parse_qsl, ParseResult, unquote, urlencode, urlparse)
def clean_url(url):
url = unquote(url)
# Extracting url info
parsed_url = urlparse(url)
# Extracting URL arguments from parsed URL
get_args = parsed_url.query
# Converting URL arguments to dict
parsed_get_args = dict(parse_qsl(get_args))
# Merging URL arguments dict with new params
parsed_get_args.pop('page', '')
parsed_get_args.update(
{k: dumps(v) for k, v in parsed_get_args.items()
if isinstance(v, (bool, dict))}
)
# Converting URL argument to proper query string
encoded_get_args = urlencode(parsed_get_args, doseq=True)
# Creating new parsed result object based on provided with new
# URL arguments. Same thing happens inside of urlparse.
new_url = ParseResult(
parsed_url.scheme, parsed_url.netloc, parsed_url.path,
parsed_url.params, encoded_get_args, parsed_url.fragment
).geturl()
return new_url
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment