使用django-crispy-forms处理带有分割字段的django-filter表单

时间:2014-01-30 06:42:53

标签: python django django-crispy-forms django-filter

我有一个django-tables2表,正在使用django-filter进行过滤。我已根据this great answer中的选项D 实施了一个方案。

但是,我使用django-crispy-forms和bootstrap3进行表单渲染。这很有用,直到我想要应用RangeFilter,它会吐出两个字段(最小和最大字段)。

我得到的输出是Resultant crazy price field

我想手动控制每个字段,以便我可以单独使用PrependedAppendedText。

有什么想法吗?

views.py

from crispy_forms.bootstrap import PrependedAppendedText, FormActions
from crispy_forms.helper import FormHelper
from crispy_forms.layout import Layout, Submit, Button
from django_tables2 import SingleTableView
from aids.filters import AidFilter
import os

class FilteredSingleTableView(SingleTableView):
    filter_class = None

    def get_table_data(self):
        self.filter = AidFilter(
            self.request.GET,
            queryset=super(FilteredSingleTableView, self).get_table_data(),
        )
        self.filter.helper = FormHelper()
        self.filter.helper.form_id = 'id_filterForm'
        self.filter.helper.form_class = 'form-inline'
        self.filter.helper.form_method = 'get'
        self.filter.helper.form_tag = True
        self.filter.helper.field_template = os.path.join('bootstrap3', 'layout', 'inline_field.html')

        self.filter.helper.layout = Layout(
            'name',
            PrependedAppendedText('price', '$', 'min', ),
            # Fieldset(
                # 'Filter by price',
                # PrependedAppendedText('price_0', '$', 'min', id='id_price_0'),
                # PrependedAppendedText('price_1', '$', 'max', id='id_price_1'),
            # ),
            'maint',
            'post',
            'supplier',
            FormActions(
                Submit('submit_filter', 'Filter', css_class='btn-primary'),
                Button('clear', 'Clear', css_class='btn-sm')
            )
        )

        return self.filter

    def get_context_data(self, **kwargs):
        context = super(FilteredSingleTableView, self).get_context_data(**kwargs)
        context['filter'] = self.filter
        return context

filters.py

import django_filters
from aids.models import Aid

class AidFilter(django_filters.FilterSet):
    name = django_filters.CharFilter(lookup_type='contains')
    price = django_filters.RangeFilter()

    class Meta:
        model = Aid
        fields = ['name', 'price', 'maint', 'post', 'supplier']

view_table.html

{% extends 'base.html' %}

{% block title %}{{ title }}{% endblock %}

{% block extra_css %}
    <link rel="stylesheet" href="{{ STATIC_URL }}django_tables2/themes/paleblue/css/screen.css" />
{% endblock %}

{% load render_table from django_tables2 %}
{% load crispy_forms_tags %}

{% block heading %}
    {{ title }}
{% endblock %}

{% block content %}
    {% crispy filter.form filter.helper %}
    {% render_table table %}
{% endblock %}

urls.py

from django.conf.urls import patterns, include, url
from django.conf.urls.static import static
from django.contrib import admin
from aids.filters import AidFilter
from aids.models import Aid
from aids.tables import AidTable
from sadb import settings
from aids.views import FilteredSingleTableView

admin.autodiscover()

urlpatterns = patterns('',
    url(r'^admin/', include(admin.site.urls)),
    url(r'^aid_form/', 'aids.views.aid_form'),
    url(r'^supplier_form/', 'aids.views.supplier_form'),
    url(r'^view_aids/', 'aids.views.view_aids'),
    url(
        r'^$', FilteredSingleTableView.as_view(
            model=Aid,
            table_class=AidTable,
            template_name='view_table.html',
            filter_class=AidFilter
        ), name='filtered_view'
    ),
) + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)

1 个答案:

答案 0 :(得分:1)

在覆盖DateRangeFilter之后我遇到了类似的问题,并通过修改窗口小部件输出来绕过它,并使用form-inline类和def format_output()中的修改位对其进行div包装在小部件类的末尾。

https://docs.google.com/file/d/0BwIBq4D09D0RaDZGTWhCWnJaZU0的屏幕截图(声誉已经让我发布了图片!)

我的完整代码如下(最初取自https://groups.google.com/d/msg/django-filter/lbi_B4zYq4M/37s63u0WrncJ

class DateRangeWidget(forms.MultiWidget):
    def __init__(self, attrs=None):
        attrs_from = {'class': 'date-from'}
        attrs_to = {'class': 'date-to'}

        if attrs:
            attrs_from.update(attrs)
            attrs_to.update(attrs)

        widgets = (forms.TextInput(attrs=attrs_from), forms.TextInput(attrs=attrs_to))
        super(DateRangeWidget, self).__init__(widgets, attrs)

    def decompress(self, value):
        if value:
            return [value.start, value.stop]
        return [None, None]

    def format_output(self, rendered_widgets):
        return '<div class="date-range form-inline">' + ' - '.join(rendered_widgets) + '</div>'


class DateRangeField(forms.MultiValueField):
    widget = DateRangeWidget

    def __init__(self, *args, **kwargs):
        fields = (
            forms.DateField(),
            forms.DateField(),
        )
        super(DateRangeField, self).__init__(fields, *args, **kwargs)

    def compress(self, data_list):
        if data_list:
            return slice(*data_list)
        return None


class DateRangeFilter(Filter):
    field_class = DateRangeField

    def filter(self, qs, value):
        date_start = datetime.datetime.combine(value.start, datetime.time(0, 0, 0))
        date_stop = datetime.datetime.combine(value.stop, datetime.time(23, 59, 59))

        if value:
            lookup = '%s__range' % self.name
            return qs.filter(**{lookup: (date_start, date_stop)})
        return qs