Django-filter: (2.0) FilterViewλŠ” λ°”μΈλ”©λ˜μ§€ μ•Šμ€ FilterSet에 λŒ€ν•΄ 항상 빈 QuerySet을 λ°˜ν™˜ν•©λ‹ˆλ‹€.

에 λ§Œλ“  2018λ…„ 06μ›” 29일  Β·  19μ½”λ©˜νŠΈ  Β·  좜처: carltongibson/django-filter

django_table2 ν…Œμ΄λΈ”μ„ λ Œλ”λ§ν•˜λ €κ³  ν•˜λŠ” AccountList λ·°κ°€ μžˆμŠ΅λ‹ˆλ‹€. 보기의 μ†ŒμŠ€ μ½”λ“œ:

class AccountList(SingleTableMixin, FilterView):
    model = Account
    table_class = AccountTable
    template_name = 'accounts/account_list.html'
    context_table_name = 'object_list'
    ordering = ['vps']

    filterset_class = AccountFilter

이 λ³΄κΈ°λŠ” ν˜„μž¬ 이 ν•„ν„° μ„ΈνŠΈ(django_filtersμ—μ„œ)λ₯Ό μ‚¬μš©ν•˜κ³  μžˆμŠ΅λ‹ˆλ‹€.

import django_filters
from accounts.models import Account

class AccountFilter(django_filters.FilterSet):
    class Meta:
        model = Account
        fields = ['is_suspended', 'is_abandoned']

    is_suspended = django_filters.BooleanFilter(name='is_suspended', initial='False')
    is_abandoned = django_filters.BooleanFilter(name='is_abandoned', initial='False')

    def __init__(self, data=None, *args, **kwargs):
        # if filterset is bound, use initial values as defaults
        if data is not None:
            # get a mutable copy of the QueryDict
            data = data.copy()

            for name, f in self.base_filters.items():
                initial = f.extra.get('initial')

                # filter param is either missing or empty, use initial as default
                if not data.get(name) and initial:
                    data[name] = initial

        super().__init__(data, *args, **kwargs)

이 ν…œν”Œλ¦Ώ μ‚¬μš©:

{% if filter %}
    <form action="" method="get" class="form form-inline">
        {{ filter.form.as_p }}
        <input type="submit" />
    </form>
{% endif %}

{% render_table object_list %}

{% endblock %}

이것은 λ‚΄ urls.pyμ—μ„œ κ°€μ Έμ˜¨ κ²ƒμž…λ‹ˆλ‹€.

path('', login_required(AccountList.as_view())),

λ‚΄ νŽ˜μ΄μ§€ 127.0.0.1:8000 λ°©λ¬Έν•˜λ©΄ ν•„ν„°κ°€ μ„€μ •λ˜μ§€ μ•Šμ€ 것을 λ³Ό 수 μžˆμŠ΅λ‹ˆλ‹€.
enter image description here

ν•˜μ§€λ§Œ 127.0.0.1:8000?page=1 ν•˜λ©΄ ν•„ν„°κ°€ μ œλŒ€λ‘œ μ΄ˆκΈ°ν™”λ˜λŠ” 것을 λ³Ό 수 μžˆμŠ΅λ‹ˆλ‹€.

enter image description here

λ‚΄ URL에 page=1이 μΆ”κ°€λ˜μ§€ μ•Šμ€ 경우 λ‚΄ 필터에 기본값이 μ—†λŠ” μ΄μœ λŠ” λ¬΄μ—‡μž…λ‹ˆκΉŒ?

κ°€μž₯ μœ μš©ν•œ λŒ“κΈ€

예 - PR을 μ‹œμž‘ν–ˆμ§€λ§Œ λ‚΄ μ˜€ν”ˆ μ†ŒμŠ€ μž‘μ—…μ„ 보λ₯˜ν•΄μ•Ό ν–ˆμŠ΅λ‹ˆλ‹€. λ‚˜λŠ” λ‚΄κ°€ μ€‘λ‹¨ν•œ κ³³μ—μ„œ κ½€ 빨리 λ‹€μ‹œ μ‹œμž‘ν•  수 μžˆμ–΄μ•Ό ν•œλ‹€.

λͺ¨λ“  19 λŒ“κΈ€

λ¬Έμžμ—΄ κ°’ 'False' λŒ€μ‹  μ‹€μ œ False λΆ€μšΈ 값을 μ‚¬μš©ν•΄ λ³΄μ‹­μ‹œμ˜€.

