Vybudujte si web elektronického obchodu s Wagtail CMS, Bootstrap a Django Framework.

V tomto blogu budujeme plně funkční e-commerce web s Django a Wagtail CMS. Budu také krátce diskutovat:

  • Proč zvolit django framework k vytvoření vlastního webu elektronického obchodu
  • Který z nich je pro váš web lepší? Django CMS versus WordPress

Demo finálního produktu si můžete prohlédnout na následující URL

Proč zvolit Django pro e-commerce?

Za prvé, zde jsou některé funkce Django, které je třeba zvážit, pokud hledáte správný rámec pro stavbu obchodu.

→ Škálovatelnost

Django je ideální pro začínající e-commerce, protože se dobře hodí pro malé weby a dokáže se dokonale přizpůsobit růstu firmy. Na Django se můžete spolehnout, že zvládne stovky/tisíce návštěvníků najednou. Je sestaven z nezávislých komponent, které můžete odpojit nebo vyměnit v závislosti na vašich potřebách v kteroukoli konkrétní dobu.

→ Zabezpečení

S elektronickým obchodováním chcete zajistit, aby se obchodníci i klienti cítili bezpečně prostřednictvím vašeho nakupování. Django předchází spoustě běžných bezpečnostních chyb, které často oslabují tradiční PHP CMS. Například Django skryje zdrojový kód vašeho webu před přímým zobrazením na webu dynamickým generováním webových stránek.

→ Bohaté na funkce

Ve srovnání s většinou frameworků Django přichází s mnohem více funkcemi hned po vybalení. To vám umožní vytvořit aplikaci přímo z pálky. Perfektní pro podporu vašeho online obchodu s funkcemi, jako je autentizace uživatele, správa obsahu nebo RSS kanál. Pokud se zdá, že něco chybí, můžete se spolehnout na to, že Django komunita a ekosystém pluginů rozšíří vaši aplikaci!

→ SEO-friendly

SEO je prvořadé pro každé online podnikání. Django obhajuje osvědčené postupy pro SEO. Lidsky čitelné adresy URL a funkce mapy webu jistě potěší každý marketingový tým.

Jo, a také je to rychlé, což je vždy skvělé pro zákaznickou zkušenost i SEO.

→ Spolehlivý

Django usnadňuje vytváření vlastních webových stránek elektronického obchodu. Má moderní, snadno použitelný Python Web Framework, který podporuje rychlý vývoj a škálovatelnou architekturu. Jeho vysoká úroveň abstrakce usnadňuje vytváření, připojení, konfiguraci a údržbu aplikací. Kromě toho Django usnadňuje přidávání do mezipaměti, zabezpečené ověřování a zobrazení agnostických adres URL

Který z nich je lepší pro váš web elektronického obchodu? Django CMS versus WordPress

