DEV Community

Cover image for Build an E-Commerce Site with Wagtail CMS, Bootstrap & Django Framework.
paulsaul621
paulsaul621

Posted on

Build an E-Commerce Site with Wagtail CMS, Bootstrap & Django Framework.

In this blog, we build a fully functional e-commerce site with Django and Wagtail CMS. I will also be briefly discussing:

  • Why choose django framework to build a custom e-commerce website
  • Which One is Better For Your Website? Django CMS Vs WordPress

You can check the demo of the final product at the following URL

Why choose Django for e-commerce?

First, here are some Django features to consider if you're looking for the right framework to build a shop.

→ Scalability

Django is perfect for e-commerce startups, as it's a good fit for small websites and can scale perfectly with business growth. You can rely on Django to handle hundreds/thousands of visitors at a time. It's built with independent components you can unplug or replace depending on your needs at any specific time.

→ Security

With e-commerce, you want to make sure merchants and clients alike feel safe through your shopping experience. Django prevents a whole lot of common security mistakes, often weakening traditional PHP CMSs. For instance, Django hides your site's source code from direct viewing on the web by dynamically generating web pages.

→ Feature-rich

Compared to most frameworks, Django comes with way more features out-of-the-box. It allows you to build an app right off the bat. Perfect for supporting your online store with functionalities such as user authentification, content management, or RSS feed. If something seems to be missing, you can count on Django's community and plugins ecosystem to extend your app!

→ SEO-friendly

SEO is paramount for any online business. Django advocates best practices for SEO. Human-readable URLs and sitemap features are sure to please any marketing team.

Oh, and also, it's fast, which is always great for both customer experience and SEO.

→ Reliable

Django makes it easy to build custom e-commerce websites. It has a modern, easy-to-use Python Web Framework that encourages rapid development and scalable architecture. Its high level abstraction make it easier to build, connect, configure, and maintain applications. In addition, Django makes it easy to add caching, secure authentication, and URL-agnostic views

Which One is Better For Your E-commerce Website? Django CMS Vs WordPress

Django database API makes it easy to work with databases. Django has a robust and secure authentication system. Django has a built in template engine, which means that you do not need to use a separate template language. Django is a free, open source web framework to! Django also follows the DRY(Don't Repeat Yourself) principle, DRY is the principle of reducing repetition in the code, referring back to a single source—or "snippet"—of reusable code whenever you need it.
Django also removes a lot of overhead. Develiopers do not have to worry about making their programs compatible with different versions of Python, which means they can spend more time about the application.

Wagtail CMS + Snipcart e-commerce

Wagtail is a developer-first Django content management system. Free and open-source, the good-hearted folks at Torchbox developed it. It's elegant, flexible, and, IMHO, kicks ass.

In the following Wagtail tutorial, the CMS will be in charge of creating and managing products that users will then be able to buy through a shopping cart.

By the end of it, you'll have a solid Django-powered e-commerce site up and running.

Below is a screenshot of the shop we are going to build:

Screenshot from 2022-02-04 02-54-05.png

bio.png

Shall we begin!

Pre-requisites

  • A Snipcart account.

1. Creating a Wagtail app

Use pip, which is packaged with Python, to install Wagtail and its dependencies:

$ python3 -m  venv venv
$ source venv/bin/activate
$ pip install wagtail requests
Enter fullscreen mode Exit fullscreen mode

Open a terminal and launch a new Wagtail site:

$ wagtail start snipcartwagtaildemo
$ cd snipcartwagtaildemo
Enter fullscreen mode Exit fullscreen mode

We have an extra step to complete the Wagtail setup, and it's to install the wagtail.contrib.settings plugin that we'll require later on.

In your new Wagtail project, open the base.py file located in snipcartwaigtaildemo/settings folder. Then, add wagtail.contrib.settings to the INSTALLED_APPS array.

# ./setting/base.py
INSTALLED_APPS = [
    ...,
    'wagtail.contrib.settings'
]
Enter fullscreen mode Exit fullscreen mode

1.1 Models Creation.

The first thing you need to do is create your Page models. Wagtail uses these Django models to generate a page type.

Open the models.py file located in the home folder of your product. This is where you'll define all your custom models.

Create two different models:

  • Product: defines the product you're selling.

    • ProductCustomField: defines a single product custom field.

Let's begin by importing the required modules:

# ./home/models.py

from django.db import models

from wagtail.core.models import Page

from modelcluster.fields import ParentalKey

from wagtail.core.models import Page, Orderable
from wagtail.admin.edit_handlers import FieldPanel, MultiFieldPanel, InlinePanel
from wagtail.images.edit_handlers import ImageChooserPanel
from wagtail.contrib.settings.models import BaseSetting, register_setting
from django.core.paginator import EmptyPage, PageNotAnInteger, Paginator
from django.template.response import TemplateResponse
from django.core.paginator import Paginator
Enter fullscreen mode Exit fullscreen mode

Now add the Product model:

# ./home/models.py

class Product(Page):
    def get_context(self, request):
        context = super().get_context(request)
        fields = []
        for f in self.custom_fields.get_object_list():
            if f.options:
                f.options_array = f.options.split('|')
                fields.append(f)
            else:
                fields.append(f)

        context['custom_fields'] = fields

        return context
    sku = models.CharField(max_length=255)
    short_description = models.TextField(blank=True, null=True)
    price = models.DecimalField(decimal_places=2, max_digits=10)
    image = models.ForeignKey(
        'wagtailimages.Image',
        null=True,
        blank=True,
        on_delete=models.SET_NULL,
        related_name='+'
    )

    content_panels = Page.content_panels + [
        FieldPanel('sku'),
        FieldPanel('price'),
        ImageChooserPanel('image'),
        FieldPanel('short_description'),
        InlinePanel('custom_fields', label='Custom fields'),
    ]

Enter fullscreen mode Exit fullscreen mode

Update the homepage class which is responsible for listing products on the homepage only. I will be listing only 6 products on the homepage. In any Wagtail Page, you can override a method name get_context. You can add the data that the view will receive in parameters

class HomePage(Page):
    def get_context(self, request):
        context = super().get_context(request)

        context['products'] = Product.objects.child_of(self).live()[:6]

        return context
Enter fullscreen mode Exit fullscreen mode

And ProductCustomField:

# ./home/models.py

class ProductCustomField(Orderable):
    product = ParentalKey(Product, on_delete=models.CASCADE, related_name='custom_fields')
    name = models.CharField(max_length=255)
    options = models.CharField(max_length=500, null=True, blank=True)

    panels = [
        FieldPanel('name'),
        FieldPanel('options')
    ]
Enter fullscreen mode Exit fullscreen mode

We need to add one more view that will be responsible for showing all products in our online shop, and for paginating the page. I will be paginating on the backend side as loading all the objects on a page is not very efficient. We can however cache our data and load it all on the front end. This will make the pagination seamless, this all depends on the size of your database.

def shop(request):
    products = Product.objects.live()
    p = Paginator(products, 10)
    #shows number of items in page
    totalProducts = (p.count)
    pageNum = request.GET.get('page', 1)
    page1 = p.page(pageNum)

    return TemplateResponse(request, 'home/shop.html', {
        'products': products,
        'dataSaved':page1
    })
Enter fullscreen mode Exit fullscreen mode

Finally, add the about us and contact us section views, like so:

def about(request):
    return TemplateResponse(request, 'home/about.html', {
    }) 

def contact(request):
    return TemplateResponse(request, 'home/contact.html', {
    })

Enter fullscreen mode Exit fullscreen mode

2. Adding Snipcart configuration settings

Let's make sure you can update the Snipcart API key directly from Wagtail's dashboard.

You'll need to add site settings to do so.

Site settings are special fields that you can add to your models.py file. They'll appear in the Wagtail Settings section of the dashboard.

Import this module:

# ./home/models.py

from wagtail.contrib.settings.models import BaseSetting, register_setting
Enter fullscreen mode Exit fullscreen mode

Then add these:

# ./home/models.py

@register_setting
class SnipcartSettings(BaseSetting):
    api_key = models.CharField(
        max_length=255,
        help_text='Your Snipcart public API key'
    )
Enter fullscreen mode Exit fullscreen mode

3. Database migrations

Now that your models are created, you'll need to generate database migrations and run them.

In your terminal, use the makemigrations command:

$ python3 manage.py makemigrations
$ python3 manage.py migrate
Enter fullscreen mode Exit fullscreen mode

Finally, create your first CMS user with the createsuperuser command:

$ python3 manage.py createsuperuser
Enter fullscreen mode Exit fullscreen mode

It will ask for an email address, a username, and to create a password. Don't forget the username and password you picked; you will need them to log into Wagtail's dashboard.

4. Creating products

Fire up your server like so:

$ python3 manage.py runserver
Enter fullscreen mode Exit fullscreen mode

Now open your browser and navigate to http://127.0.0.1:8000/admin. Use the credentials you set up earlier to log in.

Select the Home page in Wagtail's menu. Then click on the Add child page button.

You'll be asked to pick a type of page, select Home page and give it a name.

Go back to the Home page menu and click Add child page button under your newly created page.

1629928245-addchildproduct.webp

Once again, you'll be asked to pick a type of page, this time select Product.

1629928289-wagtailcreateproduct.webp

You can create as many products as you wish.

4.1 Adding Snipcart API key

Remember the SnipcartSettings class you created? You'll be able to configure your API key by expanding the Settings menu and going to Snipcart settings.

1629928362-snipcart-setting-wagtail.webp

Open Snipcart's dashboard and get your public API key (Test or Live), go back to Wagtail, and paste it in the API key field.

Save your settings.

Adding new URL routes

I have barely used wagtail and i am not very familiar with how the URL's are handled but i created my own URL for the shop view, for listing all products:

from django.conf import settings
from django.urls import include, path
from django.contrib import admin

from wagtail.admin import urls as wagtailadmin_urls
from wagtail.core import urls as wagtail_urls
from wagtail.documents import urls as wagtaildocs_urls

from search import views as search_views
from home import models as p
urlpatterns = [
    path('django-admin/', admin.site.urls),

    path('admin/', include(wagtailadmin_urls)),
    path('documents/', include(wagtaildocs_urls)),

    path('search/', search_views.search, name='search'),
    path('shop/', p.shop, name='shop'),
    path('about/', p.about, name='about'),
    path('contact/', p.contact, name='contact'),

]


if settings.DEBUG:
    from django.conf.urls.static import static
    from django.contrib.staticfiles.urls import staticfiles_urlpatterns

    # Serve static and media files from development server
    urlpatterns += staticfiles_urlpatterns()
    urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)

