What you’ll be making
You will be making a library management system. In this system, there will be librarians, patrons, and books.
- Books have a title and author.
- books can be borrowed.
- the library has an infinite supply of each book.
- Librarians are Users who can:
- add and remove books.
- add and remove other librarians.
- view what books a patron has borrowed.
- remove patron accounts.
- Patrons are Users who can:
- sign themselves up.
- view all the books.
- borrow an unlimited number of books.
- they can only borrow each book once, though.
Here’s a video demonstrating my implementation:
Your task
- Click the GitHub link in the project 3 announcement, and clone your proj3 repo like before.
- Go into that directory and run the Flask app there to test that it works (see below).
- Edit the Python and template code to make your app.
- And feel free to style it up with CSS any way you like.
The starting point
After cloning the starter repo, be sure to test that you have everything set up right:
cd
into theproj3
directoryexport FLASK_APP=proj3.py
- or on Windows,
set FLASK_APP=proj3.py
I guess? or use a proper POSIX shell on Windows
- or on Windows,
export FLASK_ENV=development
- this will make it automatically reload when you change your python code!
flask initdb
flask run
- Windows users: try
python -m flask run
instead.
- Windows users: try
- open the page in your browser (the link is printed out on the console)
- sign in as the owner (username
owner
, passwordpass
)
You have been given a few files:
.gitignore
is how you tell git to ignore certain files.- this means your database will not be included in your repository. this is fine!
app.py
contains the global app configuration and the Flaskapp
object.models.py
is where the database stuff lives.proj3.py
is where you’ll be doing most of your work.- the
templates
directory:base.html
is the base page layout that the other templates inherit.home.html
is where you’ll list the library books.login.html
andregister.html
are just what they say… you probably don’t need to change these.
- and
static/style.css
for styling the page.
User accounts
The starting code is based on the “Minitwit” example I gave you and showed in class. It already handles the ability for users to sign in, log out, and for patrons to sign themselves up.
In models.py
, the initdb
command creates a single user, owner
, with the password pass
.
The only way to create librarian accounts is for a logged-in librarian to create it. Librarians cannot sign themselves up.
Within your Python code and templates, the g.user
variable will be a User object containing the currently logged-in user, or it will be None
. This is set up by the before_request
function in proj3.py
.
You can therefore tell whether or not the current user is a librarian with g.user.librarian
.
/
(the home page)
This is the home.html
template, and the accompanying home()
function in proj3.py
.
This page will list the books in the library:
- List all the books in the library in an orderly way.
- by title? by author?
- for example, you can use
Book.query.order_by(Book.title).all()
- Use an HTML table to show the books.
- Your template code will use a
for
loop to loop over all the books.
- Your template code will use a
This page will also look a little different for each kind of visitor:
- If no user is logged in:
- Display a welcome message and some information about the (fake) library above the book listing.
- If a patron is logged in:
- Say something like “welcome back!” and explain how to borrow books.
- In the book listing, there should be some additional info for each book:
- Show whether or not they currently have it borrowed
- If they have it borrowed, put a button that will return it
- If they have not borrowed it, put a button that will borrow it
- See details about how to implement this below.
Borrowing and returning books
Since these actions change the state of the server, the buttons for these should send POST requests. However, you can’t make a POST request with a regular <a>
link.
Instead, you must put each button inside a <form>
element, and each button should be a <button type="submit">
element. The code for each button might look like:
<form method="post">
<button type="submit" name="book_id" value="2">Return this book</button>
</form>
- the form’s
method
attribute makes this a POST request. - the button must be a
submit
button. - each button’s
name
should bebook_id
. - each button’s
value
should be the ID of the book to borrow/return.
Your template will generate this code for each button in the listing.
Your home()
function in proj3.py
will detect POST requests, and if so, get the book ID by looking up request.form.get('book_id')
. (Have a look at the login()
function for an example on doing this. The get()
method will not crash if there is no book_id
, it will just give None.)
Last, you can change which books a user is borrowing with their .borrows
attribute:
someBook = # ...some code that gets a Book object...
g.user.borrows.append(someBook) # to borrow
g.user.borrows.remove(someBook) # to return
Hey, if you got this far, that means you’re done with the patron stuff, and have a 70%. Great!
Librarian mode
For librarians, you will add some extra navigation items to base.html
, inside the <nav>
’s <ul>
:
- a “Manage Books” link to
/books/
- an “Accounts” link to
/accounts/
- but be sure to use the
url_for('...')
pattern like the other links.- remember, it’s the name of the function that handles that page!
For the home page:
- Don’t have any special message, just the book listing.
- For each book,
- Make the book’s title a link to a page that shows details about that book
- it will be something like
url_for('books', book_id=book.id)
- it will be something like
- Show how many patrons have borrowed the book
- Make the book’s title a link to a page that shows details about that book
Note: I guess you could still allow librarians to borrow books, just like patrons… But if it looks too cluttered, you don’t have to.
The /books/<book_id>
page
Make a new route function using the above route, like this:
@app.route('/books/')
@app.route('/books/<book_id>')
def books(book_id=None):
...
This page is librarian-only. If a non-librarian is logged in, or no user is logged in, redirect them to the home page.
This page has two modes:
- If
book_id is None
, this will show a new book page.- You’ll need to make a new template that extends
base.html
. - This page should show a form to create a new book. (You can base it on the
login
orregister
templates.) - Have your
books()
route function do the appropriate checks before adding a newBook
object to the databasedb
.- remember:
db.session.add(Book(...))
db.session.commit()
- You’ll need to make a new template that extends
- otherwise, this will show a book detail page.
- So for
/books/1
, this will show the details for the book with ID 1. - Make another template!
- Show the book’s title and author.
- Also show a list of the patrons who are borrowing it.
- Last, if no one is borrowing this book, show a button to remove this book from the library.
- If the book is successfully removed:
- use
flash()
to tell them it was successful; - and redirect them to the home page.
- use
- If the book is successfully removed:
- So for
If book_id
is invalid, you should abort with a 404.
The /accounts/<user_id>
page
One last page. This will be similar to the /books/
page.
This page is librarian-only. If a non-librarian is logged in, or no user is logged in, redirect them to the home page.
This page has two modes:
- if
user_id is None
, this will show all users.- List the usernames alphabetically.
- List the librarians and the patrons separately.
- There should also be a form to add a new librarian.
- It will work much like the “Sign Up” page.
- Each username should also be a link to their
/accounts/user_id
details page (much like the/books/book_id
details pages).
- else, this will show a user details page.
- So for
/accounts/1
, this will show the details for user number 1 (which should beowner
…) - Show the user’s username and email.
- NOT THEIR PASSWORD. LMAO.
- Also show a list of books that they are borrowing.
- Finally, have a button to delete the user.
- No one can delete the
owner
account. - You do not have to worry about deleting an account that is borrowing books. The database will automatically update itself to remove those borrows.
- No one can delete the
- So for