Databázové API Django usnadňuje práci s databázemi. Django má robustní a bezpečný autentizační systém. Django má vestavěný šablonovací engine, což znamená, že nemusíte používat samostatný jazyk šablon. Django je bezplatný webový rámec s otevřeným zdrojovým kódem! Django se také řídí principem DRY (Don't Repeat Yourself), DRY je princip snížení opakování v kódu, odkazující zpět na jediný zdroj – nebo „úryvek“ – opakovaně použitelného kódu, kdykoli jej potřebujete.
Django také odstraňuje spoustu režie. Vývojáři se nemusí starat o to, aby jejich programy byly kompatibilní s různými verzemi Pythonu, což znamená, že mohou s aplikací trávit více času.

Elektronický obchod Wagtail CMS + Snipcart

Wagtail je vývojářský systém pro správu obsahu Django. Bezplatný a open source, dobrosrdeční lidé z Torchbox jej vyvinuli. Je elegantní, flexibilní a IMHO nakopává prdel.

V následujícím tutoriálu Wagtail bude mít CMS na starosti vytváření a správu produktů, které si pak uživatelé budou moci zakoupit prostřednictvím nákupního košíku.

Na konci toho budete mít zprovozněný solidní e-commerce web založený na Django.

Níže je snímek obrazovky obchodu, který se chystáme postavit:

Můžeme začít!

Předpoklady

  • Účet Snipcart.

1. Vytvoření aplikace Wagtail

K instalaci Wagtail a jeho závislostí použijte pip, který je součástí Pythonu:

$ python3 -m  venv venv
$ source venv/bin/activate
$ pip install wagtail requests

Otevřete terminál a spusťte nový web Wagtail:

$ wagtail start snipcartwagtaildemo
$ cd snipcartwagtaildemo

K dokončení nastavení Wagtail máme další krok, a to instalaci pluginu wagtail.contrib.settings, který budeme později vyžadovat.

Ve svém novém projektu Wagtail otevřete soubor base.py umístěný ve složce snipcartwaigtaildemo/settings. Poté přidejte wagtail.contrib.settings do pole INSTALLED_APPS.

# ./setting/base.py
INSTALLED_APPS = [
    ...,
    'wagtail.contrib.settings'
]

1.1 Tvorba modelů.

První věc, kterou musíte udělat, je vytvořit modely stránky. Wagtail používá tyto modely Django ke generování typu stránky.

Otevřete soubor models.py umístěný v domovské složce vašeho produktu. Zde definujete všechny své vlastní modely.

Vytvořte dva různé modely:

  • Produkt:definuje produkt, který prodáváte.

    • ProductCustomField:definuje jedno vlastní pole produktu.

Začněme importem požadovaných modulů:

# ./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

Nyní přidejte model produktu:

# ./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'),
    ]

Aktualizujte třídu domovské stránky, která je zodpovědná za uvádění produktů pouze na domovskou stránku. Na domovské stránce uvedu pouze 6 produktů. Na libovolné stránce Wagtail můžete přepsat název metody get_context. Data, která pohled obdrží, můžete přidat v parametrech

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

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

        return context

A 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')
    ]

Potřebujeme přidat ještě jeden pohled, který bude zodpovědný za zobrazování všech produktů v našem online obchodě a za stránkování stránky. Budu stránkovat na straně backendu, protože načítání všech objektů na stránce není příliš efektivní. Můžeme však naše data ukládat do mezipaměti a načíst je všechna na frontend. Díky tomu bude stránkování bezproblémové, vše závisí na velikosti vaší databáze.

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
    })

Nakonec přidejte zobrazení sekce o nás a kontaktujte nás, například:

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

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

2. Přidání nastavení konfigurace Snipcart

Ujistěte se, že můžete aktualizovat klíč API Snipcart přímo z ovládacího panelu Wagtail.

K tomu budete muset přidat nastavení webu.

Nastavení webu jsou speciální pole, která můžete přidat do souboru models.py. Zobrazí se na hlavním panelu v sekci Wagtail Settings.

Importujte tento modul:

# ./home/models.py

from wagtail.contrib.settings.models import BaseSetting, register_setting

Pak přidejte tyto:

# ./home/models.py

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

3. Migrace databáze

Nyní, když jsou vaše modely vytvořeny, budete muset vygenerovat migrace databáze a spustit je.

Ve svém terminálu použijte příkaz makemigrations:

$ python3 manage.py makemigrations
$ python3 manage.py migrate

Nakonec vytvořte svého prvního uživatele CMS pomocí příkazu createuperuser:

$ python3 manage.py createsuperuser

Vyžádá si e-mailovou adresu, uživatelské jméno a vytvoření hesla. Nezapomeňte uživatelské jméno a heslo, které jste si vybrali; budete je potřebovat, abyste se přihlásili do panelu Wagtail's.

4. Vytváření produktů

Spusťte svůj server takto:

$ python3 manage.py runserver

