removed all old *_lst tables, added corresponding new classes, etc. fro covertype, owned, rating, and dropped tables from DB, etc. Updated base.html to use new tables as drop-downs that are set correctly. So far slight hack on BookForm, will finish that after syncing this all back to mara. If I do the sync, and export/import this version of DB, then the fixes.sql and fix_db() code included in main.py can be removed. Finally, lamely added a favicon and a static/ to support it

This commit is contained in:
Damien De Paoli
2020-11-17 21:22:15 +11:00
parent 8eddce043b
commit 9544790ffa
16 changed files with 466 additions and 47 deletions

2
README
View File

@@ -14,7 +14,7 @@ sudo apt install python3-pip python3-psycopg2 libpq-dev
pip3 install --user flask sqlalchemy flask-sqlalchemy flask-marshmallow SQLAlchemy-serializer flask-wtf flask-bootstrap marshmallow-sqlalchemy
### alter db that was saved by:
### fix up db changes by running those in fixes.sql
psql library
alter table genre_lst rename to genre;
alter table genre rename COLUMN genre to name;

View File

@@ -36,8 +36,6 @@ class ConditionForm(Form):
@app.route("/conditions", methods=["GET"])
def conditions():
conditions = Condition.query.order_by('id').all()
print ( Condition.query.order_by('id') )
print ( conditions )
return render_template("conditions.html", conditions=conditions)
################################################################################

66
covertype.py Normal file
View File

@@ -0,0 +1,66 @@
from wtforms import SubmitField, StringField, HiddenField, SelectField, validators, Form
from flask import request, render_template
from __main__ import db, app, ma
################################################################################
# Class describing Covertype in the database, and via sqlalchemy, connected to the DB as well
################################################################################
class Covertype(db.Model):
id = db.Column(db.Integer, unique=True, nullable=False, primary_key=True)
name = db.Column(db.String(50), unique=False, nullable=False)
def __repr__(self):
return "<id: {}, name: {}>".format(self.id,self.name)
################################################################################
# Helper class that inherits a .dump() method to turn class Covertype into json / useful in jinja2
################################################################################
class CovertypeSchema(ma.SQLAlchemyAutoSchema):
class Meta: model = Covertype
################################################################################
# Helper class that defines a form for covertype, used to make html <form>, with field validation (via wtforms)
################################################################################
class CovertypeForm(Form):
id = HiddenField()
name = StringField('Name:', [validators.DataRequired()])
submit = SubmitField('Save' )
delete = SubmitField('Delete' )
################################################################################
# Routes for covertype data
#
# /covertypes -> GET only -> prints out list of all covertypes
################################################################################
@app.route("/covertypes", methods=["GET"])
def covertypes():
covertypes = Covertype.query.order_by('id').all()
return render_template("covertypes.html", covertypes=covertypes)
################################################################################
# /covertype/<id> -> GET/POST(save or delete) -> shows/edits/delets a single
# covertype
################################################################################
@app.route("/covertype/<id>", methods=["GET", "POST"])
def covertype(id):
### DDP: should this be request.form or request.values?
alert="Success"
covertype_form = CovertypeForm(request.form)
if request.method == 'POST' and covertype_form.validate():
id = request.form['id']
covertype = Covertype.query.get(id)
try:
request.form['submit']
except:
message="Sorry, Deleting unsupported at present"
alert="Danger"
else:
covertype.name = request.form['name']
db.session.commit()
message="Successfully Updated Covertype (id={})".format(id)
else:
covertype = Covertype.query.get(id)
covertype_form = CovertypeForm(request.values, obj=covertype)
message=""
return render_template("covertype.html", covertype=covertype, alert=alert, message=message, covertype_form=covertype_form)

View File

