BUILDING E-COMMERCE WEBSITE USING DJANGO PYTHON: TEMPLATE INHERITANCE, ADDING BOOTSTRAP AND WORKING WITH STATIC FILES(PART-03)

in Programming/Dev2 months ago

Hello everyone. Welcome to the third part on the series "Building E-Commerce Website Using Django Python". In this tutorial, we will be seeing how templates inheritance works and then adding a design (i.e. bootstrap classes and some CSS), and then finally we will work with static files. Please find the link to the previous tutorial below. The source code can also be found below in the same post.

First Part: Getting Started
Second Part: Admin, Models and Data Rendering

The project directory and files structure as per the previous tutorial looks like this:

image.png

So first lets work with templates inheritance in a simple way. For this, I will create three new html files inside ecommerce/store/templates/store. I will call it as base.html, login.html and signup.html. I will remove everything starting from <div class="container-fluid"> to its closing tag in home.html that we have in previous tutorial. Now our project structure will look like this.

image.png

Lets also comment out first three lines in our homePage views. And add two more views: one for login and another for signup. For now home.html,login.html and signup.html pages are empty.

from django.shortcuts import render
from django.http import HttpResponse
from .models import *


def homePage(request):
    # products = Product.objects.all()
    # categories = Category.objects.all()
    # context = {'products': products, 'categories': categories}
    return render(request, 'store/home.html')


def login(request):
    return render(request, 'store/login.html')


def signup(request):
    return render(request, 'store/signup.html')



Lets add path in our store/urls.py for login page and signup page.

from django.urls import path
from . import views

urlpatterns = [
    path('', views.homePage, name="home"),
    path('login/', views.login, name="login"),
    path('signup/', views.signup, name="signup"),

]

Now lets see how template inheritance works. First lets put this simple Bootstrap code in our home.html.

<!doctype html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">

  <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css">
  <title>E-Commerce</title>
</head>
<body>
<h1>This is Navbar</h1>
<hr>
<h1>Here's Homepage Content</h1>
<hr>
<h1>This is Footer</h1>


<script src="https://code.jquery.com/jquery-3.2.1.slim.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.12.9/umd/popper.min.js"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/js/bootstrap.min.js"></script>
</body>
</html>


Go to your terminal. Type in python manage.py runserver to run the development server. You shouldn't encounter any error as of now. Go to the browser and type http://127.0.0.1:8000/, you should see the following screen.

image.png

Also lets add code to our login.html page:

<!doctype html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">

  <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css">
  <title>E-Commerce</title>
</head>
<body>
<h1>This is Navbar</h1>
<hr>
<h1>Here's Login Page</h1>
<hr>
<h1>This is Footer</h1>


<script src="https://code.jquery.com/jquery-3.2.1.slim.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.12.9/umd/popper.min.js"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/js/bootstrap.min.js"></script>
</body>
</html>


You can see it in browser as:

image.png

Also for signup page:

<!doctype html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">

  <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css">
  <title>E-Commerce</title>
</head>
<body>
<h1>This is Navbar</h1>
<hr>
<h1>Here's Signup Page</h1>
<hr>
<h1>This is Footer</h1>


<script src="https://code.jquery.com/jquery-3.2.1.slim.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.12.9/umd/popper.min.js"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/js/bootstrap.min.js"></script>
</body>
</html>


And see it in browser as:

image.png

You can see the navbar and footer is same for every pages and we are writing the same html code for each of the pages. When your application gets bigger or you want to add some new functionalities later then it will create a problem as your code in the screen will be too large to handle. So, its where template inheritance comes to rescue. We will keep navbar and footer to one single base html pages and the rest of the html pages can inherit from the base template. Django helps to achieve this with the help of extends, include keywords and templates blocks content. Remember we have created base.html before. Its where we will put navbar and footer and every other pages will inherit this one. So open base.html and put the common code for navbar and footer to this file.

<!doctype html>
<html lang="en">
<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">

    <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css">
    <title>E-Commerce</title>
</head>
<body>
<h1>This is Navbar</h1>
<hr>

{% block content %}

{% endblock %}


<h1>This is Footer</h1>

<script src="https://code.jquery.com/jquery-3.2.1.slim.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.12.9/umd/popper.min.js"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/js/bootstrap.min.js"></script>
</body>
</html>

We will place content inside {% block content %} {% endblock %} in other html pages that will inherit from this base.html. Now you can open your home.html and place this code:

{% extends 'store/base.html' %}

{% block content %}
<h1>Here's Homepage Content</h1>
<hr>

{% endblock %}


Now see the home.html has a pretty clean and short code. It extends the base template (i.e. inherit it) and whatever inside the block content is written in home.html the base template will override that content within its block content. Lets see if the output is same as the previous one. Open your browser and you should see this:

image.png

