Пользовательский фильтр в Django Admin возвращает SuspiciousOperation

Я пытаюсь реализовать новую версию этого фрагмента, чтобы сделать его совместимым с Django 1.4 и 1.5.

Довольно много изменений, приведенный ниже код почти работает, за исключением того, что Django возвращает мне ошибку SuspiciousOperation. Я вижу, как я мог бы взломать его, но я бы предпочел не трогать ядро ​​Django. Если у вас есть предложения, добро пожаловать:

Вот мой фильтр:

class RelatedNullFilterSpec(FieldListFilter):
    def __init__(self, field, request, params, model, model_admin, field_path):
        field_root, field_name = field_path.rsplit('__', 1)
        self.lookup_title = field.verbose_name
        self.title = self.lookup_title
        self.null_lookup_kwarg = '%s__isnull' % field_root
        self.null_lookup_val = request.GET.get(self.null_lookup_kwarg, None)
        self.lookup_kwarg = '%s__exact' % (field_path)
        self.lookup_val = request.GET.get(self.lookup_kwarg, None)
        if isinstance(field, models.fields.BooleanField):
            self.lookup_choices = (
                # (None, _('All')),
                ('1', _('Yes')),
                ('0', _('No')))
        else:
            self.lookup_choices = field.get_choices(include_blank=False)
        super(RelatedNullFilterSpec, self).__init__(field, request, params, model, model_admin, field_path)

    def expected_parameters(self):
        return [self.lookup_kwarg, self.null_lookup_kwarg]

    def choices(self, cl):
        yield {'selected': self.lookup_val is None and self.null_lookup_val is None,
               'query_string': cl.get_query_string({}, [self.lookup_kwarg,self.null_lookup_kwarg]),
               'display': _('All')}
        yield {'selected': self.lookup_val is None and self.null_lookup_val=="True",
               'query_string': cl.get_query_string({self.null_lookup_kwarg:True},[self.lookup_kwarg]),
               'display': _('Null')}
        yield {'selected': self.lookup_val is None and self.null_lookup_val=="False",
               'query_string': cl.get_query_string({self.null_lookup_kwarg:False},[self.lookup_kwarg]),
               'display': _('Not Null')}
        for pk_val, val in self.lookup_choices:
            yield {'selected': self.lookup_val == smart_unicode(pk_val),
                   'query_string': cl.get_query_string({self.lookup_kwarg: pk_val},[self.null_lookup_kwarg]),
                   'display': val}

Затем в моем администраторе у меня есть следующее:

list_filter = ('time_added', 'time_modified', ('model1__model2__property', RelatedNullFilterSpec),)

И я всегда получал эту ошибку от метода lookup_allowed класса Django BaseModelAdmin...

В django.db.models.options я мог бы реализовать хак для перезаписи или расширения self.related_fkey_lookups, но на мой вкус это слишком хакерски.

РЕДАКТИРОВАТЬ: обратите внимание, что следующий почти стандартный фильтр также возвращает ту же ошибку: ('venue__eat_venue', BooleanFieldListFilter)

В общем, моя цель состоит в том, что мне нужен фильтр, который позволяет мне сортировать объекты по наличию/отсутствию поля, связанного с model2 (Null/Not-Null), и по значениям свойства (в случае, если поле, связанное с model2, существует ). Это было бы очень удобно, и я не думаю, что это слишком конкретно.

И, наконец, да, все работает, когда я не запрашиваю этот пользовательский фильтр для своего model1__model2__property :-)


person Rmatt    schedule 01.07.2013    source источник


Ответы (1)


Похоже, это ошибка в Django, когда у вас есть двойное подчеркивание для пути фильтра (первый элемент кортежа)... в вашем случае 'model1__model2__property'

ПРИМЕЧАНИЕ: исправлено в Django 1.7

Подробнее здесь: https://code.djangoproject.com/ticket/19182

Это довольно уродливо, но пока единственным обходным решением, которое я нашел, было вставить этот фиксированный и переопределенный метод в ModelAdmin, где вы хотите использовать этот list_filter:

def lookup_allowed(self, lookup, value):
    """
    Copy and pasted from ModelAdmin to fix bug in Django
    https://code.djangoproject.com/ticket/19182
    """
    from django.db.models.constants import LOOKUP_SEP
    from django.db.models.sql.constants import QUERY_TERMS
    from django.contrib.admin import FieldListFilter
    from django.contrib.admin import widgets

    model = self.model
    # Check FKey lookups that are allowed, so that popups produced by
    # ForeignKeyRawIdWidget, on the basis of ForeignKey.limit_choices_to,
    # are allowed to work.
    for l in model._meta.related_fkey_lookups:
        for k, v in widgets.url_params_from_lookup_dict(l).items():
            if k == lookup and v == value:
                return True

    parts = lookup.split(LOOKUP_SEP)

    # Last term in lookup is a query term (__exact, __startswith etc)
    # This term can be ignored.
    if len(parts) > 1 and parts[-1] in QUERY_TERMS:
        parts.pop()

    # Special case -- foo__id__exact and foo__id queries are implied
    # if foo has been specificially included in the lookup list; so
    # drop __id if it is the last part. However, first we need to find
    # the pk attribute name.
    rel_name = None
    for part in parts[:-1]:
        try:
            field, _, _, _ = model._meta.get_field_by_name(part)
        except FieldDoesNotExist:
            # Lookups on non-existants fields are ok, since they're ignored
            # later.
            return True
        if hasattr(field, 'rel'):
            model = field.rel.to
            rel_name = field.rel.get_related_field().name
        elif isinstance(field, RelatedObject):
            model = field.model
            rel_name = model._meta.pk.name
        else:
            rel_name = None
    if rel_name and len(parts) > 1 and parts[-1] == rel_name:
        parts.pop()

    if len(parts) == 1:
        return True
    clean_lookup = LOOKUP_SEP.join(parts)
    // FIX BEGINS:
    valid_lookups = [self.date_hierarchy]
    for filter_item in self.list_filter:
        if callable(filter_item):
            valid_lookups.append(filter_item.parameter_name)
        elif isinstance(filter_item, (list, tuple)):
            valid_lookups.append(filter_item[0])
        else:
            valid_lookups.append(filter_item)
    return clean_lookup in valid_lookups
person Anentropic    schedule 27.09.2013