@@ -1,10 +1,79 @@
ALTER TABLE genre_lst RENAME TO genre;
ALTER TABLE genre RENAME COLUMN genre TO name;
CREATE TABLE condition (
id INTEGER,
name VARCHAR(20) not null,
constraint pk_condition primary key(id)
id INTEGER,
name VARCHAR(20) not null,
constraint pk_condition primary key(id)
);
INSERT INTO condition VALUES ( 1, 'Good' );
INSERT INTO condition VALUES ( 2, 'Average' );
INSERT INTO condition VALUES ( 3, 'Needs Replacing' );
INSERT INTO condition VALUES ( 4, 'N/A' );
ALTER TABLE book ADD COLUMN temp_cond integer , ADD CONSTRAINT fk_book_condition_new foreign key(temp_cond) REFERENCES condition(id)
CREATE TABLE covertype (
id INTEGER,
name VARCHAR(20) not null,
constraint pk_covertype primary key(id)
);
INSERT INTO covertype VALUES ( 1, 'Paperback' );
INSERT INTO covertype VALUES ( 2, 'Over-size Paperback' );
INSERT INTO covertype VALUES ( 3, 'Hardcover' );
INSERT INTO covertype VALUES ( 4, 'N/A' );
ALTER TABLE book ADD COLUMN temp_cover integer , ADD CONSTRAINT fk_book_covertype_new foreign key(temp_cover) REFERENCES covertype(id)
CREATE TABLE owned (
id INTEGER,
name VARCHAR(20) not null,
constraint pk_owned primary key(id)
);
INSERT INTO owned VALUES ( 1, 'Currently Owned' );
INSERT INTO owned VALUES ( 2, 'On Wish List' );
INSERT INTO owned VALUES ( 3, 'Sold' );
ALTER TABLE book ADD COLUMN temp_owned integer , ADD CONSTRAINT fk_book_owned_new foreign key(temp_owned) REFERENCES owned(id);
CREATE TABLE rating (
id INTEGER,
name VARCHAR(20) not null,
constraint pk_rating primary key(id)
);
INSERT INTO rating VALUES ( 1, '10' );
INSERT INTO rating VALUES ( 2, '9' );
INSERT INTO rating VALUES ( 3, '8' );
INSERT INTO rating VALUES ( 4, '7' );
INSERT INTO rating VALUES ( 5, '6' );
INSERT INTO rating VALUES ( 6, '5' );
INSERT INTO rating VALUES ( 7, '4' );
INSERT INTO rating VALUES ( 8, '3' );
INSERT INTO rating VALUES ( 9, '2' );
INSERT INTO rating VALUES ( 10, '1' );
INSERT INTO rating VALUES ( 11, 'N/A' );
INSERT INTO rating VALUES ( 12, 'Undefined' );
ALTER TABLE book ADD COLUMN temp_rating integer, ADD CONSTRAINT fk_book_rating_new foreign key(temp_rating) REFERENCES rating(id);
### fore each new table (e.g do above), then
# add appropriate new field to Book class definition, eg for condition:
temp_cond = db.Column(db.Integer)
# then run python code with fix_db() -- which fixes appropriate Book.<new_field>, then:
ALTER TABLE book DROP COLUMN condition;
ALTER TABLE book RENAME COLUMN temp_cond TO condition;
ALTER TABLE book DROP COLUMN covertype;
ALTER TABLE book RENAME COLUMN temp_cover TO covertype;
ALTER TABLE book DROP COLUMN owned;
ALTER TABLE book RENAME COLUMN temp_owned TO owned;
ALTER TABLE book DROP COLUMN rating;
ALTER TABLE book RENAME COLUMN temp_rating TO rating;
DROP TABLE condition_lst
DROP TABLE covertype_lst
DROP TABLE owned_lst
DROP TABLE rating_lst
# then modify main.py again, to delete condition and rename temp_<field> to <feild> in class Book

42
main.py
View File

