RawCol using Jinja2 statements
elgaucho79 opened this issue · comments
Hi Andrew,
I am trying to implement a select2 box for each row into my table. The select2 box is using Jinja2 statements with values provided by the python app itself:
so, in my table definition I copied the select box coding into a RawCol for this purpose and it seems the table is not able to render this in the right order or he tries to instantiate the select box already by the time of table creation and not at runtime.
The same seems to be using wtforms:
any idea how to treat this best?
regards Michael
I'd suggest that you do this by subclassing Col
and returning a rendered template.
For example:
from flask_table import Table, Col
from flask import Flask, render_template_string
app = Flask(__name__)
HTML_STRING = '''
<html>
<body>
{{ table }}
</body>
</html>
'''
SELECT_TEMPLATE_STRING = '''
<select>
{% for value, display in options %}
<option value="{{ value }}">{{ display }}</option>
{% endfor %}
</select>
'''
class SelectCol(Col):
def __init__(self, *args, **kwargs):
self.options = kwargs.pop('options')
super(SelectCol, self).__init__(*args, **kwargs)
def td_contents(self, item, attr_list):
return render_template_string(
SELECT_TEMPLATE_STRING,
options=self.options)
SELECT_OPTIONS = [
('a', 'AAA'),
('b', 'BBB'),
]
class MyTable(Table):
name = Col('Name')
select = SelectCol('Select', options=SELECT_OPTIONS)
@app.route('/')
def index():
items = [
dict(name='name1'),
dict(name='name2'),
]
table = MyTable(items)
return render_template_string(HTML_STRING, table=table)
if __name__ == '__main__':
app.run(debug=True)
I'm using render_template_string
rather than separate template files, just because it makes for a simpler example.
Hopefully that helps you get a <select>
into a <td>
. If there's more to what you're trying to achieve, perhaps you could copy+paste a self-contained example, if possible.
Thanks Andrew,
works well so far. I may come back with other things soon. :-)
regards
Michael
Hi Andrew,
in order to work with the selected options in the subclass, I need unique identifiers/name for the html select element for each row in flask_table. Such as
<select id=01, name=whatever>...
So, I wonder if there is a way to define unique 'id' and 'name' for each table row I create.
Thanks
Michael
Hi,
There's a few option here, depending on exactly what it is that you're looking to achieve.
Option 1
Override get_tr_attrs
in the table class.
from flask_table.columns import Col
from flask_table.table import Table
class MyTable(Table):
name = Col('Name')
def get_tr_attrs(self, item):
return {'id': item['id']}
items = [
dict(name='name1', id=1),
dict(name='name2', id=2),
]
print(MyTable(items).__html__())
Benefits: straightforward. Drawbacks: you have to modify the data you're passing in.
Option 2
Override tbody
and tr
in the table class. (See https://github.com/plumdog/flask_table/blob/v0.5.0/flask_table/table.py#L129-L146)
from flask_table.columns import Col
from flask_table.table import Table
from flask_table.html import element
class MyTable(Table):
name = Col('Name')
def tr(self, item, i):
# Changed this method to take i
content = ''.join(c.td(item, attr) for attr, c in self._cols.items()
if c.show)
attrs = self.get_tr_attrs(item)
# Add id=i to the attrs for the tr
attrs['id'] = i
return element(
'tr',
attrs=attrs,
content=content,
escape_content=False)
def tbody(self):
# Changed this to use enumerate
out = [self.tr(item, i) for i, item in enumerate(self.items)]
if not out:
return ''
content = '\n{}\n'.format('\n'.join(out))
return element('tbody', content=content, escape_content=False)
items = [
dict(name='name1'),
dict(name='name2'),
]
print(MyTable(items).__html__())
Benefits: no change to input data. Drawbacks: lots of overriding of methods.
Option 3
Wrap your input data and modify your table columns accordingly, and override get_tr_attrs
.
from flask_table.columns import Col
from flask_table.table import Table
class MyTable(Table):
# Item is now wrapped in a dict
name = Col('Name', attr='item.name')
def get_tr_attrs(self, item):
return {'id': item['id']}
items = [
dict(name='name1'),
dict(name='name2'),
]
wrapped_items = [dict(item=item, id=i) for i, item in enumerate(items)]
print(MyTable(wrapped_items).__html__())
Benefits: just wrapping input data, which is quite tidy. Drawbacks: have to adjust the attrs for your columns.
Ultimately, these all produce the same HTML (I think!). Which one works best will depend on exactly what you're doing. I think that option 3 would likely be my preferred one, because modifying your data for the sake of presentation (option 1) is a bit ugly, but wrapping it is much nicer. And option 2 just involves too much overriding and copying of code.
Ok, your solution seems working fine.
Thank you for help :)
Thanks. All 3 working fine. I also prefer solution 3. will take some to implement it in the way I need. Thank you very much.