Nyní otevřete prohlížeč a přejděte na http://127.0.0.1:8000/admin. K přihlášení použijte přihlašovací údaje, které jste nastavili dříve.

V nabídce Wagtail vyberte domovskou stránku. Poté klikněte na tlačítko Přidat podřízenou stránku.

Budete požádáni, abyste vybrali typ stránky, vybrali Domovskou stránku a pojmenovali ji.

Vraťte se do nabídky Domovská stránka a klikněte na tlačítko Přidat podřízenou stránku pod nově vytvořenou stránkou.

Znovu budete požádáni, abyste vybrali typ stránky, tentokrát vyberte Produkt.

Můžete vytvořit tolik produktů, kolik chcete.

4.1 Přidání klíče API Snipcart

Pamatujete si třídu SnipcartSettings, kterou jste vytvořili? Klíč API budete moci nakonfigurovat rozbalením nabídky Nastavení a přechodem do nastavení Snipcart.

Otevřete řídicí panel Snipcartu a získejte svůj veřejný klíč API (Test nebo Live), vraťte se do Wagtail a vložte jej do pole klíče API.

Uložte nastavení.

Přidání nových tras URL

Sotva jsem použil konipas a nejsem příliš obeznámen s tím, jak se zachází s URL, ale vytvořil jsem si vlastní URL pro zobrazení obchodu, pro seznam všech produktů:

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)),
]

5. Šablona

Nyní pokračujte ve vytváření našich HTML souborů a úpravě uživatelského rozhraní naší aplikace. Šablona pochází z této adresy URL Colorlib.

Domovská stránka

Na naší domovské stránce potřebujeme zobrazit několik položek z naší databáze. Na zobrazení domovské stránky jsme již specifikovali 6 položek. Vytvořte 3 soubory ve složce home/templates/home:

  • domovska_stranka.html
  • product.html
  • obchod.html

Než zobrazím celý kód, dovolte mi vysvětlit několik důležitých bitů, počínaje hlavičkou souborů HTML

{% 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>

Máme odkaz na značky snipcart, kde detekuje klíč API, který jsme přidali do souboru nastavení, a máme odkaz na soubory snipcart jako soubory cdn.

<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>

Máme také část produktu, která prochází produkty a zobrazuje je všechny. Další podrobnosti najdete v dokumentech s útržkovým košíkem.
Celý kód pro domovskou stránku je:

{% 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">[email protected]</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>

Stránka všech produktů

Na product.html přidejte řádky kódu níže. Nepoužil jsem funkci vestavěných šablon django pro práci v komponentách, takže musíme opakovat značky, které jsme přidali do souboru domovské stránky, pro každou stránku, kterou vytvoříme. Zkopírujte a vložte kód níže na 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">[email protected]</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>

Kód je stejný jako kód domovské stránky, jediný rozdíl je v souboru shop.html, stránkujeme naši webovou stránku a zobrazujeme všechny produkty namísto pouhých 6 jako na domovské stránce.

Podrobnosti o produktu a stránka zobrazení košíku

Na stránce produktů potřebujeme zobrazit produkt, když uživatel klikne na položku, například takto:

Abychom to mohli udělat, musíme vytvořit šablonu, která bude přidružena k modelu stránky produktu. Na product.html ve složce home/templates/home přidejte:

{% 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">[email protected]</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>

Výše uvedený javascript je zodpovědný za aktualizaci tlačítka koupit Snipcart, když je na stránce proveden výběr vlastního pole. Tento kód aktualizuje atributy dat tlačítka, když se změní hodnota select.

Živá ukázka

Podívejte se na živé demo

Závěrečné úvahy o projektu

Konipas je jednoduchý, intuitivní a minimální. Myslím, že konipas je skvělý bezhlavý CMS a užil jsem si svůj malý podnik s Django, Wagtail CMS a Bootstrap. Dejte mi vědět, co si myslíte o Wagtail v sekci komentářů!