urlpatterns = urlpatterns + [
    # For anything not caught by a more specific rule above, hand over to
    # Wagtail's page serving mechanism. This should be the last pattern in
    # the list:
    path("", include(wagtail_urls)),

    # Alternatively, if you want Wagtail pages to be served from a subpath
    # of your site, rather than the site root:
    #    path("pages/", include(wagtail_urls)),
]

Enter fullscreen mode Exit fullscreen mode

5. Templating

Let us now proceed with creating our HTML files and modifying our application UI. The template is from this Colorlib URL.

Homepage

On our homepage, we need to display a few items from our database. We already specified 6 items on our homepage view. Create 3 files on the home/templates/home folder:

  • home_page.html
  • product.html
  • shop.html

Before i display the whole code, let me explain a few important bits, starting with the head of the HTML files

{% load static wagtailcore_tags wagtailuserbar %}
{% load wagtailimages_tags %}
<!DOCTYPE html>
<html lang="en">

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

  {% load static wagtailsettings_tags %}
    {% get_settings %}

    {# Global stylesheets #}
    <link href="https://fonts.googleapis.com/css?family=Rubik:400,700|Crimson+Text:400,400i" rel="stylesheet">
    <link rel="stylesheet" href="{% static 'fonts/icomoon/style.css' %}">

    <link rel="stylesheet" href="{% static 'css/bootstrap.min.css' %}">
    <link rel="stylesheet" href="{% static 'css/magnific-popup.css' %}">
    <link rel="stylesheet" href="{% static 'css/jquery-ui.css' %}">
    <link rel="stylesheet" href="{% static 'css/owl.carousel.min.css' %}">
    <link rel="stylesheet" href="{% static 'css/owl.theme.default.min.css' %}">


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

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

    {# Snipcart #}

    {% if settings.home.SnipcartSettings.api_key %}
  <script async src="https://cdn.snipcart.com/themes/v3.2.1/default/snipcart.js"></script>
  <div hidden id="snipcart" data-api-key="{{ settings.home.SnipcartSettings.api_key }}"></div>
  <link rel="stylesheet" href="https://cdn.snipcart.com/themes/v3.2.1/default/snipcart.css" />
    {% endif %}

</head>
Enter fullscreen mode Exit fullscreen mode

We have the reference for the snipcart tags where it detects the API key we added on the settings file and have reference for the snipcart files as cdn files.

<div class="row">
          {% for product in products %}
          <div class="col-sm-6 col-lg-4 text-center item mb-4">
            <a href="{{ product.get_url }}">
                {% image product.image fill-370x270 as tmp_image %}
                <img src="{{ tmp_image.url }}" alt="Image">
            </a>
            <h3 class="text-dark"><a href="{{ product.get_url }}">{{ product.title }}</a></h3>
            <p class="price">Ksh. {{ product.price }}</p>
          </div>
          {% endfor %}
        </div>
Enter fullscreen mode Exit fullscreen mode

We also have the product part that loops through the products and displays them all. For more details, you can visit the snip cart docs.
The whole code for the homepage is:

{% load static wagtailcore_tags wagtailuserbar %}
{% load wagtailimages_tags %}
<!DOCTYPE html>
<html lang="en">

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

  {% load static wagtailsettings_tags %}
    {% get_settings %}

    {# Global stylesheets #}
    <link href="https://fonts.googleapis.com/css?family=Rubik:400,700|Crimson+Text:400,400i" rel="stylesheet">
    <link rel="stylesheet" href="{% static 'fonts/icomoon/style.css' %}">

    <link rel="stylesheet" href="{% static 'css/bootstrap.min.css' %}">
    <link rel="stylesheet" href="{% static 'css/magnific-popup.css' %}">
    <link rel="stylesheet" href="{% static 'css/jquery-ui.css' %}">
    <link rel="stylesheet" href="{% static 'css/owl.carousel.min.css' %}">
    <link rel="stylesheet" href="{% static 'css/owl.theme.default.min.css' %}">


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

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

    {# Snipcart #}

    {% if settings.home.SnipcartSettings.api_key %}
  <script async src="https://cdn.snipcart.com/themes/v3.2.1/default/snipcart.js"></script>
  <div hidden id="snipcart" data-api-key="{{ settings.home.SnipcartSettings.api_key }}"></div>
  <link rel="stylesheet" href="https://cdn.snipcart.com/themes/v3.2.1/default/snipcart.css" />
    {% endif %}

</head>

<body>
    {% wagtailuserbar %}
  <div class="site-wrap">


    <div class="site-navbar py-2">

      <div class="search-wrap">
        <div class="container">
          <a href="#" class="search-close js-search-close"><span class="icon-close2"></span></a>
          <form action="#" method="post">
            <input type="text" class="form-control" placeholder="Search keyword and hit enter...">
          </form>
        </div>
      </div>

      <div class="container">
        <div class="d-flex align-items-center justify-content-between">
          <div class="logo">
            <div class="site-logo">
              <a href="/" class="js-logo-clone">Tafari Pharmacy</a>
            </div>
          </div>
          <div class="main-nav d-none d-lg-block">
            <nav class="site-navigation text-right text-md-center" role="navigation">
              <ul class="site-menu js-clone-nav d-none d-lg-block">
                <li class="active"><a href="/">Home</a></li>
                {% if request.path == '/' %}
                <li><a href="/shop">Store</a></li>
                {% endif %}
                <li class="has-children">
                  <a href="#">Medications</a>
                  <ul class="dropdown">
                    <li><a href="#">Supplements</a></li>
                    <li class="has-children">
                      <a href="#">Vitamins</a>
                      <ul class="dropdown">
                        <li><a href="#">Supplements</a></li>
                        <li><a href="#">Vitamins</a></li>
                        <li><a href="#">Diet &amp; Nutrition</a></li>
                        <li><a href="#">Tea &amp; Coffee</a></li>
                      </ul>
                    </li>
                    <li><a href="#">Diet &amp; Nutrition</a></li>
                    <li><a href="#">Tea &amp; Coffee</a></li>

                  </ul>
                </li>
                <li><a href="/about">About</a></li>
                <li><a href="/contact">Contact</a></li>
              </ul>
            </nav>
          </div>
          <div class="icons">
            <a href="#" class="icons-btn d-inline-block js-search-open"><span class="icon-search"></span></a>
            <a href="" class="icons-btn d-inline-block bag">
              <span class="snipcart-checkout icon-shopping-bag"></span>
              <span class="snipcart-checkout number snipcart-items-count"></span>
            </a>
            <a href="#" class="site-menu-toggle js-menu-toggle ml-3 d-inline-block d-lg-none"><span
                class="icon-menu"></span></a>
          </div>
        </div>
      </div>
    </div>

    <div class="site-blocks-cover" style="background-image: url('{% static 'images/hero_1.jpg' %}');">
      <div class="container">
        <div class="row">
          <div class="col-lg-7 mx-auto order-lg-2 align-self-center">
            <div class="site-block-cover-content text-center">
              <h2 class="sub-title">Effective Medicine, New Medicine Everyday</h2>
              <h1>Welcome To Tafari Pharmacy</h1>
              <p>
                {% if request.path == '/' %}
                <a href="#" class="btn btn-primary px-5 py-3">Shop Now</a>
                {% endif %}
              </p>
            </div>
          </div>
        </div>
      </div>
    </div>

    <div class="site-section">
      <div class="container">
        <div class="row align-items-stretch section-overlap">
          <div class="col-md-6 col-lg-4 mb-4 mb-lg-0">
            <div class="banner-wrap bg-primary h-100">
              <a href="#" class="h-100">
                <h5>Free <br> Delivery</h5>
                <p>
                  Amet sit amet dolor
                  <strong>Lorem, ipsum dolor sit amet consectetur adipisicing.</strong>
                </p>
              </a>
            </div>
          </div>
          <div class="col-md-6 col-lg-4 mb-4 mb-lg-0">
            <div class="banner-wrap h-100">
              <a href="#" class="h-100">
                <h5>Season <br> Sale 50% Off</h5>
                <p>
                  Amet sit amet dolor
                  <strong>Lorem, ipsum dolor sit amet consectetur adipisicing.</strong>
                </p>
              </a>
            </div>
          </div>
          <div class="col-md-6 col-lg-4 mb-4 mb-lg-0">
            <div class="banner-wrap bg-warning h-100">
              <a href="#" class="h-100">
                <h5>Buy <br> A Gift Card</h5>
                <p>
                  Amet sit amet dolor
                  <strong>Lorem, ipsum dolor sit amet consectetur adipisicing.</strong>
                </p>
              </a>
            </div>
          </div>

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

    <div class="site-section">
      <div class="container">
        <div class="row">
          <div class="title-section text-center col-12">
            <h2 class="text-uppercase">Popular Products</h2>
          </div>
        </div>


        <div class="row">
          {% for product in products %}
          <div class="col-sm-6 col-lg-4 text-center item mb-4">
            <a href="{{ product.get_url }}">
                {% image product.image fill-370x270 as tmp_image %}
                <img src="{{ tmp_image.url }}" alt="Image">
            </a>
            <h3 class="text-dark"><a href="{{ product.get_url }}">{{ product.title }}</a></h3>
            <p class="price">Ksh. {{ product.price }}</p>
          </div>
          {% endfor %}
        </div>


        <div class="row mt-5">
          <div class="col-12 text-center">
            {% if request.path == '/' %}
            <a href="/shop" class="btn btn-primary px-4 py-3">View All Products</a>
            {% endif %}
          </div>
        </div>
      </div>
    </div>


    <div class="site-section bg-light">
      <div class="container">
        <div class="row">
          <div class="title-section text-center col-12">
            <h2 class="text-uppercase">New Products</h2>
          </div>
        </div>
        <div class="row">
          <div class="col-md-12 block-3 products-wrap">
            <div class="nonloop-block-3 owl-carousel">

              <div class="text-center item mb-4">
                <a href="shop-single.html"> <img src="{% static 'images/product_03.png' %}" alt="Image"></a>
                <h3 class="text-dark"><a href="shop-single.html">Umcka Cold Care</a></h3>
                <p class="price">$120.00</p>
              </div>

              <div class="text-center item mb-4">
                <a href="shop-single.html"> <img src="{% static 'images/product_01.png' %}" alt="Image"></a>
                <h3 class="text-dark"><a href="shop-single.html">Umcka Cold Care</a></h3>
                <p class="price">$120.00</p>
              </div>

              <div class="text-center item mb-4">
                <a href="shop-single.html"> <img src="{% static 'images/product_02.png' %}" alt="Image"></a>
                <h3 class="text-dark"><a href="shop-single.html">Umcka Cold Care</a></h3>
                <p class="price">$120.00</p>
              </div>

              <div class="text-center item mb-4">
                <a href="shop-single.html"> <img src="{% static 'images/product_04.png' %}" alt="Image"></a>
                <h3 class="text-dark"><a href="shop-single.html">Umcka Cold Care</a></h3>
                <p class="price">$120.00</p>
              </div>

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

    <div class="site-section">
      <div class="container">
        <div class="row">
          <div class="title-section text-center col-12">
            <h2 class="text-uppercase">Testimonials</h2>
          </div>
        </div>
        <div class="row">
          <div class="col-md-12 block-3 products-wrap">
            <div class="nonloop-block-3 no-direction owl-carousel">

              <div class="testimony">
                <blockquote>
                  <img src="{% static 'images/person_1.jpg' %}" alt="Image" class="img-fluid w-25 mb-4 rounded-circle">
                  <p>&ldquo;Lorem ipsum dolor, sit amet consectetur adipisicing elit. Nemo omnis voluptatem consectetur quam tempore obcaecati maiores voluptate aspernatur iusto eveniet, placeat ab quod tenetur ducimus. Minus ratione sit quaerat unde.&rdquo;</p>
                </blockquote>

                <p>&mdash; Kelly Holmes</p>
              </div>

              <div class="testimony">
                <blockquote>
                  <img src="{% static 'images/person_2.jpg' %}" alt="Image" class="img-fluid w-25 mb-4 rounded-circle">
                  <p>&ldquo;Lorem ipsum dolor, sit amet consectetur adipisicing elit. Nemo omnis voluptatem consectetur quam tempore
                    obcaecati maiores voluptate aspernatur iusto eveniet, placeat ab quod tenetur ducimus. Minus ratione sit quaerat
                    unde.&rdquo;</p>
                </blockquote>

                <p>&mdash; Rebecca Morando</p>
              </div>

              <div class="testimony">
                <blockquote>
                  <img src="{% static 'images/person_3.jpg' %}" alt="Image" class="img-fluid w-25 mb-4 rounded-circle">
                  <p>&ldquo;Lorem ipsum dolor, sit amet consectetur adipisicing elit. Nemo omnis voluptatem consectetur quam tempore
                    obcaecati maiores voluptate aspernatur iusto eveniet, placeat ab quod tenetur ducimus. Minus ratione sit quaerat
                    unde.&rdquo;</p>
                </blockquote>

                <p>&mdash; Lucas Gallone</p>
              </div>

              <div class="testimony">
                <blockquote>
                  <img src="{% static 'images/person_4.jpg' %}" alt="Image" class="img-fluid w-25 mb-4 rounded-circle">
                  <p>&ldquo;Lorem ipsum dolor, sit amet consectetur adipisicing elit. Nemo omnis voluptatem consectetur quam tempore
                    obcaecati maiores voluptate aspernatur iusto eveniet, placeat ab quod tenetur ducimus. Minus ratione sit quaerat
                    unde.&rdquo;</p>
                </blockquote>

                <p>&mdash; Andrew Neel</p>
              </div>

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

    <div class="site-section bg-secondary bg-image" style="background-image: url('{% static 'images/bg_2.jpg' %}');">
      <div class="container">
        <div class="row align-items-stretch">
          <div class="col-lg-6 mb-5 mb-lg-0">
            <a href="#" class="banner-1 h-100 d-flex" style="background-image: url('{% static 'images/bg_1.jpg' %}');">
              <div class="banner-1-inner align-self-center">
                <h2>Tafari Pharmacy Products</h2>
                <p>Lorem, ipsum dolor sit amet consectetur adipisicing elit. Molestiae ex ad minus rem odio voluptatem.
                </p>
              </div>
            </a>
          </div>
          <div class="col-lg-6 mb-5 mb-lg-0">
            <a href="#" class="banner-1 h-100 d-flex" style="background-image: url('{% static 'images/bg_2.jpg' %}');">
              <div class="banner-1-inner ml-auto  align-self-center">
                <h2>Rated by Experts</h2>
                <p>Lorem, ipsum dolor sit amet consectetur adipisicing elit. Molestiae ex ad minus rem odio voluptatem.
                </p>
              </div>
            </a>
          </div>
        </div>
      </div>
    </div>


    <footer class="site-footer">
      <div class="container">
        <div class="row">
          <div class="col-md-6 col-lg-3 mb-4 mb-lg-0">

            <div class="block-7">
              <h3 class="footer-heading mb-4">About Us</h3>
              <p>Lorem ipsum dolor sit amet, consectetur adipisicing elit. Eius quae reiciendis distinctio voluptates
                sed dolorum excepturi iure eaque, aut unde.</p>
            </div>

          </div>
          <div class="col-lg-3 mx-auto mb-5 mb-lg-0">
            <h3 class="footer-heading mb-4">Quick Links</h3>
            <ul class="list-unstyled">
              <li><a href="#">Supplements</a></li>
              <li><a href="#">Vitamins</a></li>
              <li><a href="#">Diet &amp; Nutrition</a></li>
              <li><a href="#">Tea &amp; Coffee</a></li>
            </ul>
          </div>

          <div class="col-md-6 col-lg-3">
            <div class="block-5 mb-5">
              <h3 class="footer-heading mb-4">Contact Info</h3>
              <ul class="list-unstyled">
                <li class="address">203 Fake St. Mountain View, San Francisco, California, USA</li>
                <li class="phone"><a href="tel://23923929210">+2 392 3929 210</a></li>
                <li class="email">emailaddress@domain.com</li>
              </ul>
            </div>


          </div>
        </div>
        <div class="row pt-5 mt-5 text-center">
          <div class="col-md-12">
            <p>
              <!-- Link back to Colorlib can't be removed. Template is licensed under CC BY 3.0. -->
              Copyright &copy;
              <script>document.write(new Date().getFullYear());</script> All rights reserved | This template is made
              with <i class="icon-heart" aria-hidden="true"></i> by <a href="https://colorlib.com" target="_blank"
                class="text-primary">Colorlib</a>
              <!-- Link back to Colorlib can't be removed. Template is licensed under CC BY 3.0. -->
            </p>
          </div>

        </div>
      </div>
    </footer>
  </div>

  <script src="{% static 'js/jquery-3.3.1.min.js' %}"></script>
  <script src="{% static 'js/jquery-ui.js' %}"></script>
  <script src="{% static 'js/popper.min.js' %}"></script>
  <script src="{% static 'js/bootstrap.min.js' %}"></script>
  <script src="{% static 'js/owl.carousel.min.js' %}"></script>
  <script src="{% static 'js/jquery.magnific-popup.min.js' %}"></script>
  <script src="{% static 'js/aos.js' %}"></script>

  <script src="{% static 'js/main.js' %}"></script>

</body>

</html>
Enter fullscreen mode Exit fullscreen mode

All Products Page

On the product.html, add the lines of code below. I didn't use the inbuilt django templates functionality to work in components so we have to repeat the tags we added in the homepage file, for every page we create. Copy paste the code below on the templates/home/shop.html:

{% load static wagtailcore_tags wagtailuserbar %}
{% load wagtailimages_tags %}
<!DOCTYPE html>
<html lang="en">

<head>
  <title>Tafari Tafari Pharmacycy &mdash; Colorlib Template</title>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">

  {% load static wagtailsettings_tags %}
    {% get_settings %}

    {# Global stylesheets #}
    <link href="https://fonts.googleapis.com/css?family=Rubik:400,700|Crimson+Text:400,400i" rel="stylesheet">
    <link rel="stylesheet" href="{% static 'fonts/icomoon/style.css' %}">

    <link rel="stylesheet" href="{% static 'css/bootstrap.min.css' %}">
    <link rel="stylesheet" href="{% static 'css/magnific-popup.css' %}">
    <link rel="stylesheet" href="{% static 'css/jquery-ui.css' %}">
    <link rel="stylesheet" href="{% static 'css/owl.carousel.min.css' %}">
    <link rel="stylesheet" href="{% static 'css/owl.theme.default.min.css' %}">


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

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

    {# Snipcart #}

    {% if settings.home.SnipcartSettings.api_key %}
  <script async src="https://cdn.snipcart.com/themes/v3.2.1/default/snipcart.js"></script>
  <div hidden id="snipcart" data-api-key="{{ settings.home.SnipcartSettings.api_key }}"></div>
  <link rel="stylesheet" href="https://cdn.snipcart.com/themes/v3.2.1/default/snipcart.css" />
    {% endif %}

</head>

<body>
    {% wagtailuserbar %}

  <div class="site-wrap">


    <div class="site-navbar py-2">

      <div class="search-wrap">
        <div class="container">
          <a href="#" class="search-close js-search-close"><span class="icon-close2"></span></a>
          <form action="#" method="post">
            <input type="text" class="form-control" placeholder="Search keyword and hit enter...">
          </form>
        </div>
      </div>

      <div class="container">
        <div class="d-flex align-items-center justify-content-between">
          <div class="logo">
            <div class="site-logo">
              <a href="/" class="js-logo-clone">Tafari Pharmacy</a>
            </div>
          </div>
          <div class="main-nav d-none d-lg-block">
            <nav class="site-navigation text-right text-md-center" role="navigation">
                <ul class="site-menu js-clone-nav d-none d-lg-block">
                  <li><a href="/">Home</a></li>
                  <li class="active"><a href="/shop">Store</a></li>
                  <li class="has-children">
                    <a href="#">Medications</a>
                    <ul class="dropdown">
                      <li><a href="#">Supplements</a></li>
                      <li class="has-children">
                        <a href="#">Vitamins</a>
                        <ul class="dropdown">
                          <li><a href="#">Supplements</a></li>
                          <li><a href="#">Vitamins</a></li>
                          <li><a href="#">Diet &amp; Nutrition</a></li>
                          <li><a href="#">Tea &amp; Coffee</a></li>
                        </ul>
                      </li>
                      <li><a href="#">Diet &amp; Nutrition</a></li>
                      <li><a href="#">Tea &amp; Coffee</a></li>

                    </ul>
                  </li>
                  <li><a href="/about">About</a></li>
                  <li><a href="/contact">Contact</a></li>
                </ul>
              </nav>
          </div>
          <div class="icons">
            <a href="#" class="icons-btn d-inline-block js-search-open"><span class="icon-search"></span></a>
            <a href="" class="icons-btn d-inline-block bag">
                <span class="snipcart-checkout icon-shopping-bag"></span>
                <span class="snipcart-checkout number snipcart-items-count"></span>
              </a>
            <a href="#" class="site-menu-toggle js-menu-toggle ml-3 d-inline-block d-lg-none"><span
                class="icon-menu"></span></a>
          </div>
        </div>
      </div>
    </div>

    <div class="bg-light py-3">
      <div class="container">
        <div class="row">
          <div class="col-md-12 mb-0"><a href="/">Home</a> <span class="mx-2 mb-0">/</span> <strong class="text-black">Store</strong></div>
        </div>
      </div>
    </div>

    <div class="site-section">
      <div class="container">



        {% for product in dataSaved.object_list %}
        <div class="row">
          <div class="col-sm-6 col-lg-4 text-center item mb-4">
            <a href="{{ product.get_url }}">
                {% image product.image fill-370x270 as tmp_image %}
                <img src="{{ tmp_image.url }}" alt="Image">
            </a>
            <h3 class="text-dark"><a href="{{ product.get_url }}">{{ product.title }}</a></h3>
            <p class="price">Ksh. {{ product.price }}</p>
          </div>
        </div>
        {% endfor %}
        <div class="row mt-5">
          <div class="col-md-12 text-center">
            <div class="site-block-27">
                {% if dataSaved.has_other_pages %}
                <ul class="pagination justify-content-center">
                    {% if dataSaved.has_previous %}
                  <li class="page-item">
                    <a class="page-link" href="?page={{ dataSaved.previous_page_number }}" tabindex="-1">Previous</a>
                  </li>
                  {% endif %}

                  {% for i in dataSaved.paginator.page_range %}
                     {% if dataSaved.number == i %}
                  <li class="page-item active">
                      <a class="page-link" href="#">{{ i }}<span class="sr-only">(current)</span></a>
                   </li>
                  {% endif %}
                  {% endfor %} 
                  <li class="page-item">
                    {% if dataSaved.has_next %}
                    <a class="page-link" href="?page={{ dataSaved.next_page_number }}">Next</a>
                    {% endif %}
                  </li>
                </ul>
                {% endif %}
            </div>
          </div>
        </div>
      </div>
    </div>


    <div class="site-section bg-secondary bg-image" style="background-image: url('{% static 'images/bg_2.jpg' %}');">
      <div class="container">
        <div class="row align-items-stretch">
          <div class="col-lg-6 mb-5 mb-lg-0">
            <a href="#" class="banner-1 h-100 d-flex" style="background-image: url('{% static 'images/bg_1.jpg' %}');">
              <div class="banner-1-inner align-self-center">
                <h2>Tafari Pharmacy Products</h2>
                <p>Lorem, ipsum dolor sit amet consectetur adipisicing elit. Molestiae ex ad minus rem odio voluptatem.
                </p>
              </div>
            </a>
          </div>
          <div class="col-lg-6 mb-5 mb-lg-0">
            <a href="#" class="banner-1 h-100 d-flex" style="background-image: url('{% static 'images/bg_2.jpg' %}');">
              <div class="banner-1-inner ml-auto  align-self-center">
                <h2>Rated by Experts</h2>
                <p>Lorem, ipsum dolor sit amet consectetur adipisicing elit. Molestiae ex ad minus rem odio voluptatem.
                </p>
              </div>
            </a>
          </div>
        </div>
      </div>
    </div>


    <footer class="site-footer">
      <div class="container">
        <div class="row">
          <div class="col-md-6 col-lg-3 mb-4 mb-lg-0">

            <div class="block-7">
              <h3 class="footer-heading mb-4">About Us</h3>
              <p>Lorem ipsum dolor sit amet, consectetur adipisicing elit. Eius quae reiciendis distinctio voluptates
                sed dolorum excepturi iure eaque, aut unde.</p>
            </div>

          </div>
          <div class="col-lg-3 mx-auto mb-5 mb-lg-0">
            <h3 class="footer-heading mb-4">Quick Links</h3>
            <ul class="list-unstyled">
              <li><a href="#">Supplements</a></li>
              <li><a href="#">Vitamins</a></li>
              <li><a href="#">Diet &amp; Nutrition</a></li>
              <li><a href="#">Tea &amp; Coffee</a></li>
            </ul>
          </div>

          <div class="col-md-6 col-lg-3">
            <div class="block-5 mb-5">
              <h3 class="footer-heading mb-4">Contact Info</h3>
              <ul class="list-unstyled">
                <li class="address">203 Fake St. Mountain View, San Francisco, California, USA</li>
                <li class="phone"><a href="tel://23923929210">+2 392 3929 210</a></li>
                <li class="email">emailaddress@domain.com</li>
              </ul>
            </div>


          </div>
        </div>
        <div class="row pt-5 mt-5 text-center">
          <div class="col-md-12">
            <p>
              <!-- Link back to Colorlib can't be removed. Template is licensed under CC BY 3.0. -->
              Copyright &copy;
              <script>document.write(new Date().getFullYear());</script> All rights reserved | This template is made
              with <i class="icon-heart" aria-hidden="true"></i> by <a href="https://colorlib.com" target="_blank"
                class="text-primary">Colorlib</a>
              <!-- Link back to Colorlib can't be removed. Template is licensed under CC BY 3.0. -->
            </p>
          </div>

        </div>
      </div>
    </footer>
  </div>

  <script src="{% static 'js/jquery-3.3.1.min.js' %}"></script>
  <script src="{% static 'js/jquery-ui.js' %}"></script>
  <script src="{% static 'js/popper.min.js' %}"></script>
  <script src="{% static 'js/bootstrap.min.js' %}"></script>
  <script src="{% static 'js/owl.carousel.min.js' %}"></script>
  <script src="{% static 'js/jquery.magnific-popup.min.js' %}"></script>
  <script src="{% static 'js/aos.js' %}"></script>

  <script src="{% static 'js/main.js' %}"></script>
  <script>
    document.addEventListener('DOMContentLoaded', function() {
        document.querySelector('.custom-field-select').onchange = function(event) {
            if (event.target.dataset.field) {
                document.querySelector('.snipcart-add-item')
                    .dataset['itemCustom' + event.target.dataset.field + 'Value'] = event.target.value;
            }
        };
    },false);
</script>
</body>

</html>
Enter fullscreen mode Exit fullscreen mode

The code is the same as the home page code, the only difference is in the shop.html file, we paginate our webpage, and display all products instead of just 6 like in the homepage.

Product details and cart display page

For the products page, we need to display the product when a user clicks on an item, like so:

Screenshot from 2022-02-06 20-58-59.png

In order to do this, we need to create a template that will be associated with the Product page model. On the product.html in the home/templates/home folder, add:

{% load static wagtailcore_tags wagtailuserbar %}
{% load wagtailimages_tags %}
<!DOCTYPE html>
<html lang="en">

<head>
  <title>Tafari Pharmacy &mdash; Colorlib Template</title>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">

  {% load static wagtailsettings_tags %}
    {% get_settings %}

    {# Global stylesheets #}
    <link href="https://fonts.googleapis.com/css?family=Rubik:400,700|Crimson+Text:400,400i" rel="stylesheet">
    <link rel="stylesheet" href="{% static 'fonts/icomoon/style.css' %}">

    <link rel="stylesheet" href="{% static 'css/bootstrap.min.css' %}">
    <link rel="stylesheet" href="{% static 'css/magnific-popup.css' %}">
    <link rel="stylesheet" href="{% static 'css/jquery-ui.css' %}">
    <link rel="stylesheet" href="{% static 'css/owl.carousel.min.css' %}">
    <link rel="stylesheet" href="{% static 'css/owl.theme.default.min.css' %}">


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

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

    {# Snipcart #}

    {% if settings.home.SnipcartSettings.api_key %}
  <script async src="https://cdn.snipcart.com/themes/v3.2.1/default/snipcart.js"></script>
  <div hidden id="snipcart" data-api-key="{{ settings.home.SnipcartSettings.api_key }}"></div>
  <link rel="stylesheet" href="https://cdn.snipcart.com/themes/v3.2.1/default/snipcart.css" />
    {% endif %}

</head>

<body>
    {% wagtailuserbar %}
  <div class="site-wrap">


    <div class="site-navbar py-2">

      <div class="search-wrap">
        <div class="container">
          <a href="#" class="search-close js-search-close"><span class="icon-close2"></span></a>
          <form action="#" method="post">
            <input type="text" class="form-control" placeholder="Search keyword and hit enter...">
          </form>
        </div>
      </div>

      <div class="container">
        <div class="d-flex align-items-center justify-content-between">
          <div class="logo">
            <div class="site-logo">
              <a href="/" class="js-logo-clone">Tafari Pharmacy</a>
            </div>
          </div>
          <div class="main-nav d-none d-lg-block">
            <nav class="site-navigation text-right text-md-center" role="navigation">
                <ul class="site-menu js-clone-nav d-none d-lg-block">
                  <li><a href="/">Home</a></li>
                  <li class="active"><a href="/shop">Store</a></li>
                  <li class="has-children">
                    <a href="#">Medications</a>
                    <ul class="dropdown">
                      <li><a href="#">Supplements</a></li>
                      <li class="has-children">
                        <a href="#">Vitamins</a>
                        <ul class="dropdown">
                          <li><a href="#">Supplements</a></li>
                          <li><a href="#">Vitamins</a></li>
                          <li><a href="#">Diet &amp; Nutrition</a></li>
                          <li><a href="#">Tea &amp; Coffee</a></li>
                        </ul>
                      </li>
                      <li><a href="#">Diet &amp; Nutrition</a></li>
                      <li><a href="#">Tea &amp; Coffee</a></li>

                    </ul>
                  </li>
                  <li><a href="/about">About</a></li>
                  <li><a href="/contact">Contact</a></li>
                </ul>
              </nav>
          </div>
          <div class="icons">
            <a href="#" class="icons-btn d-inline-block js-search-open"><span class="icon-search"></span></a>
            <a href="" class="icons-btn d-inline-block bag">
              <span class="snipcart-checkout icon-shopping-bag"></span>
              <span class="snipcart-checkout number snipcart-items-count"></span>
            </a>
            <a href="#" class="site-menu-toggle js-menu-toggle ml-3 d-inline-block d-lg-none"><span
                class="icon-menu"></span></a>
          </div>
        </div>
      </div>
    </div>

    <div class="bg-light py-3">
      <div class="container">
        <div class="row">
          <div class="col-md-12 mb-0"><a href="/">Home</a> <span class="mx-2 mb-0">/</span> <a
              href="/shop">Store</a> <span class="mx-2 mb-0">/</span> <strong class="text-black">{{ page.title }}</strong></div>
        </div>
      </div>
    </div>

    <div class="site-section">
      <div class="container">
        <div class="row">
          <div class="col-md-5 mr-auto">
            <div class="border text-center">
                {% image page.image max-370x270 as temp_image %}
              <img src="{{ temp_image.url }}" alt="Image" class="img-fluid p-5">
            </div>
          </div>
          <div class="col-md-6">
            <h2 class="text-black">{{ page.title }}</h2>
            <p>{{page.short_description}}</p>


            <p><del>$95.00</del>  <strong class="text-primary h4">Ksh. {{ page.price }}</strong></p>

            <p>
                {% for f in custom_fields %}
                    {% if f.options_array|length > 0 %}
                        <div class="form-group">
                            <label class="form-label" for="{{ f.name|lower }}">
                                {{ f.name }}:
                            </label>
                            <select class="form-select custom-field-select" id="{{ f.name|lower }}" data-field="{{ forloop.counter }}">
                                {% for opt in f.options_array %}
                                    <option>
                                        {{ opt }}
                                    </option>
                                {% endfor %}
                            </select>
                        </div>
                    {% endif %}
                {% endfor %}
            </p>


            <button class="snipcart-add-item btn btn-primary mt-6 py-2 px-4 bg-gradient-to-r from-green-400 to-blue-500 hover:from-pink-500 hover:to-yellow-500 text-white font-bold rounded-full shadow-offset hover:shadow-lg transition duration-300"
                data-item-name="{{ page.title }}"
                    data-item-id="{{ page.sku }}"
                    data-item-url="{{ page.get_full_url }}"
                    data-item-price="{{ page.price }}"
                    data-item-description="{{ page.short_description}}"
                    data-item-image="{{ temp_image.url }}"
                    {% for f in custom_fields %}
                        data-item-custom{{forloop.counter}}-name="{{f.name}}"
                        data-item-custom{{forloop.counter}}-options="{{f.options}}"
                    {% endfor %}>Add to cart
            </button>


            <div class="mt-5">
              <ul class="nav nav-pills mb-3 custom-pill" id="pills-tab" role="tablist">
                <li class="nav-item">
                  <a class="nav-link active" id="pills-home-tab" data-toggle="pill" href="#pills-home" role="tab"
                    aria-controls="pills-home" aria-selected="true">Ordering Information</a>
                </li>
                <li class="nav-item">
                  <a class="nav-link" id="pills-profile-tab" data-toggle="pill" href="#pills-profile" role="tab"
                    aria-controls="pills-profile" aria-selected="false">Specifications</a>
                </li>

              </ul>
              <div class="tab-content" id="pills-tabContent">
                <div class="tab-pane fade show active" id="pills-home" role="tabpanel" aria-labelledby="pills-home-tab">
                  <table class="table custom-table">
                    <thead>
                      <th>Material</th>
                      <th>Description</th>
                      <th>Packaging</th>
                    </thead>
                    <tbody>
                      <tr>
                        <th scope="row">OTC022401</th>
                        <td>Pain Management: Acetaminophen PM Extra-Strength Caplets, 500 mg, 100/Bottle</td>
                        <td>1 BT</td>
                      </tr>
                      <tr>
                        <th scope="row">OTC022401</th>
                        <td>Pain Management: Acetaminophen PM Extra-Strength Caplets, 500 mg, 100/Bottle</td>
                        <td>144/CS</td>
                      </tr>
                      <tr>
                        <th scope="row">OTC022401</th>
                        <td>Pain Management: Acetaminophen PM Extra-Strength Caplets, 500 mg, 100/Bottle</td>
                        <td>1 EA</td>
                      </tr>

                    </tbody>
                  </table>
                </div>
                <div class="tab-pane fade" id="pills-profile" role="tabpanel" aria-labelledby="pills-profile-tab">

                  <table class="table custom-table">

                    <tbody>
                      <tr>
                        <td>HPIS CODE</td>
                        <td class="bg-light">999_200_40_0</td>
                      </tr>
                      <tr>
                        <td>HEALTHCARE PROVIDERS ONLY</td>
                        <td class="bg-light">No</td>
                      </tr>
                      <tr>
                        <td>LATEX FREE</td>
                        <td class="bg-light">Yes, No</td>
                      </tr>
                      <tr>
                        <td>MEDICATION ROUTE</td>
                        <td class="bg-light">Topical</td>
                      </tr>
                    </tbody>
                  </table>

                </div>

              </div>
            </div>


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

    <div class="site-section bg-secondary bg-image" style="background-image: url('images/bg_2.jpg');">
      <div class="container">
        <div class="row align-items-stretch">
          <div class="col-lg-6 mb-5 mb-lg-0">
            <a href="#" class="banner-1 h-100 d-flex" style="background-image: url('images/bg_1.jpg');">
              <div class="banner-1-inner align-self-center">
                <h2>Tafari Pharmacy Products</h2>
                <p>Lorem, ipsum dolor sit amet consectetur adipisicing elit. Molestiae ex ad minus rem odio voluptatem.
                </p>
              </div>
            </a>
          </div>
          <div class="col-lg-6 mb-5 mb-lg-0">
            <a href="#" class="banner-1 h-100 d-flex" style="background-image: url('images/bg_2.jpg');">
              <div class="banner-1-inner ml-auto  align-self-center">
                <h2>Rated by Experts</h2>
                <p>Lorem, ipsum dolor sit amet consectetur adipisicing elit. Molestiae ex ad minus rem odio voluptatem.
                </p>
              </div>
            </a>
          </div>
        </div>
      </div>
    </div>


    <footer class="site-footer">
      <div class="container">
        <div class="row">
          <div class="col-md-6 col-lg-3 mb-4 mb-lg-0">

            <div class="block-7">
              <h3 class="footer-heading mb-4">About Us</h3>
              <p>Lorem ipsum dolor sit amet, consectetur adipisicing elit. Eius quae reiciendis distinctio voluptates
                sed dolorum excepturi iure eaque, aut unde.</p>
            </div>

          </div>
          <div class="col-lg-3 mx-auto mb-5 mb-lg-0">
            <h3 class="footer-heading mb-4">Quick Links</h3>
            <ul class="list-unstyled">
              <li><a href="#">Supplements</a></li>
              <li><a href="#">Vitamins</a></li>
              <li><a href="#">Diet &amp; Nutrition</a></li>
              <li><a href="#">Tea &amp; Coffee</a></li>
            </ul>
          </div>

          <div class="col-md-6 col-lg-3">
            <div class="block-5 mb-5">
              <h3 class="footer-heading mb-4">Contact Info</h3>
              <ul class="list-unstyled">
                <li class="address">203 Fake St. Mountain View, San Francisco, California, USA</li>
                <li class="phone"><a href="tel://23923929210">+2 392 3929 210</a></li>
                <li class="email">emailaddress@domain.com</li>
              </ul>
            </div>


          </div>
        </div>
        <div class="row pt-5 mt-5 text-center">
          <div class="col-md-12">
            <p>
              <!-- Link back to Colorlib can't be removed. Template is licensed under CC BY 3.0. -->
              Copyright &copy;
              <script>document.write(new Date().getFullYear());</script> All rights reserved | This template is made
              with <i class="icon-heart" aria-hidden="true"></i> by <a href="https://colorlib.com" target="_blank"
                class="text-primary">Colorlib</a>
              <!-- Link back to Colorlib can't be removed. Template is licensed under CC BY 3.0. -->
            </p>
          </div>

        </div>
      </div>
    </footer>
  </div>

  <script src="{% static 'js/jquery-3.3.1.min.js' %}"></script>
  <script src="{% static 'js/jquery-ui.js' %}"></script>
  <script src="{% static 'js/popper.min.js' %}"></script>
  <script src="{% static 'js/bootstrap.min.js' %}"></script>
  <script src="{% static 'js/owl.carousel.min.js' %}"></script>
  <script src="{% static 'js/jquery.magnific-popup.min.js' %}"></script>
  <script src="{% static 'js/aos.js' %}"></script>

  <script src="{% static 'js/main.js' %}"></script>
  <script>
    document.addEventListener('DOMContentLoaded', function() {
        document.querySelector('.custom-field-select').onchange = function(event) {
            if (event.target.dataset.field) {
                document.querySelector('.snipcart-add-item')
                    .dataset['itemCustom' + event.target.dataset.field + 'Value'] = event.target.value;
            }
        };
    },false);
</script>
</body>

</html>
Enter fullscreen mode Exit fullscreen mode

The javascript included above is responsible for updating the Snipcart buy button when a custom field selection is made on the page. This code updates the button data attributes when the select value changes.

Live demo

home.png

Screenshot from 2022-02-06 21-04-50.png

Screenshot from 2022-02-06 21-05-29.png

See the live demo

Final thoughts about the project

Wagtail is simple, intuitive and minimal. I think wagtail is a great headless CMS, and I have enjoyed my little venture with Django, Wagtail CMS, and Bootstrap. LEt me know what you think about Wagtail in the comment section!

Latest comments (10)

Collapse
 
summertime23 profile image
Kemos

Do you have a repo for this?

Collapse
 
paulwababu profile image
paulsaul621

At the moment no sorry. Its not public as it runs my own site using wagtail.

Collapse
 
summertime23 profile image
Kemos

Thanks. I was just hoping to see it working since the demo link isn't working. I'll try building it from your posts when I get some time, cool idea.

Thread Thread
 
paulwababu profile image
paulsaul621

Let me know in case of anything!

Collapse
 
nigismo profile image
Nigismo

I tried to repeat after you, but it doesn't work.

Collapse
 
okechuqu profile image
Okechuqu Achiever

God bless you for this piece

Collapse
 
paulwababu profile image
paulsaul621

Thank you bro, Glad I could help you out, incase of anything, let me know!

Collapse
 
juliusalxstudent profile image
Julius Nana Acheampong Boakye

bro you didnt add the static files

Collapse
 
paulwababu profile image
paulsaul621

I used the cdn instead of static files bro

Collapse
 
kalobtaulien profile image
Kalob Taulien

This is fantastic, thank you for writing and sharing this!