Bookish

Learning goals

  • Using relational databses
  • Object-relational mappers (ORMs)
  • Linking different sources of data
  • The MVC model (Model-View-Controller)
  • Developing effective user interfaces

Programs Used

Overview

We’re going to build a simple library management system. We’d like it to support the following features:

  • Book Management
    • The librarian can browse the catalogue of books
      • Considerations: How are the books going to be sorted? By title? By author?
      • Could we add a search feature?
    • The librarian can edit the catalogue of books
      • Add new books
      • Update the details of a book
      • Add a copy of an existing book
      • Delete a copy of a book
    • For each book, we will need details of
      • The total number of copies of that book
      • The number of copies that are currently available
      • Which users have borrowed the other copies
  • Member Management
    • The librarian can see a list of library members
      • With a list of the books that member currently has checked out
    • The librarian can add a new member
    • The librarian can edit the details of an existing member
  • Checking books in / out
    • The librarian can check out a copy of a book to a user
    • The librarian can check a copy of a book back in
      • Notification for late returns?

Tip

There isn’t a lot of time to implement all of these features, so don’t worry if you don’t get all of them done. What’s important for this exercise is that you gain an understanding of how data moves through your system, and how to manipulate objects in your database.

Setup

The starter code

First, clone the the Bookish – Python repo and follow the instructions laid out in the readme to run it. Though running it won’t achieve anything at this moment in time.

The starter code you have been provided with uses Flask to provide a REST API that will sit on top of your database. The idea here is to provide endpoints that can be called to GET, POST, PUT, or DELETE data from/to your database, however, none of these endpoints have been implemented yet.

It’s worth going through what we do have in the starter code to make sure we understand what we have before we try and implement these endpoints.

app.py

This file sets up our app using the configuration information provided in config.py, links the app with our database, and performs any necessary Migrations on our database (more on these later).

This also is where you will define your API’s endpoints, have a look at the Flask documentation to get a feel for what this will look like.

Models

Models are just classes that define what data the app should contain. Have a look at book.py in the models folder to get an idea of what one looks like.

We are using Flask-SQLAlchemy to define these models, this allows us to define a method to save a Book to our database in the model.

In this file, we also use Marshmallow to define a schema for converting a Book object into JSON.

The models/ folder also contains __init__.py intialises our SQLAlchemy database interface.

Migrations

Migrations are just a series of scripts that are run in order to build up the structure of our database. We’re using Alembic as our Migrations Framework to help make this easier, it will automatically run any new migrations any time the app is run. It can also compare our database with our applications’s models to automatically generate the ‘obvious’ migrations needed to make up the difference.

Have a look at b628bf37cbe1_.py in the migrations/versions folder to get an idea of what a migration looks like; this one just creates a simple table to represent a book, and adds some sample data. Note that it also defines a method to rollback the migration.

config.py

This file is used by Flask-SQLAlchemy to configure its connection to our database for running our migrations.

Setting up PostgreSQL

If you haven’t got PostgreSQL installed already, you’ll need to download and install it from the PostgreSQL website. Leave all the options as the default, but untick “Launch Stack Builder at exit” on the last screen.

Warning

Make sure you remember the username/password you set when installing!

When you installed PostgreSQL Server, you may have also installed pgAdmin (if not, download and install it from here). This will allow you to manage your PostgreSQL server.

Navigate to Servers > PostgreSQL > Login/Group Roles (when you’re asked for a PostgreSQL password, put in the one you chose above for installing Postgres): right-click and create a new login/group role named bookish with a suitable password and privileges “Can login” and “Create databases”. 

Go to Servers > PostgreSQL > Databases, right-click and create a new database named bookish with owner bookish.

Make sure you can fire up PostgreSQL Server and connect to the database using pgAdmin. If you can’t, please speak to your trainer to help get it sorted.

Designing the database

You’ll need to come up with a database structure (i.e. what tables you want in your database. This is similar to deciding what classes to have in your application). Think through your database design. Work out what tables you need to support the scenarios outlined above. Discuss this with your trainers before you create things in the database.