κ·Έλž˜μ„œ λ‹€μŒμ„ μ‹œλ„ν–ˆμ„ λ•Œ :

is_suspended = django_filters.BooleanFilter(name='is_suspended', initial=False)
is_abandoned = django_filters.BooleanFilter(name='is_abandoned', initial=False)

127.0.0.1:8000/ μ—μ„œ ν•„ν„°κ°€ λ Œλ”λ§λ˜λŠ” λ°©μ‹μž…λ‹ˆλ‹€.

image

ν•„ν„°κ°€ μ˜¬λ°”λ₯Έ κ°’μœΌλ‘œ λ Œλ”λ§λ˜μ—ˆμ§€λ§Œ μΌμ‹œ μ€‘μ§€λ˜κ±°λ‚˜ μ€‘λ‹¨λœ 계정을 계속 λ³Ό 수 μžˆμœΌλ―€λ‘œ ν•„ν„°κ°€ νš¨κ³Όκ°€ μ—†μ—ˆμŠ΅λ‹ˆλ‹€.

λ˜ν•œ ν•˜λ‹¨μ˜ 2 λ²„νŠΌμ„ 눌러 2νŽ˜μ΄μ§€λ₯Ό λ°©λ¬Έν•˜λ©΄ ν•„ν„°κ°€ Unknown μž¬μ„€μ •λœ μƒνƒœμ—μ„œ http://127.0.0.1:8000/?page=2 λ„λ‹¬ν•©λ‹ˆλ‹€.

image

μ΄μœ κ°€ μžˆλ‹€κ³  λ³΄μ‹­λ‹ˆκΉŒ?

μ£„μ†‘ν•©λ‹ˆλ‹€. λˆˆμ— λ„λŠ” 것이 μ—†μŠ΅λ‹ˆλ‹€.

첫 번째 문제의 경우 κ²°κ³Ό SQL 쿼리λ₯Ό μ‚΄νŽ΄λ³΄λŠ” 것이 μ’‹μŠ΅λ‹ˆλ‹€. suspended 및 abandoned ν•„ν„°κ°€ μ‹€μ œλ‘œ 적용되고 μžˆλŠ”μ§€ ν™•μΈν•˜μ‹­μ‹œμ˜€.
두 번째 문제의 경우 μˆ˜μ‹  데이터λ₯Ό ν™•μΈν•˜κ² μŠ΅λ‹ˆλ‹€. νŽ˜μ΄μ§€λ₯Ό λ³€κ²½ν•  λ•Œ suspended 및 abandoned 필터에 잘λͺ»λœ 값이 제곡될 수 μžˆμŠ΅λ‹ˆλ‹€.

λͺ‡ 가지 인쇄 문을 μ‚½μž…ν•˜κ³  λ‹€μŒμ„ μ°Ύμ•˜μŠ΅λ‹ˆλ‹€.

def __init__(self, data=None, *args, **kwargs):
    if data is not None:        # 1
        data = data.copy()

        for name, f in self.base_filters.items():
            initial = f.extra.get('initial')   

            # filter param is either missing or empty, use initial as default
            if not data.get(name) and initial:
                data[name] = initial

    super(BaseFilterSet, self).__init__(data, *args, **kwargs)

λ”°λΌμ„œ data λŠ” None . λ‚΄ 클래슀 기반 보기가 data λ₯Ό AccountFilter μ „λ‹¬ν•˜μ§€ μ•ŠλŠ” 이유λ₯Ό μ•Œκ²Œ λ κΉŒμš”?

흠. data μΈμˆ˜λŠ” 항상 FilterView μ œκ³΅ν•΄μ•Ό ν•©λ‹ˆλ‹€.

https://github.com/carltongibson/django-filter/blob/b1f1c6592ef64a2172ec39040607a9c0b0714140/django_filters/views.py#L39 -L46

data λŠ” 첫 번째 νŽ˜μ΄μ§€μ™€ 두 번째 νŽ˜μ΄μ§€ λͺ¨λ‘μ— λΉ„μ–΄ μžˆμŠ΅λ‹ˆκΉŒ?

이것은 μžˆλŠ” κ·ΈλŒ€λ‘œ μ²˜λ¦¬ν•  수 μ—†μŠ΅λ‹ˆλ‹€. 문제λ₯Ό μ‹λ³„ν•˜κΈ°μ— μΆ©λΆ„ν•œ 정보가 보λ₯˜ μ€‘μž…λ‹ˆλ‹€.