The same goes with login and signup page. For login page:

{% extends 'store/base.html' %}

{% block content %}

<h1>Here's Login Page</h1>
<hr>

{% endblock %}


Output is:

image.png

For signup page:

{% extends 'store/base.html' %}

{% block content %}

<h1>Here's Signup Page</h1>
<hr>

{% endblock %}


Output for signup page is:

image.png

Now we have learned how template inheritance works. Now lets actually add simple design to each of the three pages. Design is not our primary focus in this series. We are focusing on how Django backend part works. I already have custom design made for each of the pages. You can directly copy/paste from below for each of the pages. Also we have already learned about how to render data to the template in the previous tutorial. So, I will be also using that directly within the template. Below is the design for base.html, which is for navbar and footer.

<!doctype html>
<html lang="en">
<head>

    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">

    <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css">
    <link rel="stylesheet" href="https://pro.fontawesome.com/releases/v5.10.0/css/all.css">

    <title>Ecommerce</title>
    <style>


        a{
        font-size: 15px;
        }

        a:hover{
            transition: transform .6s ease;
            transform: scale(1.1);
            text-decoration: none;
            letter-spacing: 0.5px;
            font-weight: 700;
            border: none;
            color: white;
            background: #FF0000 !important;
                }
        body{
            background-color: #000000;

        }
        .hover_img{
        transition: transform .5s ease;
        }
        .hover_img:hover{
            transform: scale(1.05);
        }
        #popup_modal{
        max-width: 1000px;
        margin-top: 0px;
        }

    </style>
</head>
<body>

<nav class="navbar navbar-expand-lg navbar-dark bg-dark">
    <a class="navbar-brand text-white" style="pointer-events: none;">Shopaholic</a>
    <button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarSupportedContent"
            aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation">
        <span class="navbar-toggler-icon"></span>
    </button>

    <div class="collapse navbar-collapse" id="navbarSupportedContent">
        <ul class="navbar-nav mr-auto">
            <li class="nav-item active">
                <a class="nav-link" href="">Store</a>
            </li>
        </ul>

        <ul class="navbar-nav my-2 my-lg-0">
            <li class="nav-item">
            <li class="nav-item">
                <a class="nav-link text-white" href="">Logout</a>
            </li>

            <li class="nav-item">
                <a class="nav-link text-white" href="">Signup</a>
            </li>
            <li class="nav-item">
                <a class="nav-link text-white" href="">Login</a>
            </li>
        </ul>

    </div>
</nav>



{% block content %}

{% endblock %}

<div class="container-fluid bg-dark mt-3">
    <center><strong><p class="text-white mb-0  ml-4 py-3" style="font-size: 18px;"><i class="fas fa-copyright"></i> 2021 E-Commerce Software by leoumesh. All rights reserved.</p></strong></center>
</div>

<script src="https://code.jquery.com/jquery-3.2.1.slim.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.12.9/umd/popper.min.js"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/js/bootstrap.min.js"></script>
<script>

</script>
</body>
</html>

Every other pages in our code will now inherit from this one. So lets put this code inside home.html:

{% extends 'store/base.html' %}


{% block content %}
<div class="container mt-4">
    <div class="row">

        <div class="col-lg-3">

            <div class="list-group" id="myList">
                {% for category in categories %}
                <a href="/?category={{category.id}}" class="list-group-item text-white" style="background: #343a40">
                    <i class="fa fa-shopping-cart" style="margin-right: 15px; font-size: 20px;"></i>{{category.name}}</a>
                {% endfor %}
            </div>



        </div>

        <div id="products" class="col-lg-8">
            <div class="row mx-auto">
                {% for product in products %}
                <div class="card mx-auto mb-4 bg-dark" style="width: 13.5rem;">
                    <a href="#{{product.name}}" data-toggle="modal">
                        <img src="{{product.image.url}}" class="card-img-top hover_img"></a>


                    <div class="modal fade" id="{{product.name}}">
                        <div class="modal-dialog" id="popup_modal">
                            <div class="modal-content text-white bg-dark">
                                <div class="modal-body">

                                    <div class="row">
                                        <div class="col-md-6">
                                            <img src="{{product.image.url}}" height="100%" width="100%">
                                        </div>

                                        <div class="col-md-6">
                                            <h1>{{product.name}}</h1>

                                            <h5 class="mt-3 text-warning" style="font-size: 30px; letter-spacing: 2px;">
                                                <i class="fas fa-dollar-sign" style="margin-right: 2px; font-size: 26px;">
                                                </i>{{product.price}}.00</h5>

                                            <p class="mt-3 text-justify">{{product.description}}</p>

                                            <h5 class="mt-3 text-warning" style="font-size: 18px;">In Stock:</h5> 4
                                            <h5 class="mt-3 text-warning" style="font-size: 18px;">Category:</h5>{{product.category}}
                                            <h5 class="mt-3 text-warning" style="font-size: 18px;">Delivery:</i></h5> 3 Working Days
                                            <h5 class="mt-3 text-warning" style="font-size: 18px;">Price:</i></h5> Non-Negotiable

                                        </div>
                                    </div>

                                </div>
                                <div class="modal-footer">
                                    <button type="button" class="btn btn-warning" data-dismiss="modal">
                                        Close
                                    </button>
                                </div>
                            </div>

                        </div>

                    </div>

                    <div class="card-body text-white">
                        <p class="card-title">{{product.name}}</p>
                        <p class="card-text"><i class="fas fa-dollar-sign" style="margin-right: 2px; font-size: 15px;"></i>{{product.price}}</p>
                        <a href="#" class="btn btn-primary btn-sm">Add to Cart</a>
                    </div>
                </div>
                {% endfor %}
            </div>
        </div>
    </div>
