plumdog / flask_table

Because writing HTML is fiddly and all of your tables are basically the same

Home Page:http://flask-table.readthedocs.org/

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

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:

image

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.

image
image

result:
image

The same seems to be using wtforms:
image

result:
image

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.