You’re going to be using an Flask-SQLAlchemy, an ORM (object-relational mapper), to help manage the database. These help to abstract the interactions with the database by creating Models to represent the tables in your database, and Migrations to manage changes to the structure of your database.

You’ve already been provided with a migration to create a book table and a Book model (though they likely need a few additions), use these as inspiration to create models and migrations to define the rest of your database structure.

When writing code to connect to the database, the username/password should be the ones you set up previously (i.e. bookish / <password>) – at this stage we can put these values directly in our code, but remember this is bad practice; such values should be externalised and passwords should be encrypted.

Building an API

Let’s start building an API that your frontend can call to in order to interact with the database.

You’ll want to start by implementing a basic endpoint: GETing all the books available in the library. You’ll find the Flask-SQLAlchemy querying documentation and the Marshmallow serializing documentation very useful for this.

First, add a new endpoint to app.py. It should define a GET request, and have a sensible route name – /books, for example.

This endpoint should get a list of all the books in the database, and include them all in a JSON-formatted response.

To test your endpoint is working, simply run the app with poetry run flask run and use your browser or a tool like Postman to test your API endpoint and check that it’s all working as intended. Provided you followed the instructions above, the endpoint url should be http://localhost:<port>/books. It should return a JSON-formatted list of all the books in your database.

Once you’re satisfied that that’s working, have a go at implementing a few more of the features in your API, starting with a POST endpoint to add a new book to the library. As your API gets bigger, maybe think about how you can organise your code differently to keep it maintainable.

As usual, be sure to ask your trainer for help if you’re not sure of something!

Build the website

Now that you’ve got the API, it’s time to start building the frontend of the website. Otherwise, people won’t actually be able to access the books they want!

We’re going to be using Vite – a dev server and bundler – to make getting everything working a bit easier. Open your terminal, go to your work directory and run

npm create vite@latest -- --template vanilla

NPM should offer to install Vite if you don’t already have it. It should prompt you for a name for the project, and then create a directory with the same name that should be populated with a basic template for a site. If you go to that directory, you should see a few different files. The ones you should pay most attention to are:

  • index.html – This is where the majority of the layout of the site is defined
  • main.js – This is where interactive and dynamic parts of the site are written
  • style.css – This contains the styling for your site

Currently, they’ll have some example content from the template. To preview the site, you enter the file that vite has created and install dependencies and run the dev server by running the following in your terminal:

npm install
npm run dev

Go to the address it shows in your browser, and a site should appear! Once you’re happy that’s working, open index.html. You’ll notice a div with id="app". Start building out a basic structure for your site, with a simple header and page layout, replacing the div that’s currently there. As you’re doing this, try to keep accessibility in mind and use correct semantic HTML elements when you can. Once you’ve got a basic structure, you can try styling it a bit too by editing style.css.

Adding content

You’ve got a basic structure for your site, but there’s no content yet! You’ve already made an API route for listing all the available books, so now you can put it to use. You’ll probably find it helpful to create a container div with a unique, descriptive id that you can use to put all the content in. If you open main.js, you’ll notice the code already there, which is used to add content to that original div that you replaced. Feel free to remove or comment out the code – make sure you leave the import './style.css' though!

Now, add some code to your main.js to call your API, retrieve the list of available books, and populate the container you made when the site loads. You’ll want to make use of document.querySelector to get the container element (you can look at the template code for an example of how to use it), and the Fetch API to make an HTTP request to your API. This should end up looking something like:

fetch("localhost:3001/books")
    .then(response => response.json())
    .then(data => setMyData(data));

Once you’ve done this, you should be able to refresh the page, and see all the books listed on your page.

Interactive sites

It’s good that people can see the available books, but how are they going to add new ones to the library? You should have an API endpoint for this too, so all you need is an interface to interact with it. Add a form to your page so that people can submit new books. You’ll want to use the form HTML tag. Have a look through the MDN docs for the form tag and see if you can figure out how to submit data to your API. Remember to make sure you’ve set the correct method to match what you defined for your API.

Once you’re done with that, you should be able to submit a book, refresh the page, and have it appear along with all the other books!