Skip to content

Objectives

  • Add the asset class model
  • Add the asset class form
  • Create a new asset class html file and extend it from base
  • Update the navigation
  • Create the three new routes to 1) read and add; 2) update; and 3) delete.

Step 1. Go to your database and add an asset class table. It should have 3 columns (asset_class_id PK [INT], asset_class_name [VARCHAR45], and allocation_percent [FLOAT]). Then go to your models.py and add the following code:

class Asset_Class(db.Model):
    asset_class_id = db.Column(db.Integer, primary_key=True)
    asset_class_name = db.Column(db.String(164), index=True, unique=True)
    allocation_percent = db.Column(db.Float)

You then need to go to your app.py and add Asset_Class after the user in the model line.

from app.models import User, Asset_Class

Modify the code at the top of the routes.py file also. This will allow you to use the model within the page.

from app.models import User, Asset_Class

Step 2. Add a form for your asset class to your forms.py.

class AssetClassForm(FlaskForm):
	asset_class_name = StringField('Asset Class Name', validators=[DataRequired()])
	allocation_percent = StringField('Allocation Percent', validators=[DataRequired()])
	submit = SubmitField('Sign In')

Be sure to add your form class to the top of the route page so you can use it within the code.

from app.forms import LoginForm, AssetClassForm

Step 3. Now let’s make some changes to our route.py file. We are going to add three routes. The first we will use to add and read (the CR of CRUD). We add a methods to our route decorator, so if we post (meaning use a form) it will add. If we just go to the page without a form submit, then it will use the get method.

We also use the @loginrequired so it will utilize the flask_login library to validate the user is logged in. This stops a user from accessing the page if they are not logged in.

Starting after the login route, but before the login stuff add the following route code:

1. Asset Class

@app.route('/asset_class', methods=['GET', 'POST'])
@login_required
def asset_class():
    form = AssetClassForm()
    user_id = session['user_id']
    if request.method == 'POST':
        asset_class_name = request.form.get("asset_class_name")
        allocation_percent = request.form.get("allocation_percent")
        asset_class = AssetClass(asset_class_name=asset_class_name, allocation_percent=allocation_percent)
        db.session.add(asset_class)
        db.session.commit()
        return redirect(url_for('asset_class'))
    asset_classes = get_asset_class(user_id)
    return render_template('asset_class.html', form=form, asset_classes=asset_classes)

Notice you will need to add the request library to the flask import at the top of the route page:

from flask import render_template, redirect, url_for, flash, session, request

We also need to create a new function “get_asset_class()” to access the database.

Create a new file called functions.py in the app folder. Add the following code to that file.

from app.db_connect import connect
from app import db, app

def get_asset_class():
    conn = connect()
    with conn.cursor() as cur:
        sql = f'SELECT asset_class_id, asset_class_name, allocation_percent from asset_class'
        cur.execute(sql)
        return cur.fetchall()

You will need to create a db_connect.py file inside your app folder also, to store your login information. You may need to install pymysql library. Make sure you put YOUR database, user, and password in the correct spots.

import pymysql

#### MAIN DB CONNECTION ####
def connect():
    connection = pymysql.connect(host='bryanmarshall.com',
                                 port=3306,
                                 database='---',
                                 user='---',
                                 password='---',
                                 cursorclass=pymysql.cursors.DictCursor)
    print("Connected")
    return connection

If we want to use the get_asset_class function in our routes page we need to include (import) it at the top of the route.py page.

from app.functions import *

Now let’s add the update route code. Just put this code under the asset_class route.

2. Asset Class Update

@app.route('/asset_class_update/<asset_class_id>/',  methods=['GET', 'POST'])
@login_required
def asset_class_update(asset_class_id):
    form = AssetClassForm()
    if request.method == 'POST':
        asset_class_data = AssetClass.query.get(asset_class_id)
        asset_class_data.asset_class_name = form.asset_class_name.data
        asset_class_data.allocation_percent = form.allocation_percent.data
        db.session.commit()
        return redirect(url_for('asset_class'))
    return render_template('asset_class.html', form=form)

3. Asset Class Delete

Next let’s add our last route for this assignment the asset_class_delete, obviously the D in CRUD.

@app.route('/asset_class_delete/<asset_class_id>/', methods=['GET', 'POST'])
@login_required
def asset_class_delete(asset_class_id):
    delete_asset_class(asset_class_id)
    flash(f"The asset class has been deleted.")
    return redirect(url_for('asset_class'))