</div>

{% endblock %}



I have inherited the navbar and footer. And since, I don't want to put a long code I have actually used for loop logic to show the categories and product dynamically from our model, which I have already shown in the previous part on how to render data dynamically to the front-end. I will try to keep it simple: we have Bootstrap list in the left side of our application, where we have iterated over categories that's in our model. On the right side is the product that is shown using Bootstrap card. And we have iterated over products field that's in our model in card-body using Django for-loop logic, that I have already shown how to use in our previous tutorial. To view the product description, we have a Bootstrap pop-up modal. Lets see the output for homepage in our browser. Check if your development server is still running. If not type python manage.py runserver in your terminal.

image.png

Scrolling down:

image.png

Lets view one of the product description:

image.png

But all the products are showing in a single homepage and we want to display product by categories. So you can see we have pass categories id in above home.html file when we hover over the categories in the left handside. This is the fragment of above code: <a href="/?category={{category.id}}" class="list-group-item text-white" style="background: #343a40">. So we are sending GET request. In our views file we will grab the categories id that is being sent as GET request. And after grabbing this we will display the product by categories accordingly.

Lets add this code to our homePageview.

def homePage(request):
    products = None
    categories = Category.objects.all()
    category_id = request.GET.get('category')
    if category_id:
        products = Product.objects.filter(category=category_id)
    else:
        products = Product.objects.filter(category=1)
    context = {'products': products, 'categories': categories}
    return render(request, 'store/home.html', context)


Initially we assign nothing to the product i.e. it is empty. We query through our Category model. And then we grab the content under GET request which is the category id as we discussed above. So only after we get that category id we want to display product that is under that category id else by default we will show the product with category id as 1 which is the product inside "Men's Clothing". So when it doesn't have any category id, it will display Men's clothing product by default. Now our final views.py is:

from django.shortcuts import render
from django.http import HttpResponse
from .models import *


def homePage(request):
    products = None
    categories = Category.objects.all()
    category_id = request.GET.get('category')
    if category_id:
        products = Product.objects.filter(category=category_id)
    else:
        products = Product.objects.filter(category=1)
    context = {'products': products, 'categories': categories}
    return render(request, 'store/home.html', context)


def login(request):
    return render(request, 'store/login.html')


def signup(request):
    return render(request, 'store/signup.html')


Lets see the output in the screen:

image.png

By default you will see Men's Clothing in the homepage.

image.png

Lets add design to our login.html file.

{% extends 'store/base.html' %}

{% block content %}

<div class="container text-white pt-5">
    <div class="p-5 m-5">
        <div class="col-md-6 mx-auto pt-4 bg-dark">
            <h3 class="text-white text-center pb-2">Login to your account</h3>
            <form action="" method="post" class="bg-dark">

                <div class="form-group">
                    <label for="email">Email address</label>
                    <input type="email" name="email" class="form-control" placeholder="Enter your email">
                </div>
                <div class="form-group">
                    <label for="password1">Password</label>
                    <input type="password" name="password" class="form-control" placeholder="Password">
                </div>
                <div class="form-group">
                    <input type="submit" value="Login" class="btn btn-primary mt-1 mr-2 mb-3">
                    <a href="{% url 'signup' %}">Signup</a> Instead!
                </div>
            </form>
        </div>
    </div>
</div>

{% endblock %}


Go to browser and type: http://127.0.0.1:8000/login/. You should see following login page:

image.png

Also, lets add this code to our signup.html,

{% extends 'store/base.html' %}

{% block content %}