2.0으둜 μ—…κ·Έλ ˆμ΄λ“œν•œ 후에도 λ™μΌν•œ λ¬Έμ œκ°€ λ°œμƒν•©λ‹ˆλ‹€. κ²°κ³Όλ₯Ό μ–»μœΌλ €λ©΄ κΈ°λ³Έ νŽ˜μ΄μ§€ λ˜λŠ” 빈 ν•„ν„°κ°€ URL에 μžˆμ–΄μ•Ό ν•©λ‹ˆλ‹€. 이것은 적어도 μ–΄λ”˜κ°€μ— λ¬Έμ„œν™”λ˜μ–΄ μžˆμŠ΅λ‹ˆκΉŒ?

μž¬ν˜„ν•˜λ €λ©΄ 아직 더 λ§Žμ€ 정보가 ν•„μš”ν•©λ‹ˆλ‹€...

page ? 이것은 Django Filterκ°€ μ•„λ‹ˆλΌ νŽ˜μ΄μ§€ 맀김과 관련이 μžˆμ§€ μ•ŠμŠ΅λ‹ˆκΉŒ?

μ •ν™•νžˆ μ–΄λ–€ 정보가 ν•„μš”ν•œκ°€μš”? 2.0으둜 μ—…κ·Έλ ˆμ΄λ“œν•˜λ©΄ μ„€λͺ…λœ λ™μž‘μ΄ λ°œμƒν•©λ‹ˆλ‹€. 1.1.0으둜 λ‹€μš΄κ·Έλ ˆμ΄λ“œν•˜λ©΄ λͺ¨λ“  것이 μ •μƒμœΌλ‘œ λŒμ•„μ˜΅λ‹ˆλ‹€. κ°€λŠ₯ν•œ ν•œ μ΅œμ†Œν•œμœΌλ‘œ 보기와 ​​필터 μ„ΈνŠΈλ₯Ό λ‹¨μˆœν™”ν•˜λ €κ³  μ‹œλ„ν–ˆμ§€λ§Œ λ™μž‘μ΄ μ§€μ†λ˜λ―€λ‘œ μž¬ν˜„ν•˜κΈ°κ°€ μƒλ‹Ήνžˆ μ‰¬μ›Œμ•Ό ν•©λ‹ˆλ‹€.

@moorchegue , 문제λ₯Ό λ³΄μ—¬μ£ΌλŠ” μ΅œμ†Œν•œμ˜ 예제 ν…ŒμŠ€νŠΈ μΌ€μ΄μŠ€ λ˜λŠ” ν…ŒμŠ€νŠΈ ν”„λ‘œμ νŠΈκ°€ 도움이 될 κ²ƒμž…λ‹ˆλ‹€.

λ˜ν•œ νŽ˜μ΄μ§€ 맀김 논리λ₯Ό μ œκ³΅ν•˜λŠ” λͺ¨λ“  ν•­λͺ©μ— λ¬Έμ œκ°€ μžˆλŠ” 것 κ°™μŠ΅λ‹ˆλ‹€. 예, μƒˆ 버전과 μ•½κ°„μ˜ λΉ„ν˜Έν™˜μ„±μ΄ μžˆμ„ 수 μžˆμ§€λ§Œ λ‹€λ₯Έ νŒ¨ν‚€μ§€μ˜ 디버깅 λ¬Έμ œλŠ” λ²”μœ„λ₯Ό λ²—μ–΄λ‚©λ‹ˆλ‹€. μ—¬κΈ°μ„œ ν•  수 μžˆλŠ” λͺ¨λ“  μž‘μ—…μ„ μˆ˜ν–‰ν•˜λ €λ©΄ Django Filterμ—μ„œ 버그λ₯Ό μ‹œμ—°ν•΄μ•Ό ν•©λ‹ˆλ‹€.

자, μ—¬κΈ°μ„œ λ¬Έμ œλŠ” #788의 μΌλΆ€λ‘œ λ„μž…λœ is_valid() 방법에 μžˆμŠ΅λ‹ˆλ‹€.

https://github.com/carltongibson/django-filter/blob/1dde11c70eedeac32f6de92c426deb289e1aebd8/django_filters/filterset.py#L202 -L206

data 이 None λ•Œ μžλ™μœΌλ‘œ False λ°˜ν™˜ν•©λ‹ˆλ‹€.

λ·°μ—μ„œ object_list λ₯Ό qs.none() ν•©λ‹ˆλ‹€.

https://github.com/carltongibson/django-filter/blob/1dde11c70eedeac32f6de92c426deb289e1aebd8/django_filters/views.py#L80 -L83