You will need to create a new function on the function page to delete an asset class:

def delete_asset_class(asset_class_id):
    conn = connect()
    with conn.cursor() as cur:
        sql = f'DELETE from asset_class WHERE asset_class_id = {asset_class_id}'
        cur.execute(sql)
        conn.commit()

Notice the conn.commit at the end, this will “write” the changes to the database when you are finished with the delete.


Step 4. Our last step is to create the asset_class.html page and extend it off the base.html file in the templates folder.

I would just create a new file and copy the code from inside dashboard.html into the new file.

We should also adjust our navigation in the base.html file. Put it under the “else” in the navigation, so IF they are logged in they see the asset class file.

<li class="nav-item"><a class="nav-link {% block nav_asset_class %}{% endblock %}" href="{{ url_for('asset_class') }}">Asset Class</a></li>

Now lets focus on the asset_class.html file. There are three main parts to this. We are going to use modals to add an asset class and update an asset class.

There are four main areas we are going to work on in the asset_class.html file. All of these are in the block body.

Area 1. Header

<header class="py-4">
            <div class="container px-lg-5">
                <div class="p-4 p-lg-1 bg-light rounded-3 text-center">
                    <div class="m-4 m-lg-5">
                        <h1 class="display-5 fw-bold">Asset Class Management</h1>
                    </div>
                </div>
            </div>
        </header>

Not a whole to explain here, we are just setting the H1 Tag to Asset Class Management.

Area 2. About Section – this is really not the about section, but that is the label in the code. It comes below the header.

We are going to put our flash messages here, anytime we return a flash message to the page, it will be display here. We also add our “Add Asset Class button”.

<section id="about">
            <div class="container px-4">
                <div class="row gx-4 justify-content-center">
                    <div class="col-lg-10">

                        {% with messages = get_flashed_messages() %}
                        {% if messages %}
                        <ul>
                        {% for message in messages %}
                            <div class="alert alert-success alert-dismissible fade show" role="alert">
                                {{ message }}
                                <button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
                            </div>
                        {% endfor %}

                        {% endif %}
                        {% endwith %}


                        <br>

                        <h1>Asset Classes
                            <button type="button" class="btn btn-success btn-lg btn-block" data-bs-toggle="modal" data-bs-target="#add_asset_class">Add Asset Class</button>
                        </h1>

Notice it calls a modal called add_asset_class. That is important since the next code is the modal itself.

<div class="modal fade" id="add_asset_class" aria-labelledby="add_account_typeLabel" aria-hidden="true">
                                        <div class="modal-dialog">
                                            <div class="modal-content">
                                                <div class="modal-header">
                                                    <h5 class="modal-title" id="add_modalLabel">Add Transaction Type</h5>
                                                    <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
                                                </div>
                                                <form action="{{ url_for('asset_class') }}" method="POST" novalidate>
                                                <div class="modal-body">
                                                        {{ form.hidden_tag() }}
                                                    <p>
                                                        {{ form.asset_class_name.label }}<br>
                                                        {{ form.asset_class_name(size=32) }}
                                                        {% for error in form.asset_class_name.errors %}
                                                        <span style="color: red;">[{{ error }}] </span>
                                                        {% endfor %}
                                                    </p>
                                                    <p>
                                                        {{ form.allocation_percent.label }}<br>
                                                        {{ form.allocation_percent(size=52) }}
                                                        {% for error in form.allocation_percent.errors %}
                                                        <span style="color: red;">[{{ error }}] </span>
                                                        {% endfor %}
                                                    </p>
                                                    <p> {{ form.submit(class="btn btn-success") }} </p>
                                                </div>
                                                <div class="modal-footer">
                                                    <button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Cancel</button>
                                                </div>
                                                </form>
                                            </div>
                                        </div>
                        </div>

Next we will add a table to display the current data in the database. As we add data it should show here.