@@ -1,10 +1,9 @@
from flask import Flask
from flask import render_template
from flask import request
from flask import Flask, render_template, request
from flask_sqlalchemy import SQLAlchemy
from flask_marshmallow import Marshmallow
from flask_bootstrap import Bootstrap
from wtforms import SubmitField, StringField, HiddenField, SelectField, validators, Form
from flask_wtf import FlaskForm
app = Flask(__name__)
### what is this value? I gather I should chagne it?
@@ -20,6 +19,9 @@ from author import Author, AuthorForm, AuthorSchema
from publisher import Publisher, PublisherForm, PublisherSchema
from genre import Genre, GenreForm, GenreSchema
from condition import Condition, ConditionForm, ConditionSchema
from covertype import Covertype, CovertypeForm, CovertypeSchema
from owned import Owned, OwnedForm, OwnedSchema
from rating import Rating, RatingForm, RatingSchema
####################################### CLASSES / DB model #######################################
book_author_link = db.Table('book_author_link', db.Model.metadata,
@@ -53,10 +55,10 @@ class Book(db.Model):
publisher = db.relationship('Publisher', secondary=book_publisher_link)
genre = db.relationship('Genre', secondary=book_genre_link )
year_published = db.Column(db.Integer)
owned = db.Column(db.String(20))
covertype = db.Column(db.String(20))
condition = db.Column(db.String(20))
rating = db.Column(db.String(20))
condition = db.Column(db.Integer, db.ForeignKey('condition.id'))
covertype = db.Column(db.Integer, db.ForeignKey('covertype.id'))
owned = db.Column(db.Integer, db.ForeignKey('owned.id'))
rating = db.Column(db.Integer, db.ForeignKey('rating.id'))
notes = db.Column(db.Text)
blurb = db.Column(db.Text)
created = db.Column(db.Date)
@@ -85,12 +87,15 @@ class BookSchema(ma.SQLAlchemyAutoSchema):
#
# To be completed
#
class BookForm(Form):
class BookForm(FlaskForm):
# I think I'll have to skip setting default on create, and using jquery to
# change it when I create the from? (or maybe I could use a default=set_me
# in the line below, then when I set create set_me = book.condition before
# bf=BookForm()
condition = SelectField( 'condition', choices=[(c.id, c.name) for c in Condition.query.order_by('id')] )
covertype = SelectField( 'covertype', choices=[(c.id, c.name) for c in Covertype.query.order_by('id')] )
owned = SelectField( 'owned', choices=[(c.id, c.name) for c in Owned.query.order_by('id')] )
rating = SelectField( 'rating', choices=[(c.id, c.name) for c in Rating.query.order_by('id')] )
### DDP: do I need many=True on Author as books have many authors? (or in BookSchema declaration above?)
@@ -136,11 +141,30 @@ def book(id):
book_s['sub_book'] = sub_book
return render_template("book.html", books=book_s, subs=sub_book )
book_form=BookForm(request.form)
# set defaults for drop-down's based on this book
book_form.condition.default = book.condition
book_form.covertype.default = book.covertype
book_form.owned.default = book.owned
book_form.rating.default = book.rating
print(book_form)
book_form.process()
return render_template("book.html", books=book_s, subs=sub_book, book_form=book_form )
@app.route("/", methods=["GET"])
def main_page():
return render_template("base.html")
def fix_db():
books = Book.query.all()
for book in books:
rating=Rating.query.filter_by(name=book.rating).all()
book.temp_rating=rating[0].id
print(book.temp_rating)
db.session.commit()
print( "ran fix_db()")
if __name__ == "__main__":
# fix_db()
app.run(host="0.0.0.0", debug=True)

66
owned.py Normal file
View File

@@ -0,0 +1,66 @@
from wtforms import SubmitField, StringField, HiddenField, SelectField, validators, Form
from flask import request, render_template
from __main__ import db, app, ma
################################################################################
# Class describing Owned in the database, and via sqlalchemy, connected to the DB as well
################################################################################
class Owned(db.Model):
id = db.Column(db.Integer, unique=True, nullable=False, primary_key=True)
name = db.Column(db.String(50), unique=False, nullable=False)
def __repr__(self):
return "<id: {}, name: {}>".format(self.id,self.name)
################################################################################
# Helper class that inherits a .dump() method to turn class Owned into json / useful in jinja2
################################################################################
class OwnedSchema(ma.SQLAlchemyAutoSchema):
class Meta: model = Owned
################################################################################
# Helper class that defines a form for owned, used to make html <form>, with field validation (via wtforms)
################################################################################
class OwnedForm(Form):
id = HiddenField()
name = StringField('Name:', [validators.DataRequired()])
submit = SubmitField('Save' )
delete = SubmitField('Delete' )
################################################################################
# Routes for owned data
#
# /owneds -> GET only -> prints out list of all owneds
################################################################################
@app.route("/owneds", methods=["GET"])
def owneds():
owneds = Owned.query.order_by('id').all()
return render_template("owneds.html", owneds=owneds)
################################################################################
# /owned/<id> -> GET/POST(save or delete) -> shows/edits/delets a single
# owned
################################################################################
@app.route("/owned/<id>", methods=["GET", "POST"])
def owned(id):
### DDP: should this be request.form or request.values?
alert="Success"
owned_form = OwnedForm(request.form)
if request.method == 'POST' and owned_form.validate():
id = request.form['id']
owned = Owned.query.get(id)
try:
request.form['submit']
except:
message="Sorry, Deleting unsupported at present"
alert="Danger"
else:
owned.name = request.form['name']
db.session.commit()
message="Successfully Updated Owned (id={})".format(id)
else:
owned = Owned.query.get(id)
owned_form = OwnedForm(request.values, obj=owned)
message=""
return render_template("owned.html", owned=owned, alert=alert, message=message, owned_form=owned_form)

66
rating.py Normal file
View File

@@ -0,0 +1,66 @@
from wtforms import SubmitField, StringField, HiddenField, SelectField, validators, Form
from flask import request, render_template
from __main__ import db, app, ma
################################################################################
# Class describing Rating in the database, and via sqlalchemy, connected to the DB as well
################################################################################
class Rating(db.Model):
id = db.Column(db.Integer, unique=True, nullable=False, primary_key=True)
name = db.Column(db.String(50), unique=False, nullable=False)
def __repr__(self):
return "<id: {}, name: {}>".format(self.id,self.name)
################################################################################
# Helper class that inherits a .dump() method to turn class Rating into json / useful in jinja2
################################################################################
class RatingSchema(ma.SQLAlchemyAutoSchema):
class Meta: model = Rating
################################################################################
# Helper class that defines a form for rating, used to make html <form>, with field validation (via wtforms)
################################################################################
class RatingForm(Form):
id = HiddenField()
name = StringField('Name:', [validators.DataRequired()])
submit = SubmitField('Save' )
delete = SubmitField('Delete' )
################################################################################
# Routes for rating data
#
# /ratings -> GET only -> prints out list of all ratings
################################################################################
@app.route("/ratings", methods=["GET"])
def ratings():
ratings = Rating.query.order_by('id').all()
return render_template("ratings.html", ratings=ratings)
################################################################################
# /rating/<id> -> GET/POST(save or delete) -> shows/edits/delets a single
# rating
################################################################################
@app.route("/rating/<id>", methods=["GET", "POST"])
def rating(id):
### DDP: should this be request.form or request.values?
alert="Success"
rating_form = RatingForm(request.form)
if request.method == 'POST' and rating_form.validate():
id = request.form['id']
rating = Rating.query.get(id)
try:
request.form['submit']
except:
message="Sorry, Deleting unsupported at present"
alert="Danger"
else:
rating.name = request.form['name']
db.session.commit()
message="Successfully Updated Rating (id={})".format(id)
else:
rating = Rating.query.get(id)
rating_form = RatingForm(request.values, obj=rating)
message=""
return render_template("rating.html", rating=rating, alert=alert, message=message, rating_form=rating_form)

BIN
static/favicon.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

View File

@@ -8,6 +8,7 @@
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@4.5.3/dist/css/bootstrap.min.css" integrity="sha384-TX8t27EcRE3e/ihU7zmQxVncDAy5uIKz4rEkgIXeMed4M0jlfIDPvg6uqKI2xXr2" crossorigin="anonymous">
<link rel="stylesheet" href="https://unpkg.com/bootstrap-table@1.18.0/dist/bootstrap-table.min.css">
<link rel="stylesheet" href="https://cdn.datatables.net/1.10.22/css/dataTables.bootstrap4.min.css">
<link rel="shortcut icon" href="{{ url_for('static', filename='favicon.ico') }}">
<script src="https://kit.fontawesome.com/9b4c7cf470.js" crossorigin="anonymous"></script>
{% import "bootstrap/wtf.html" as wtf %}
</head>
@@ -48,8 +49,12 @@
<a class="dropdown-item" href="{{url_for('genres')}}">Show Genres</a>
<a class="dropdown-item" href="{{url_for('conditions')}}">Create new Condition</a>
<a class="dropdown-item" href="{{url_for('conditions')}}">Show Conditions</a>
<a class="dropdown-item" href="{{url_for('genres')}}">Create new Covertype</a>
<a class="dropdown-item" href="{{url_for('genres')}}">Show Covertypes</a>
<a class="dropdown-item" href="{{url_for('covertypes')}}">Create new Covertype</a>
<a class="dropdown-item" href="{{url_for('covertypes')}}">Show Covertypes</a>
<a class="dropdown-item" href="{{url_for('owneds')}}">Create new Owned Type</a>
<a class="dropdown-item" href="{{url_for('owneds')}}">Show Owned Types</a>
<a class="dropdown-item" href="{{url_for('ratings')}}">Create new Rating</a>
<a class="dropdown-item" href="{{url_for('ratings')}}">Show Owned Ratings</a>
</div>
</div>
</div>

View File

@@ -3,44 +3,52 @@
<h3><center>View/Edit Book</center></h1>
{% set keys = [ 'title', 'author', 'publisher', 'genre', 'owned', 'covertype', 'condition', 'year_published', 'rating', 'notes', 'blurb' ] %}
<div class="container">
<div class="row with-margin">
<div class="row">
<form class="form col-lg-12">
{% for key in keys %}
<div class="input-group input-group-lg">
{% if key == "condition" or key == "covertype" or key == "owned" or key == "rating" %}
<div class="form-row">
<label for="{{key}}" class="col-lg-2 col-form-label">{{key}}:</label>
<div class="col-lg-10">
{{book_form[key](class="form-control")}}
</div class="col-lg-10">
</div class="form-row">
{% else %}
<div class="form-row">
<label for="{{key}}" class="col-lg-2 col-form-label">{{key}}:</label>
<div class="col-lg-10">
<div class="row">
{% if books[key] is iterable and books[key] is not string %}
{% set cnt = namespace(idx=0, val=0) %}
{% for objects in books[key] %}
{% set cnt.val = 0 %}
{% set str = namespace(val="") %}
{% for attr in objects %}
{% if attr != "id" %}
{% if cnt.val > 0 %}
{% set str.val=str.val+", "+objects[attr] %}
{% else %}
{% set str.val=str.val+objects[attr] %}
{% if books[key] is iterable and books[key] is not string %}
<div class="row" style="margin-left:0px; margin-right:0px;">
{% set cnt = namespace(idx=0, val=0) %}
{% for objects in books[key] %}
{% set cnt.val = 0 %}
{% set str = namespace(val="") %}
{% for attr in objects %}
{% if attr != "id" %}
{% if cnt.val > 0 %}
{% set str.val=str.val+", "+objects[attr] %}
{% else %}
{% set str.val=str.val+objects[attr] %}
{% endif %}
{% set cnt.val = cnt.val + 1 %}
{% endif %}
{% set cnt.val = cnt.val + 1 %}
{% endif %}
{% endfor %}
<span class="form-control col-lg-{{((12/books[key]|length)|int)}}" id="{{key}}-{{books[key][cnt.idx].id}}">
<a href="{{url_for(key, id=books[key][cnt.idx].id)}}">{{str.val}}</a>
</span>
{% set cnt.idx = cnt.idx+1 %}
{% endfor %}
<span class="form-control col-lg-{{((12/books[key]|length)|int)}}" id="{{key}}-{{books[key][cnt.idx].id}}">
<a href="{{url_for(key, id=books[key][cnt.idx].id)}}">{{str.val}}</a>
</span>
{% set cnt.idx = cnt.idx+1 %}
{% endfor %}
</div>
</table>
{% else %}
{% if key == "notes" or key == "blurb" %}
<textarea rows="5" type="text" class="form-control input-lg" id="{{key}}">{{books[key]}}</textarea>
</div class="row">
{% else %}
<input type="text" class="form-control input-lg" id="{{key}}" value="{{books[key]}}">
{% if key == "notes" or key == "blurb" %}
<textarea rows="5" type="text" class="form-control input-lg" id="{{key}}">{{books[key]}}</textarea>
{% else %}
<input type="text" class="form-control input-lg" id="{{key}}" value="{{books[key]}}">
{% endif %}
{% endif %}
{% endif %}
</div class="col-lg-8">
</div class="form-group">
</div class="col-lg-10">
</div class="form-row">
{% endif %}
{% endfor %}
</form>
</div class="row">

24
templates/covertype.html Normal file
View File

@@ -0,0 +1,24 @@
{% extends "base.html" %} {% block main_content %}
<h3><center>Covertype</center></h3>
<div class="container">
<right>
{% if message|length %}
<div class="row alert alert-{{alert}}">
{{message}}
</div>
{% endif %}
<div class="row">
<form class="form form-inline col-xl-12" action="" method="POST">
{{ wtf.form_field( covertype_form.id, form_type='inline' ) }}
<div class="col-xl-12">
{{ wtf.form_field( covertype_form.name, form_type='horizontal', horizontal_columns=('xl', 2, 10), style="width:100%" ) }}
</div>
<div class="col-xl-12">
<br></br>
</div>
{{ wtf.form_field( covertype_form.submit, horizontal_columns=('xl', 2, 2), class="btn btn-primary offset-xl-1 col-xl-2" )}}
{{ wtf.form_field( covertype_form.delete, horizontal_columns=('xl', 2, 2), class="btn btn-danger offset-xl-1 col-xl-2" )}}
</form>
</div class="row">
</div class="container">
{% endblock main_content %}

15
templates/covertypes.html Normal file
View File

@@ -0,0 +1,15 @@
{% extends "base.html" %}
{% block main_content %}
<h3>Covertypes</h3>
<table id="book_table" class="table table-striped table-sm" data-toolbar="#toolbar" data-search="true">
<thead>
<tr class="thead-light"><th>Name</th></tr>
</thead>
<tbody>
{% for covertype in covertypes %}
<tr><td data-sort="{{covertype.id}}"><a href="{{url_for('covertype', id=covertype.id )}}">{{covertype.name}}</a></td></tr>
{% endfor %}
</tbody>
</table>
{% endblock main_content %}

24
templates/owned.html Normal file
View File

@@ -0,0 +1,24 @@
{% extends "base.html" %} {% block main_content %}
<h3><center>Owned</center></h3>
<div class="container">
<right>
{% if message|length %}
<div class="row alert alert-{{alert}}">
{{message}}
</div>
{% endif %}
<div class="row">
<form class="form form-inline col-xl-12" action="" method="POST">
{{ wtf.form_field( owned_form.id, form_type='inline' ) }}
<div class="col-xl-12">
{{ wtf.form_field( owned_form.name, form_type='horizontal', horizontal_columns=('xl', 2, 10), style="width:100%" ) }}
</div>
<div class="col-xl-12">
<br></br>
</div>
{{ wtf.form_field( owned_form.submit, horizontal_columns=('xl', 2, 2), class="btn btn-primary offset-xl-1 col-xl-2" )}}
{{ wtf.form_field( owned_form.delete, horizontal_columns=('xl', 2, 2), class="btn btn-danger offset-xl-1 col-xl-2" )}}
</form>
</div class="row">
</div class="container">
{% endblock main_content %}

15
templates/owneds.html Normal file
View File

@@ -0,0 +1,15 @@
{% extends "base.html" %}
{% block main_content %}
<h3>Owned List</h3>
<table id="book_table" class="table table-striped table-sm" data-toolbar="#toolbar" data-search="true">
<thead>
<tr class="thead-light"><th>Name</th></tr>
</thead>
<tbody>
{% for owned in owneds %}
<tr><td data-sort="{{owned.id}}"><a href="{{url_for('owned', id=owned.id )}}">{{owned.name}}</a></td></tr>
{% endfor %}
</tbody>
</table>
{% endblock main_content %}

24
templates/rating.html Normal file
View File

@@ -0,0 +1,24 @@
{% extends "base.html" %} {% block main_content %}
<h3><center>Rating</center></h3>
<div class="container">
<right>
{% if message|length %}
<div class="row alert alert-{{alert}}">
{{message}}
</div>
{% endif %}
<div class="row">
<form class="form form-inline col-xl-12" action="" method="POST">
{{ wtf.form_field( rating_form.id, form_type='inline' ) }}
<div class="col-xl-12">
{{ wtf.form_field( rating_form.name, form_type='horizontal', horizontal_columns=('xl', 2, 10), style="width:100%" ) }}
</div>
<div class="col-xl-12">
<br></br>
</div>
{{ wtf.form_field( rating_form.submit, horizontal_columns=('xl', 2, 2), class="btn btn-primary offset-xl-1 col-xl-2" )}}
{{ wtf.form_field( rating_form.delete, horizontal_columns=('xl', 2, 2), class="btn btn-danger offset-xl-1 col-xl-2" )}}
</form>
</div class="row">
</div class="container">
{% endblock main_content %}

15
templates/ratings.html Normal file
View File

@@ -0,0 +1,15 @@
{% extends "base.html" %}
{% block main_content %}
<h3>Ratings</h3>
<table id="book_table" class="table table-striped table-sm" data-toolbar="#toolbar" data-search="true">
<thead>
<tr class="thead-light"><th>Name</th></tr>
</thead>
<tbody>
{% for rating in ratings %}
<tr><td data-sort="{{rating.id}}"><a href="{{url_for('rating', id=rating.id )}}">{{rating.name}}</a></td></tr>
{% endfor %}
</tbody>
</table>
{% endblock main_content %}