Posted on

Pyramid 05: Changing the layout with Bootstrap 4

Let’s make minor changes to the layout, so we keep things simple and concentrate at the backend of our application along the next posts.

By the time I am writing this tutorial, Boostrap is on version 4.1.
You can download it here.

Download bootstrap 4, unzip it and put the folder inside your static folder ( task_manager/static/ ).
Rename the Bootstrap folder to bootstrap.

In order to use Bootstrap 4 you can follow the instructions on the getting started tutorial.

Here we are going to use our local version of the Bootstrap. This may give you some freedom in case you want to edit it and will be useful in case you need to work on the application while without internet connection.

Reading the instructions you realize that it is necessary to use jquery and popper.

Again, you may follow the instructions on official Bootstrap documentation, but here we are going to use local copies of these tools, so download them from the respective links.
By using local copies we may be able to run our application while offline.

Inside the static folder create a js folder and put the downloaded copies of jquery and popper inside it.
Remove from the static folder the file pyramid.png. We are not going to use it.

By the end your static folder would be like:

static/
├── bootstrap
│   ├── css
│   │   ├── bootstrap.css
│   │   ├── bootstrap.css.map
│   │   ├── bootstrap-grid.css
│   │   ├── bootstrap-grid.css.map
│   │   ├── bootstrap-grid.min.css
│   │   ├── bootstrap-grid.min.css.map
│   │   ├── bootstrap.min.css
│   │   ├── bootstrap.min.css.map
│   │   ├── bootstrap-reboot.css
│   │   ├── bootstrap-reboot.css.map
│   │   ├── bootstrap-reboot.min.css
│   │   └── bootstrap-reboot.min.css.map
│   └── js
│       ├── bootstrap.bundle.js
│       ├── bootstrap.bundle.js.map
│       ├── bootstrap.bundle.min.js
│       ├── bootstrap.bundle.min.js.map
│       ├── bootstrap.js
│       ├── bootstrap.js.map
│       ├── bootstrap.min.js
│       └── bootstrap.min.js.map
├── js
│   ├── jquery-3.4.1.slim.min.js
│   └── popper.min.js
├── pyramid-16x16.png
└── theme.css

Open your file layout.jinja2 that is inside the templates folder, and edit it to your taste, but make sure that the bootstrap required items are there.

My version of layout.jinja2:

<!DOCTYPE html>
<html lang="{{request.locale_name}}">
  <head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta name="description" content="pyramid web application">
    <meta name="author" content="Pylons Project">
    <link rel="shortcut icon" href="{{request.static_url('task_manager:static/pyramid-16x16.png')}}">

    <title>Task Manager - Pyramid - MongoDB - WTForms</title>

    <!-- Bootstrap core CSS -->
    <link href="{{request.static_url('task_manager:static/bootstrap/css/bootstrap.min.css')}}" rel="stylesheet">

    <!-- Custom styles for this scaffold -->
    <link href="{{request.static_url('task_manager:static/theme.css')}}" rel="stylesheet">

    <!-- HTML5 shiv and Respond.js IE8 support of HTML5 elements and media queries -->
    <!--[if lt IE 9]>
      <script src="//oss.maxcdn.com/libs/html5shiv/3.7.0/html5shiv.js" integrity="sha384-0s5Pv64cNZJieYFkXYOTId2HMA2Lfb6q2nAcx2n0RTLUnCAoTTsS0nKEO27XyKcY" crossorigin="anonymous"></script>
      <script src="//oss.maxcdn.com/libs/respond.js/1.3.0/respond.min.js" integrity="sha384-f1r2UzjsxZ9T4V1f2zBO/evUqSEOpeaUUZcMTz1Up63bl4ruYnFYeM+BxI4NhyI0" crossorigin="anonymous"></script>
    <![endif]-->
  </head>

  <body>

    <div class="starter-template">
      <div class="container">
        <div class="row">

          <div class="col-md-12">
            {% block content %}
                <p>No content</p>
            {% endblock content %}
          </div>
        </div>

      </div>
    </div>


    <!-- Bootstrap core JavaScript
    ================================================== -->
    <!-- Placed at the end of the document so the pages load faster -->
    <script src="{{request.static_url('task_manager:static/js/jquery-3.4.1.slim.min.js')}}" ></script>
    <script src="{{request.static_url('task_manager:static/js/popper.min.js')}}"></script>
    <script src="{{request.static_url('task_manager:static/bootstrap/js/bootstrap.min.js')}}"></script>
  </body>