<table  class="table">
                          <thead>
                            <th scope="col">Asset Class Name</th>
                            <th scope="col">Allocation Percent</th>
                          </thead>
                            {% for asset_class in asset_classes %}
                          <tr>
                            <td align="left">{{ asset_class.asset_class_name }}</td>
                            <td align="left">{{ asset_class.allocation_percent | percentageFormat }}</td>
                            <td>
                                <button type="button" class="btn btn-primary" data-bs-toggle="modal" data-bs-target="#update_modal{{asset_class.asset_class_id}}">Update Info</button>
                                <div class="modal fade" id="update_modal{{asset_class.asset_class_id}}" aria-labelledby="update_modalLabel" aria-hidden="true">
                                        <div class="modal-dialog">
                                            <div class="modal-content">
                                                <div class="modal-header">
                                                    <h5 class="modal-title" id="update_modalLabel">Update Asset Class</h5>
                                                    <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
                                                </div>
                                                <form action="{{url_for('asset_class_update', asset_class_id=asset_class.asset_class_id)}}" method="POST" novalidate>
                                                <div class="modal-body">
                                                        {{ form.hidden_tag() }}
                                                    <p>
                                                        {{ form.asset_class_name.label }}<br>
                                                        {{ form.asset_class_name(size=32, value=asset_class.asset_class_name) }}

                                                        {% for error in form.asset_class_name.errors %}
                                                        <span style="color: red;">[{{ error }}] </span>
                                                        {% endfor %}
                                                    </p>
                                                    <p>
                                                        {{ form.allocation_percent.label }}<br>
                                                        {{ form.allocation_percent(size=32, value=asset_class.allocation_percent) }}

                                                        {% for error in form.allocation_percent.errors %}
                                                        <span style="color: red;">[{{ error }}] </span>
                                                        {% endfor %}
                                                    </p>
                                                    <p> {{ form.submit(text='Update Asset Class', class="btn btn-success") }} </p>
                                                </div>
                                                <div class="modal-footer">
                                                    <button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Cancel</button>
                                                </div>
                                                </form>
                                            </div>
                                        </div>
                                    </div>

                                <button type="button" class="btn btn-danger" data-bs-toggle="modal" data-bs-target="#delete_modal{{asset_class.asset_class_id}}">Delete</button>

                            <!-- DELETE MODAL -->
                                <div class="modal fade" id="delete_modal{{asset_class.asset_class_id}}" aria-labelledby="delete_modalLabel" aria-hidden="true">
                                    <div class="modal-dialog">
                                        <div class="modal-content">
                                            <div class="modal-header">
                                                <h5 class="modal-title" id="delete_modalLabel">Delete Asset Class</h5>
                                                <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
                                            </div>
                                            <div class="modal-body">
                                                <p>Are you sure you want to delete {{ asset_class.asset_class_name }}? This can't be undone.
                                            </div>
                                            <div class="modal-footer">
                                                <button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Cancel</button>
                                                <form action="{{url_for('asset_class_delete', asset_class_id=asset_class.asset_class_id)}}" method="POST">
                                                    <input type="submit" class="btn btn-primary" value="Delete">
                                                </form>
                                          </div>
                                        </div>
                                    </div>
                                </div>
                            </td>
                          </tr>
                            {% endfor %}
                        </table>

Then of course we close our tags from above to create a final file.

</div>
                </div>
            </div>
        </section>

Make sure you add some javascript footer code to your base.html file. Put it right above the last </body></html> tags.

<!-- Footer-->
        <footer class="py-5 bg-dark">
            <div class="container"><p class="m-0 text-center text-white">Copyright &copy; DIY Money 2022</p></div>
        </footer>
        <!-- Bootstrap core JS-->
        <!-- JavaScript Bundle with Popper -->
        <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/js/bootstrap.bundle.min.js"></script>
        <!-- Core theme JS-->
        <script src="{{ url_for('static', filename='js/scripts.js') }}"></script>
        <script src="https://code.jquery.com/jquery-3.5.1.slim.min.js" integrity="sha384-DfXdz2htPH0lsSSs5nCTpuj/zy4C+OGpamoFVy38MVBnE+IbbVYUew+OrCXaRkfj" crossorigin="anonymous"></script>
        <script src="https://cdn.jsdelivr.net/npm/popper.js@1.16.1/dist/umd/popper.min.js" integrity="sha384-9/reFTGAW83EW2RDu2S0VKaIzap3H66lZH81PoYlFhbGU+6BZp6G7niu735Sk7lN" crossorigin="anonymous"></script>
        <script src="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/js/bootstrap.min.js" integrity="sha384-B4gt1jrGC7Jh4AgTPSdUtOBvfO8shuf57BaghqFfPlYxofvL8/KUEfYiJOMMV+rV" crossorigin="anonymous"></script>

Turn this in. To turn it in upload your code to GitHub and then be sure it hits Render. Test your code on Render. Turn in your Render URL and GitHub repository URL.