<div class="container text-white">
    <div class="p-2 m-2">
        <div class="col-md-6 mx-auto pt-4 bg-dark">
            <h3 class="text-white text-center pb-2">Create an Account</h3>
            <form action="" method="post" class="bg-dark">
                <div class="form-group">
                    <label for="firstname">Firstname</label>
                    <input type="text" name="firstname" class="form-control" placeholder="Firstname">
                </div>
                <div class="form-group">
                    <label for="lastname">Lastname</label>
                    <input type="text" name="lastname" class="form-control" placeholder="Lastname">
                </div>
                <div class="form-group">
                    <label for="lastname">Phone Number</label>
                    <input type="text" name="phone" class="form-control" placeholder="Phone number">
                </div>
                <div class="form-group">
                    <label for="email">Email address</label>
                    <input type="email" name="email" class="form-control" placeholder="Enter your email">
                </div>
                <div class="form-group">
                    <label for="password">Password</label>
                    <input type="password" name="password" class="form-control" placeholder="Password">
                </div>


                <div class="form-group">
                    <input type="submit" value="Register" class="btn btn-primary mt-1 mr-2 mb-3">
                    <a href="{% url 'login' %}">Login</a> Instead!
                </div>
            </form>
        </div>
    </div>
</div>
{% endblock %}


Go to browser and type: http://127.0.0.1:8000/signup/. You should see following login page:

image.png

We will be adding design to Cart Page, Check out Form, Order Page as we go ahead while building our shopping application. As you can notice I have also used dynamic URL routing. For example, see this fragment in login.html file: <input type="submit" value="Login" class="btn btn-primary mt-1 mr-2 mb-3"> <a href="{% url 'signup' %}">Signup</a> Instead!

In anchor tag has a hypertext reference to signup page. This is the syntax for dynamic url routing in Django {% url 'path_name' %}. The path_name is what we have specified in store/urls.py file.

So the last part of the tutorial is working with static files. We want to put logo in the top left handside of our application which is currently Shopaholic. Suppose we don't want logo to be fetched dynamically. We will treat it as static, that is never changing. For this I will put image named logo.png inside ecommerce/static/images. You can download the logo image from here too:

logo.png

Since the navbar is in base.html file, we will put image here and first we have to load static in the top of our page. The syntax is {% load static %} and replace the navbar-brand class with:

    <a class="navbar-brand text-white" style="pointer-events: none;">
        <img src="{% static 'images/logo.png' %}" height="30px">
    </a>

Also you can see we have used internal css in our base.html file. Lets create a folder named as css inside static folder. And then create a file named as style.css inside static/css. Copy paste all those internal CSS into this file. The project structure now looks like this:

image.png

Also add this reference link to the style.css in our base.html.

 <link rel="stylesheet" href="{% static 'css/style.css' %}">

So our final base.html with those static files included is:

{% load static %}

<!doctype html>
<html lang="en">
<head>

    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">

    <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css">
    <link rel="stylesheet" href="https://pro.fontawesome.com/releases/v5.10.0/css/all.css">
    <link rel="stylesheet" href="{% static 'css/style.css' %}">

    <title>Ecommerce</title>
</head>
<body>

<nav class="navbar navbar-expand-lg navbar-dark bg-dark">
    <a class="navbar-brand text-white" style="pointer-events: none;">
        <img src="{% static 'images/logo.png' %}" height="30px">
    </a>
    <button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarSupportedContent"
            aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation">
        <span class="navbar-toggler-icon"></span>
    </button>

    <div class="collapse navbar-collapse" id="navbarSupportedContent">
        <ul class="navbar-nav mr-auto">
            <li class="nav-item active">
                <a class="nav-link" href="">Store</a>
            </li>
        </ul>

        <ul class="navbar-nav my-2 my-lg-0">
            <li class="nav-item">
            <li class="nav-item">
                <a class="nav-link text-white" href="">Logout</a>
            </li>

            <li class="nav-item">
                <a class="nav-link text-white" href="">Signup</a>
            </li>
            <li class="nav-item">
                <a class="nav-link text-white" href="">Login</a>
            </li>
        </ul>

    </div>
</nav>



{% block content %}

{% endblock %}

<div class="container-fluid bg-dark mt-3">
    <center><strong><p class="text-white mb-0  ml-4 py-3" style="font-size: 18px;"><i class="fas fa-copyright"></i> 2021 E-Commerce Software by leoumesh. All rights reserved.</p></strong></center>
</div>

<script src="https://code.jquery.com/jquery-3.2.1.slim.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.12.9/umd/popper.min.js"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/js/bootstrap.min.js"></script>
<script>

</script>
</body>
</html>


Check in the browser if everything is working correctly with the logo and the design not being messed up. For example: in my case lets see only home page:

image.png

Now this marks the end of our tutorial. In the next part, we will be dealing with Customer model and working with signup form and adding custom validation for it.

Find the project source code here
Find the product sample images here

Getting Started:

Admin Dashboard link: http://127.0.0.1:8000/admin/
Username: testuser
Password: testuser321

Sort:  

Manually curated by EwkaW from the @qurator Team. Keep up the good work!