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

Example using SQLAlchemy?

Osiris1975 opened this issue · comments

Hi,

Would you be able to write up an example where you create a table from an sqlalchemy table or query_set?

Yeah, that's not a bad idea. I'll write up a proper example.

To get you going in the meantime, it should be fairly straightforward - you just have a to give the table an iterable of objects.

# So if your model looks a bit like:
class User(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    username = db.Column(db.String(80), unique=True)
    email = db.Column(db.String(120), unique=True)

# Then maybe your table would look like:
class UserTable(Table):
    username = Col('Username')
    email = Col('Email')

# then
table = UserTable(User.query.all())  # maybe with filtering or ordering

# then in your template
{{ table }}

Okay, I will try it out and if it works I am happy to report back my code as an example. Thanks for responding!

That would be super useful! Hope my quick example above is vaguely useful, too.

Here's what I have so far. I've replaced the code not relevant to flask-table with ellipses. For now, I'm only using flask-table when request method is GET. You'll notice I wasn't able to pass my query_set exactly as I wanted, mainly because I want to paginate my table. So, this code works, but only if I don't pass a paginated query. Any plans to support paginated tables?

from flask import render_template, request
from flask_sqlalchemy import SQLAlchemy
from submission_viewer import app
import flask_excel as excel
import os
import logging
import datetime
from flask_table import Table, Col
...
class Submissions(db.Model):
    spuid = db.Column(db.Integer, unique=True, primary_key=True)
    spuid_date = db.Column(db.String)
    g_number = db.Column(db.String(6))
    accession = db.Column(db.String(20))
    bioproject = db.Column(db.String(12))
    biosample = db.Column(db.String(12))
    release_date = db.Column(db.String)
    ncbi_submission_id = db.Column(db.String(9))
    submission_status = db.Column(db.String(20))
    response_message = db.Column(db.String(300))
    response_severity = db.Column(db.String(20))
    read_file = db.Column(db.String(100))
    temp_path = db.Column(db.String(200))


class SubTable(Table):
    spuid = Col('spuid')
    spuid_date = Col('spuid_date')
    g_number = Col('g_number')
    accession = Col('accession')
    bioproject = Col('bioproject')
    biosample = Col('biosample')
    release_date = Col('release_date')
    ncbi_submission_id = Col('ncbi_submission_id')
    submission_status = Col('submission_status')
    response_message = Col('response_message')
    response_severity = Col('response_severity')
    read_file = Col('read_file')
    temp_path = Col('temp_path')


@app.route('/', methods=['GET', 'POST'])
@app.route('/index', methods=['GET', 'POST'])
@app.route('/index/<int:page>', methods=['GET', 'POST'])
def index(page=1):
    logger.info(request.method)
    submissions = Submissions.query.order_by(Submissions.spuid.desc())
    if request.method == 'POST':
...
    else:
        # we are visiting the page for first time this session.
        table = SubTable(submissions.paginate(page=page, per_page=ROWS_PER_PAGE))
        return render_template('index.html', submissions=table,
                               headers=fields)
        #return render_template('index.html', submissions=submissions.paginate(page=page, per_page=ROWS_PER_PAGE),
        #                       headers=fields)

You need to pass pagination.items to the table constructor.

page=1
per_page=20

pagination = Submissions.query.order_by(Submissions.spuid.desc()).paginate(page, per_page)
table = SubTable(pagination.items)

Paul.

Tried that before (look in the else block at end of my code snippet that I commented out), it results in TypeError: 'Pagination' object is not iterable

You need to pass the items collection not the pagination object itself.

Ah okay, thanks! Got it. This does break the way I was handling page navigation, so guess I'll need to figure something else out for that.

@Osiris1975 You may need to pass the pagination object's items to the table class, then pass the pagination object and the table to your template. It shouldn't upset anything with page navigation.

Eg:

pagination = submissions.paginate(page=page, per_page=ROWS_PER_PAGE)
table = SubTable(pagination.items)
return render_template(
    'index.html',
    submissions_table=table,
    pagination=pagination)

Then you have access to things like the current page, and has_next/has_prev etc in your template.

(Relatedly, I would be concerned based on your example above about sometimes passing submissions=[[Table instance]] and sometimes passing submissions=[[list of models]] to your index.html template. Much nicer to be consistent (up to duck-typing) in what you're passing.)

As for your query about paginated tables, because you can do the code as above, I have no intention of adding anything for this. Also, because flask_table is just about taking an iterable of some objects and turning them into an html table containing exactly those objects. No filtering, no sorting, no slicing, etc. All of that logic belongs in your view, not in the table.

@plumdog thanks for the advice! A bit of a novice here so I really appreciate it. I will try your recommendation for handling the pagination object as well.

@Osiris1975 No problem! Happy to help, and let me know if you have any​ other questions.

So I do have the table loading now after doing this:

        pagination = submissions.paginate(page=page, per_page=ROWS_PER_PAGE)
        table = SubTable(pagination.items)
        return render_template('index.html', submissions=table,
                              headers=fields)

but I have two issues. The first is it doesn't actually seem to sort, even though I do see the headers are links now. Here's my SubTable class:

class SubTable(Table):
    spuid = Col('spuid')
    spuid_date = Col('spuid_date')
    g_number = Col('g_number')
    accession = Col('accession')
    bioproject = Col('bioproject')
    biosample = Col('biosample')
    release_date = Col('release_date')
    ncbi_submission_id = Col('ncbi_submission_id')
    submission_status = Col('submission_status')
    response_message = Col('response_message')
    response_severity = Col('response_severity')
    read_file = Col('read_file')
    temp_path = Col('temp_path')
    allow_sort = True

    def sort_url(self, col_key, reverse=False):
        if reverse:
            direction = 'desc'
        else:
            direction = 'asc'
        return url_for('index', sort=col_key, direction=direction)

The second issue is the pagination links I made in the HTML don't work. I think the issue is that we are now passing the table object which contains the items of the paginate object, but not the paginate object itself, which contains the has_next and has_previous booleans. So the conditional code in the HTML that checks if submissions.has_prev always evaluates to false.

@Osiris1975 For the sorting: there's no code I can see in your view to do the sorting. Take a look at the sortable.py example. You have to do the sorting of the items yourself. All the table class does for sorting is to handle the display of the sorting links in the headers.

And for the pagination, the table doesn't know anything about pagination, that's why in my snippet before both the table and the pagination object get passed to the template. Then you can use the pagination object for pagination things, and just {{ submissions_table }} for the table.

Hope that helps!

I've finally got around to adding an example using SQLAlchemy, so I'm happy to close this issue now.