Шаблон Django - проверьте, имеет ли поле значение в нем или пустое, если ManyToManyField

Это кажется довольно простым, но я не могу найти хорошее решение из документов SO или Django (с использованием Django 1.10)

Я извлекаю объект модели в шаблоне и отображаю только те поля, которые имеют значения (т. е. не пустые или нулевые) или не пустые, если ManyToManyField.

Я не могу найти хороший способ сделать это, кроме как использовать множество if и else для проверки типа поля, а затем запускать соответствующее условие if.

{% if model_name.field_name %} всегда возвращает true за ManytoManyFieldс (поскольку это ManyRelatedManager)

{% if model_name.field_name|length > 0 %} всегда возвращает false в течение ManytoManyFieldс

{% if model_name.field_name.all %} может нормально работать для ManyToManyFields, но не для других (например, CharField)

Есть ли один способ проверить, есть ли внутри поля какое-то полезное значение (будь то ManytoManyField или простое CharField)? Я могу прибегнуть к использованию нескольких if-else, чтобы сначала проверить тип поля, а затем выполнить соответствующую проверку, но это кажется нелогичным, так как это похоже на обычный вариант использования для оправдания тега шаблона.


person Anupam    schedule 10.05.2017    source источник
comment
Если вам нужна такая логика, может быть, было бы лучше поместить ее в свой views.py. Связанные объекты из ManyToManyField или ForeignKeyField должны вызываться каким-либо методом RelatedManager: all или, может быть, filter.   -  person cezar    schedule 10.05.2017
comment
@cezar - спасибо, я перенес логику в views.py. Опубликовал мой окончательный код в качестве ответа.   -  person Anupam    schedule 11.05.2017


Ответы (3)


Вы можете сделать его чище, используя get_fields: https://docs.djangoproject.com/en/1.11/ref/models/meta/#retrieving-all-field-instances-of-a-model

Итак, в вашем случае это будет выглядеть примерно так (это псевдокод):

{% for field in Model._meta.get_fields() %}
    {% if field.get_internal_type() == "ManyToManyField" %}
        ...  # render m2m, here you could use gettatr
    {% else %}
        ...  # render regular field, here you could use gettatr
    {% endif %}
{% endfor %}

Но я также согласен с комментарием @cezar о том, что вы должны перенести что-то подобное в views.py

person Sardorbek Imomaliev    schedule 10.05.2017
comment
Спасибо @Sardorbek - вы имели в виду использование соответствующей логики для get_fields(), getattr(), get_internal_type() и т. д. в шаблоне? Я разместил свой код - теперь переместил логику в views.py, и, что интересно, окончательный код похож на ваш псевдокод для шаблона, поэтому голосование - person Anupam; 11.05.2017
comment
@Anupam да, это можно использовать в шаблонах, но я настоятельно рекомендую сделать это в views.py, как вы это сделали. - person Sardorbek Imomaliev; 12.05.2017

Вот что я в итоге сделал:

Совет @cezar имел смысл, поэтому я переместил логику в views.py (в конце концов, логика того, показывать ли значение или нет, попадет в корзину «что показывать» и, следовательно, переходит к представлению, а не к тому, как показать в шаблоне)

Кроме того, вместо того, чтобы возвращать объект модели в шаблон, теперь возвращается dict только с соответствующими значениями. ManyToManyFields (если они не пустые) отображаются в виде списков в словаре.

for field in school._meta.get_fields(): # where school is the model object

    #checking if field type is m2m
    if (field.get_internal_type() == 'ManyToManyField'):
        if getattr(school,field.name).exists():
             school_display[field.name] = [k.name for k in getattr(school, field.name).all()]

    #checking if field has choices (so we could send the display name)
    elif field.choices:
        func_name_to_display = 'get_' + field.name + '_display'
        #note the '()' at the end, below. getattr(a,b) is equivalent to a.b so we need to add a '()' at the end since its a function
        school_display[field.name] = getattr(school,func_name_to_display)()

    #just returning the value (if not empty) in other cases
    else:   
        field_value = getattr(school,field.name)
        if (field_value and field_value!='None'):
             school_display[field.name] = field_value

return render(request, 'detail.html', {'school_display' : school_display})

Я рад, что сделал это, потому что в процессе обнаружил вызовы двух других методов объекта модели, которые в идеале принадлежали бы views.py, и также переместил их.

Рад узнать, можно ли улучшить описанный выше подход.

person Anupam    schedule 11.05.2017

Если вы используете шаблонный фильтр, будет совершенно очевидно, как он должен выглядеть:

from django import template
register = template.Library()

@register.filter
def is_not_empty(field):
    try:
        return field.count()
    except (AttributeError, TypeError):
        return field
person Philipp Zedler    schedule 10.05.2017