λ”°λΌμ„œ κ΄€μ°° 된 행동.

이에 λŒ€ν•œ ν…ŒμŠ€νŠΈλŠ” ν…ŒμŠ€νŠΈ ν…œν”Œλ¦Ώμ„ μ‚¬μš©ν•˜κ³  있기 λ•Œλ¬Έμ— μ‹€νŒ¨ν•˜μ§€ μ•ŠλŠ” filter.qs λ³΄λ‹€λŠ” object_list .

https://github.com/carltongibson/django-filter/blob/1dde11c70eedeac32f6de92c426deb289e1aebd8/tests/templates/tests/book_filter.html#L3 -L5

즉각적인 ν•΄κ²° 방법은 ν•„ν„° μ„ΈνŠΈμ— strict = False μ„€μ •ν•˜λŠ” κ²ƒμž…λ‹ˆλ‹€. 이둜 인해 μ‹€μ œ μœ νš¨ν•˜μ§€ μ•Šμ€ ν•„ν„° λ§€κ°œλ³€μˆ˜κ°€ 빈 κ²°κ³Όλ₯Ό ν‘œμ‹œν•˜λŠ” λŒ€μ‹  λΆ€λΆ„ ν•„ν„°λ§μœΌλ‘œ μ΄μ–΄μ§€μ§€λ§Œ ν˜„μž¬λ‘œμ„œλŠ” μž‘λ™ν•  수 μžˆμ–΄μ•Ό ν•©λ‹ˆλ‹€. (λ¬Όλ‘  λ·° get 논리λ₯Ό μž¬μ •μ˜ν•˜λŠ” 것이 더 μ’‹μŠ΅λ‹ˆλ‹€...)

κ΅¬μ†λ˜μ§€ μ•Šμ€ μΌ€μ΄μŠ€μ— λŒ€ν•œ 처리λ₯Ό κ°œμ„ ν•˜κ³  μƒκ°ν•˜κ² μŠ΅λ‹ˆλ‹€.

흠. μ—¬κΈ°μ„œ μ£Όμš” 결함은 View의 object_list 와 FilterSet의 .qs μ‚¬μ΄μ˜ λΆˆμΌμΉ˜μž…λ‹ˆλ‹€. μ—„κ²©ν•œ λ™μž‘μ„ FilterSet.qs λ‹€μ‹œ μ΄λ™ν•˜λ©΄ λ¬Έμ œκ°€ ν•΄κ²°λ©λ‹ˆλ‹€.

λ˜ν•œ DRF λ°±μ—”λ“œμ— λŒ€ν•œ 엄격/비엄격 μ²˜λ¦¬κ°€ μ—†μŠ΅λ‹ˆλ‹€.

λ‚˜λŠ” 이것을 닦을 것이닀.

@rpkilby 이 λ¬Έμ œμ— 진전이 μžˆμŠ΅λ‹ˆκΉŒ?

예 - PR을 μ‹œμž‘ν–ˆμ§€λ§Œ λ‚΄ μ˜€ν”ˆ μ†ŒμŠ€ μž‘μ—…μ„ 보λ₯˜ν•΄μ•Ό ν–ˆμŠ΅λ‹ˆλ‹€. λ‚˜λŠ” λ‚΄κ°€ μ€‘λ‹¨ν•œ κ³³μ—μ„œ κ½€ 빨리 λ‹€μ‹œ μ‹œμž‘ν•  수 μžˆμ–΄μ•Ό ν•œλ‹€.

ν•΄κ²° 방법은 FilterMixin ν•˜μœ„ ν•­λͺ©(μ•„λ§ˆλ„ View strict = False 을 μ„€μ •ν•˜λŠ” κ²ƒμž…λ‹ˆλ‹€.

λ¬΄ν•œ ν˜•μ‹μ˜ 경우 qsλ₯Ό λ°˜ν™˜ν•˜λ„λ‘ 쑰건을 λ³€κ²½ν•  수 μ—†μŠ΅λ‹ˆκΉŒ?

예

-        if self.filterset.is_valid() or not self.get_strict():
+        if not self.filterset.is_bound or self.filterset.is_valid() or not self.get_strict():

https://github.com/carltongibson/django-filter/pull/1007

감사 ν•΄μš”.

이 νŽ˜μ΄μ§€κ°€ 도움이 λ˜μ—ˆλ‚˜μš”?
0 / 5 - 0 λ“±κΈ‰