</html>

If you read the file above, you may notice . There we have theme.css.
Let’s change it a little bit so the reading gets more comfortable. Add the code below to the end of the file theme.css:

input {
  color: black;
}

a {
  color: #ffffff;
}

a:hover {
  color: #ffffff;
  text-decoration: underline;
}

Now let’s adjust our file add.jinja2 to make the input text more readable:

{% extends "layout.jinja2" %}

{% block content %}
<div class="content">
    <h1>Add <span class="smaller">Create a new document on our collection.</span></h1>
    <form action="" method="post">
        <div>
            {{ form.name.label }}: {{ form.name() }}
            {% if form.name.errors %}
            <ul class="errors">
                {% for error in form.name.errors %}
                <li>{{ error }}</li>
                {% endfor %}
            </ul>
            {% endif %}
        </div>
        <div>
            {{ form.active.label}}: {{ form.active() }}
            {% if form.active.errors %}
            <ul class="errors">
                {% for error in form.active.errors %}
                <li>{{ error }}</li>
                {% endfor %}
            </ul>
            {% endif %}
        </div>
        <input type="submit" value="Submit">
    </form>
</div>
{% endblock content %}

In fact I just removed the class “text-pramary” from the fields.

At edit.jinja2, like the file above, remove the “text-primary” class:

{% extends "layout.jinja2" %}

{% block content %}
<div class="content">
    <h1>Edit <span class="smaller">{{form.name.data}}</span></h1>
    <form action="" method="post">
        {{ form.id }}
        <div>
            {{ form.name.label }}: {{ form.name() }}
            {% if form.name.errors %}
            <ul class="errors">
                {% for error in form.name.errors %}
                <li>{{ error }}</li>
                {% endfor %}
            </ul>
            {% endif %}
        </div>
        <div>
            {{ form.active.label}}: {{ form.active() }}
            {% if form.active.errors %}
            <ul class="errors">
                {% for error in form.active.errors %}
                <li>{{ error }}</li>
                {% endfor %}
            </ul>
            {% endif %}
        </div>
        <input type="submit" value="Submit">
    </form>
</div>
{% endblock content %}

Finally we edit the file home.jinja2:

{% extends "layout.jinja2" %}

{% block content %}
<div class="content">
  <h1><span class="font-semi-bold">Task Manager</span> <span class="smaller">A simple CRUD</span></h1>
  <p class="lead">Welcome to <span class="font-normal">{{project}}</span>, a&nbsp;Pyramid application that&nbsp;intends
    to help you with MongoDB and WTForms.</p>
  <hr>
  <h1> Tasks</h1> <br>
  <ul>
    {% if tasks %}

    {% else %}
    <li>No tasks</li>
    {% endif %}
    {% for task in tasks %}
    <li>
      {{task.name}} | <a href="{{ request.route_url('tedit', id=task['_id'])}}">Edit</a> | <a
        href="{{ request.route_url('tdelete', id=task['_id'])}}">Delete</a>
    </li>

    {% endfor %}
  </ul>
  <h2>Actions</h2>
  <ul>
    <li><a href="{{request.route_url('tadd')}}">Add new Task</a></li>
  </ul>
</div>

{% endblock content %}

Go to the folder where the development.ini file is and run the project:

env/bin/pserve development.ini --reload

Now you can edit the layout as you want or keep it the way it is.

You can find the code for this part of the tutorial at the part05 branch of the project repository.
To clone and run the specific branch use:

git clone -b part05 https://github.com/albertosdneto/tutorial_pyramid_mongo.git
cd tutorial_pyramid_mongo/
python3 -m venv env
env/bin/pip install --upgrade pip setuptools
env/bin/pip install -e .
env/bin/pserve development.ini --reload

I hope you enjoy.