Reputation: 1225
I have a model for coffee beans that I send over to my HTML
pages and display the objects' data. Currently, I have a solution that requires me to update my HTML
code if I modify my Bean
object.
I want to the remove coupling from my program by iterating over the attributes associated with the object as opposed to hard-coding each attribute into the HTML
.
My current (outdated) solution:
# Bean model defined using Flask-SQLAlchemy
class Bean(db.Model):
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(20), index=True, nullable=False)
...
<!-- some_page.html -->
<table class="table">
<thead>
<tr>
<th scope="col">#</th>
<th scope="col">Name</th>
...
</tr>
</thead>
<tbody>
{% for bean in beans %}
{% include '_bean_info.html' %}
{% endfor %}
</tbody>
</table>
<!-- _bean_info.html -->
<tr>
<th scope="col">{{ bean.id }}</th>
<th scope="col">{{ bean.name }}</th>
...
</tr>
What I am trying to do:
<!-- _bean_info.html -->
<tr>
{% for each b in bean %}
<th scope="col">
{{ bean.b }} <!-- where 'b' is an attribute, e.g. 'name' -->
</th>
</tr>
Upvotes: 0
Views: 2665
Reputation: 311605
Depending on what your data structure looks like, you may be able to take advantage of the fact that the attributes of an object are available via the __dict__
attribute...in other words, if we have data like this:
from dataclasses import dataclass
@dataclass
class Bean:
id: str
name: str
beans = [
Bean(id=1, name="Guatemalan"),
Bean(id=2, name="Peruvian"),
Bean(id=3, name="Hawaiian"),
]
Then we can use that in a template like this:
import jinja2
t = jinja2.Template('''
<table>
{% for bean in beans %}
<tr>
{% for attr in bean.__dict__.keys() -%}
<td>{{ attr }}</td><td>{{ bean[attr] }}</td>
{% endfor -%}
</tr>
{% endfor %}
</table>
''')
print(t.render(beans=beans))
Put together, the examples above will produce:
<table>
<tr>
<td>id</td><td>1</td>
<td>name</td><td>Guatemalan</td>
</tr>
<tr>
<td>id</td><td>2</td>
<td>name</td><td>Peruvian</td>
</tr>
<tr>
<td>id</td><td>3</td>
<td>name</td><td>Hawaiian</td>
</tr>
</table>
...which is I think what you want.
In this particular situation, I would probably explicitly convert my model into a dict
, because this makes the template a little less magical:
from dataclasses import dataclass, asdict
...
t = jinja2.Template('''
<table>
{% for bean in beans %}
<tr>
{% for attr in bean.keys() -%}
<td>{{ attr }}</td><td>{{ bean[attr] }}</td>
{% endfor -%}
</tr>
{% endfor %}
</table>
''')
print(t.render(beans=(asdict(bean) for bean in beans)))
Upvotes: 1