BUILDING E-COMMERCE WEBSITE USING DJANGO PYTHON: HIDING AND SHOWING NAVBAR LINKS, IMPLEMENTING LOGOUT, FETCHING PRODUCTS DETAILS FROM CART ALONG WITH PRICE USING DJANGO FILTER(PART-07)

in Programming/Dev24 days ago

Hello everyone. Welcome to the seventh part on the series "Building E-Commerce Website Using Django Python". In the last tutorial, we focused on implementing add to cart functionality without the use of any JavaScript. In this tutorial, we will be doing couple of things with our projects like hiding certain navbar links like login, signup when the user is logged in, then adding logout functionality, design cart page, fetch the cart products stored in cookies along with the product image and total price. Also, we will use Django filter for showing total price and unit price in our desired currency.

To see the preceding posts of the series along with the source code, you can visit the following link. I have placed the link to source code along with other files at the bottom of each post.

First Part: Getting Started
Second Part: Admin, Models and Data Rendering
Third Part: Template Inheritance, Bootstrap and Static Files
Fourth Part: Signup Form Validation
Fifth Part: Login Functionality, Admin Dashboard Customization
Sixth Part: Add to Cart Functionality

So lets start from where we have left behind in our last tutorial. First, we will be hiding certain navbar links when the user is logged in. If you remember during login, when user email exists in the database and entered password matches, we have stored that customer with a unique id to a session along with its email. We will use this session to determine if the user is logged in or not. If logged in we will show the user email, cart and logout navbar link and if not we will show login and signup. Also, in the later part, we will show cart button even to the anonymous user (i.e. the guest user who is not logged in). Lets open store/templates/store/base.html. We will simply write an if-else logic there to show and hide the navbar links.

