I have a Model
with numerous attributes; in the template rendered by my DetailView
I want to show only the attributes that are not None
.
This can be easily done with an if
template tag that checks if the value of the attribute is not None
, tho I should add an if condition for every attribute in my model, and there's a lot of them.
I would like to iterate through all the attributes and if not None
display them.
In pseudo code it would look like this:
{% for attribute in my_instance.all_attributes %}
{% if attribute.value is not None %}
{{attribute.value}}
I can get a tuple of concrete attributes both through the class or the instance:
cls._meta.concrete_fields
or
self._meta.concrete_fields
Now that I have the my_instance.all_attributes
in my pseudo code example, I can iterate it but I don't know how to get the actual value of the instance's attribute.
EDIT:
.concrete_values
returns an array of Field
instances, it looks like this:
(<django.db.models.fields.BooleanField: gov>, <django.db.models.fields.BooleanField: in_group>, <django.db.models.fields.CharField: legal_class>,)
I can access the value of the name attribute of the Field
instance using .name
. Calling .name
on the example above would return 'gov'
, 'in_group'
, 'legal_class'
CodePudding user response:
The authors of Django went out of their way to make sure that the template language isn't used to do things like this! Their opinion as I understand it is that templates should do formatting, and Python should do program logic.
There are two ways around it. One is to create a structure that the template can iterate through, and pass it to the DetailView context. Something like
def get_context_data(self):
data = super().get_context_data(**kwargs)
fieldnames = [
x.name for x in self.object._meta.concrete_fields ]
display_fields = [
(name, getattr( self.object, name, None)) for name in fieldnames ]
display_fields = [ x for x in display_fields if x[1] is not None ]
data['display_fields'] = display_fields
return data
and in the template you can now do
{% for name,value in display_fields %}
You might prefer to code a list of names instead of using ._meta.concrete_fields
because that lets you choose the order in which they appear. Or you could start with an ordered list and append anything that's in the _meta but not yet in your list (and delete anything that's in your list but not in _meta)
The other way is to use Jinja as the template engine for this view.