Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Flask-admin how to add button beside rows

I make a tuition payment app using Flask and using Flask-admin to managing the payment.

Flask-Admin automatically generated rows of tables by the SQLAlchemy that I declared on my models.py

Here is the image of my table: enter image description here Now I want to give a button beside the every total bill number for checkout purpose the bill.

I know how to add columns with column_list method that provide by ModelView, as I try like this code below:

column_list = ('student_id', 'total_bill', 'Pay Now')

and it will give the view display like this: enter image description here Now I want column Pay Now have a button for every that rows like I code manually with HTML like this: enter image description here There is have checkout button for every rows, as I mentioned above this purpose is for the checkout button.

So, how to do that..?, any help will be much appreciated

like image 718
Tri Avatar asked Dec 03 '22 10:12

Tri


1 Answers

You can use column_formatters to render the column whichever way you wish.

Example code on Github, flask-admin-row-form.

For example, each row "Checkout" button could be rendered as a HTML form with a submit button and the student id rendered as a hidden field within the form.

Simple example below (Python 2.7), files all reside in the root directory. views.py contains the important code, the rest is straightforward Flask stuff.

enter image description here

Class StudentView defines a method _format_pay_now which either renders a string "Paid" if the model's is_paid value is True or otherwise a HTML form.

Class StudentView also exposes a route '/admin/student/checkout' via the method checkout_view to process the submitted form. In this particular instance the is_paid column is set True and the list view re-rendered.

views.py

from flask import redirect, flash, url_for
from flask_admin import expose
from flask_admin.contrib import sqla
from flask_admin.helpers import get_form_data
from flask_admin.babel import gettext
from markupsafe import Markup


class StudentView(sqla.ModelView):

    page_size = 5

    column_list = ('id', 'cost', 'Pay Now')
    column_editable_list = ['cost']

    # override the column labels
    column_labels = {
        'id': 'Student ID',
        'cost': 'Total Bill',
    }

    def _format_pay_now(view, context, model, name):

        if model.is_paid:
            return 'Paid'

        # render a form with a submit button for student, include a hidden field for the student id
        # note how checkout_view method is exposed as a route below
        checkout_url = url_for('.checkout_view')

        _html = '''
            <form action="{checkout_url}" method="POST">
                <input id="student_id" name="student_id"  type="hidden" value="{student_id}">
                <button type='submit'>Checkout</button>
            </form
        '''.format(checkout_url=checkout_url, student_id=model.id)

        return Markup(_html)

    column_formatters = {
        'Pay Now': _format_pay_now
    }

    @expose('checkout', methods=['POST'])
    def checkout_view(self):

        return_url = self.get_url('.index_view')

        form = get_form_data()

        if not form:
            flash(gettext('Could not get form from request.'), 'error')
            return redirect(return_url)

        # Form is an ImmutableMultiDict
        student_id = form['student_id']

        # Get the model from the database
        model = self.get_one(student_id)

        if model is None:
            flash(gettext('Student not not found.'), 'error')
            return redirect(return_url)

        # process the model
        model.is_paid = True

        try:
            self.session.commit()
            flash(gettext('Student, ID: {student_id}, set as paid'.format(student_id=student_id)))
        except Exception as ex:
            if not self.handle_view_exception(ex):
                raise

            flash(gettext('Failed to set student, ID: {student_id}, as paid'.format(student_id=student_id), error=str(ex)), 'error')

        return redirect(return_url)

models.py

from flask_sqlalchemy import SQLAlchemy


db = SQLAlchemy()


class Student(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    cost = db.Column(db.Integer(), nullable=False)
    is_paid = db.Column(db.Boolean(), nullable=False)

    def __str__(self):
        return unicode(self).encode('utf-8')

    def __unicode__(self):
        return "ID: {id}; Cost : {cost}".format(id=self.id, cost=self.cost)

commands.py

Use flask create-database to generate the SQLite database.

import random
from flask.cli import click, with_appcontext
from models import db, Student


@click.command('create-database')
@with_appcontext
def create_database():

    # Create 100 students

    db.drop_all()
    db.create_all()

    for _ in range(0, 100):
        _project = Student(
            cost=random.randrange(10, 200),
            is_paid=False
        )
        db.session.add(_project)

    db.session.commit()

app.py

from flask import Flask
from flask_admin import Admin
from models import db, Student
from commands import create_database

app = Flask(__name__)

# Create dummy secrey key so we can use sessions
app.config['SECRET_KEY'] = '123456790'
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = True

# Create in-memory database
app.config['DATABASE_FILE'] = 'sample_db.sqlite'
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///' + app.config['DATABASE_FILE']
db.init_app(app)

app.cli.add_command(create_database)


# Flask views
@app.route('/')
def index():
    return '<a href="/admin/">Click me to get to Admin!</a>'


from views import StudentView

admin = Admin(app, template_mode="bootstrap3")
admin.add_view(StudentView(Student, db.session))


if __name__ == '__main__':
    app.run()

requirements.txt

Click==7.0
enum34==1.1.6
Flask==1.0.2
Flask-Admin==1.5.3
Flask-SQLAlchemy==2.3.2
itsdangerous==1.1.0
Jinja2==2.10
MarkupSafe==1.1.0
SQLAlchemy==1.2.17
Werkzeug==0.14.1
WTForms==2.2.1
like image 65
pjcunningham Avatar answered Dec 21 '22 10:12

pjcunningham