{% 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/logo1.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">
                {% if request.session.customer %}
            <li class="nav-item">
                <a class="nav-link text-white" href="#" style="pointer-events: none;">{{request.session.email}}</a>
            </li>
            <li class="nav-item">
                <a class="nav-link text-white" href="">Cart</a>
            </li>
            <li class="nav-item">
                <a class="nav-link text-white" href="">Order</a>
            </li>
            <li class="nav-item">
                <a class="nav-link text-white" href="">Logout</a>
            </li>

            {% else %}
            <li class="nav-item">
                <a class="nav-link text-white" href="{% url 'signup' %}">Signup</a>
            </li>
            <li class="nav-item">
                <a class="nav-link text-white" href="{% url 'login' %}">Login</a>
            </li>

            {% endif %}
        </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>


Lets see if this code is working properly. Run your development server simply by typing python manage.py runserver. After that, I will be clearing my localhost cookies from browser just to show you a working demo.

image.png

I will now login with one of our already registered credentials. I will use email: [email protected] and password: demo123.

image.png

You can see when a user is logged in, it will show his email, cart, order and logout button. Now, we will work with this logout button. Clicking logout means we have to clear the session. So, we will destroy the session when logout button is clicked. Go to our store/views.py and write a logout function. We will clear session when that button is clicked and then redirect to the login page.

def logout(request):
    request.session.clear()
    return redirect('login')


Now the next thing we need to do is map a URL to this view. So open store/urls.py and in the list of urlpatterns, we will add this one.

path('logout/', views.logout, name="logout")

Also, as we have given this path a name as "logout", we will add a dynamic url routing for Logout link in our navbar. Open templates/store/base.html and replace the Logout link under <li> tag with this one.

<li class="nav-item">
    <a class="nav-link text-white" href="{% url 'logout' %}">Logout</a>
</li>


Time to test our code. Go to your browser and refresh it. Now click logout button, you will see the login screen as below.

image.png

If you now go to your home page i.e. http://127.0.0.1:8000/, you will see an empty cart because we stored that cart item in session and clearing session will clear the cart items too but if you logged in again, you will get all those cart items.

image.png

Before, I went on to show how to fetch the products from the cart, I really wanted to show the cart page even if the user is not logged in. For this in our templates/store/base.html, we will pull the cart that is under <li> tag anywhere outside the if-else logic, so that cart link can be shown when user is logged in and when user is not logged in.

Inside templates/store, we will create a new HTML file and named it as cart.html. We will design our cart page and show items added into the cart accordingly. Lets put this code inside our cart.html.

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


{% block content %}

<div class="container">
    <h1 class="text-white">This is Cart Page for now.</h1>
</div>

{% endblock %}


Lets show this out in our browser. Now we will create a cart view inside store/views.py.

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


Now, we need to map a URL to this view. Open store/urls.py and define a path inside our list of urlpatterns.

path('cart/', views.cart, name="cart")


If you go into your browser and type http://127.0.0.1:8000/cart, you should see following simple page for now.

image.png

We will fill this page dynamically with all our products in the cart. For this lets login as [email protected] with password as demo123 and add couple of products to the cart. If you notice from the last tutorial, in the session we have store the product id and its quantity in the cart. We will grab that product id to show all the information related to the product from the database, which we have done while creating the model. But since, information is stored in dictionary i.e. key-value pair, where product id is the key, we will need to grab that key. Only with that key, we can get all the information related to product like its name, price, picture etc. Lets open store/models.py and we will define a function to show product info by id.

class Product(models.Model):
    name = models.CharField(max_length=200, blank=False)
    price = models.IntegerField(default=0, blank=False)
    category = models.ForeignKey(Category, on_delete=models.CASCADE)
    description = models.TextField(max_length=600, blank=False)
    image = models.ImageField(null=True)

    def __str__(self):
        return self.name

    def get_products_by_id(cart_product_id):
        return Product.objects.filter(id__in=cart_product_id)


Lets come back to our views.py file and within our cart view, we will first grab list of product id (keys) within our cart. Then use the method get_products_by_id(cart_product_id) defined above to get the information of product.

def cart(request):
    cart_product_id = list(request.session.get('cart').keys())
    cart_product = Product.get_products_by_id(cart_product_id)
    print(cart_product)
    return render(request, 'store/cart.html')



If you have added couple products to your cart, then hit http://127.0.0.1:8000/cart/ and see the terminal (mine is below and I have use print statement above to see which products are in cart to make sure everything is working).

image.png

See the second last line to see the product name. Now we have grabbed the cart product, now lets show its info in our cart page by adding some design to it. For this first lets remove print statement from our cart view and we need to pass those products that we grabbed into dictionary if we want to render data dynamically to our cart template i.e. cart.html. So this is our final cart view inside views.py.

def cart(request):
    cart_product_id = list(request.session.get('cart').keys())
    cart_product = Product.get_products_by_id(cart_product_id)
    return render(request, 'store/cart.html', {'cart_product': cart_product})


Now lets replace the code inside store/cart.html, with this one:

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

{% load cart %}

{% block content %}


<div class="container text-white p-4">
    <p class="display-4">Your Cart</p>
    <hr style="background: white;">
    <table class="table">
        <thead>
        <tr class="bg-primary">
            <th>S.N</th>
            <th>Image</th>
            <th>Product</th>
            <th>Price</th>
            <th>Quantity</th>
            <th>Total</th>
        </tr>
        </thead>
        <tbody>
        {% for i in cart_product %}
        <tr>
            <td>{{forloop.counter}}</td>
            <td><img class="rounded-circle" height="60px" src="{{i.image.url}}"></td>
            <td>{{i.name}}</td>
            <td>{{i.price}}</td>
            <td>{{i|cart_quantity:request.session.cart}}</td>
            <td></td>
        </tr>
        {% endfor %}
        </tbody>
        <tfoot>
        <th colspan="4"></th>
        <th>Total</th>
        <th></th>
        </tfoot>
    </table>
    <hr style="background: white;">
    <div class="mb-5">
        <a href="" class="btn btn-primary float-right">Checkout</a>
    </div>
</div>
    {% endblock %}

There is nothing new which I have done here, we learn in the previous tutorial on how to render data dynamically to the template using for loop logic. In the last tutorial, we know how to use Django filter to show the cart quantity. And I have reused that same filter to know the number of product in the cart. If you go to your browser and type: http://127.0.0.1:8000/cart/, you should see following screen (it depends on which product you added to the cart and how much quantity).

image.png

You can see as of now that total price has been left blank. We will be again using a filter to show the total price. For this open store/templatetags/cart.py.

@register.filter(name='total_price')
def total_price(product, cart):
    return product.price * cart_quantity(product, cart)


We have registered a new filter called total_price which contains a function that accepts product and cart as a parameter and we returned the product price multiplied by the cart quantity. We have reused cart_quantity here again for total_price. You can see we have created a cart_quantity filter in the previous tutorial. Now time to use this filter in our cart.html to show the total price. Put this code in <td> which we have left empty to show the total price.

<td>{{i|total_price:request.session.cart}}</td>


Come to your browser and you will now see the total price of each product.

image.png

You can see the total price. Now, lets show the total sum of all products that you can see is blank just above checkout button and right to Total. We will again use a filter for this too. So go to cart.py and put this code:

@register.filter(name='total_cart_price')
def total_cart_price(product, cart):
    sum = 0
    for i in product:
        sum += total_price(i, cart)
    return sum

Now lets use this filter in cart.html to show the sum of price of all prodcuts. Replace one of <th> we have left empty to show total product price with this one:

<th>{{i|total_cart_price:request.session.cart}}</th>


Now go to http://127.0.0.1:8000/cart/, you can see the following:

image.png

Now for last, we will use filter again for currency. In the above demo, you can see there's no currency attached for currency. You can have $, £, ₹ attached to it. But, I will attach $ for now to the price column and total column. For this open cart.py and register a filter as below:

@register.filter(name='currency')
def currency(price):
    return "$" + str(price)


Now lets use this filter inside cart.html where we want to show $ sign. We have total of three places, where we need to show this. One in Price column, another in Total Column and the last one is in again Total column that is just one step above Checkout. The final code in cart.html reflecting these changes is:

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

{% load cart %}

{% block content %}


<div class="container text-white p-4">
    <p class="display-4">Your Cart</p>
    <hr style="background: white;">
    <table class="table">
        <thead>
        <tr class="bg-primary">
            <th>S.N</th>
            <th>Image</th>
            <th>Product</th>
            <th>Price</th>
            <th>Quantity</th>
            <th>Total</th>
        </tr>
        </thead>
        <tbody>
        {% for i in cart_product %}
        <tr>
            <td>{{forloop.counter}}</td>
            <td><img class="rounded-circle" height="60px" src="{{i.image.url}}"></td>
            <td>{{i.name}}</td>
            <td>{{i.price|currency}}</td>
            <td>{{i|cart_quantity:request.session.cart}}</td>
            <td>{{i|total_price:request.session.cart|currency}}</td>
        </tr>
        {% endfor %}
        </tbody>
        <tfoot>
        <th colspan="4"></th>
        <th>Total</th>
        <th>{{i|total_cart_price:request.session.cart|currency}}</th>
        </tfoot>
    </table>
    <hr style="background: white;">
    <div class="mb-5">
        <a href="" class="btn btn-primary float-right">Checkout</a>
    </div>
</div>
{% endblock %}


Now, refresh your browser and you will finally see the price with your desired currency.

image.png


Find the seventh part source code here.
Find the sample product images here.

Getting Started

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

Note: All the user in the dashboard has been saved with common password i.e. demo123 if you like to test the functionality of the project.