چگونه با استفاده از Python، Django و Wagtail یک فروشگاه اینترنتی بسازیم؟ - بخش دوم

گردآوری و تالیف : عرفان کاکایی
تاریخ انتشار : 23 خرداد 1398
دسته بندی ها : پایتون

امروز، می‌خواهیم دنیای فریم‌وورک‌های JavaScript را رها کنیم و به تجارت الکترونیک Django برویم.

در این پست، من به برخی سوال‌هایی که ممکن است در هنگام شروع یک پروژه تجارت الکترونیک داشته باشید، پاسخ خواهم داد. مانند:

آیا پایتون زبان مناسب، و Django فریم‌وورک مناسب برای پروژه من است؟ از چه ابزار یا پلاگین‌هایی باید استفاده کنم؟

در بخش دوم این مقاله با ما همراه باشید...

۱ - ساخت یک وبسایت فروشگاه Wagtail جدید

مطمئن شوید که Wagtail را به صورت نصب شد دارید.

یک ترمینال را باز کنید و یک وبسایت Wagtail جدید را راه‌اندازی کنید:

wagtail start snipcartwagtaildemo
cd snipcartwagtaildemo

ما یک قدم دیگر برای تکمیل راه‌اندازی Wagtail داریم، و این قدم نصب پلاگین wagtail.contrib.settings است که بعدا به آن نیاز خواهیم داشت.

در پروژه Wagtail جدید خود، فایل base.py‌ که در پوشه settings قرار دارد را باز کنید. سپس wagtail.contrib.settings را به آرایه INSTALLED_APPS اضافه کنید.

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

۱.۱ - تعریف مدل‌ها

اولین کاری که باید انجام دهید، ساخت مدل‌های صفحه خود است. Wagtail از Django برای تولید یک صفحه استفاده می‌کند.

فایل models.py که در پوشه home پروژه شما قرار دارد را باز کنید. در اینجا تمام مدل‌های سفارشی خود را تعریف خواهید کرد.

دو مدل متفاوت بسازید:

  • Product - محصولاتی که می‌فروشید را تعریف می‌کند.
  • ProductCustomField - یک فیلد سفارشی محصول تنها را تعریف می‌کند.

بیایید با وارد کردن ماژول‌های مورد نیاز شروع کنیم:

# ./home/models.py
from django.db import models

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

حال مدل Product را وارد کنید:

# ./home/models.py

class Product(Page):
    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'),
    ]

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

۲ - اضافه کردن تنظیمات پیکربندی Snipcart

بیایید مطمئن شویم که می‌توانید کلید Snipcart را مستقیما از داشبورد Wagtail بروزرسانی کنید. برای انجام این کار، باید تنظیمات وبسایت را اضافه کنید.

تنظیمات وبسایت، فیلدهای خاصی هستند که می‌توانید به فایل‌های مدل خود اضافه کنید. این فیلدها در بخش Wagtail Settings داشبورد ظاهر خواهند شد.

این ماژول را وارد کنید:

# ./home/models.py

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

سپس این خطوط:

# ./home/models.py

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

۳ - مهاجرت دیتابیس

حال که مدل‌های شما ساخته شده‌اند، باید مهاجرت‌های دیتابیس را تولید کرده، اجرا کنید.

در ترمینال خود، از دستور makemigrations استفاده کنید:

manage.py makemigrations

شما باید این خروجی را ببینید:

Migrations for 'home':

  home\migrations\0003_product_productcustomfield_snipcartsettings.py
    - Create model Product
    - Create model ProductCustomField
    - Create model SnipcartSettings

پس از این که مهاجرت‌ها تولید شدند، آن‌ها را با استفاده از دستور migrate به دیتابیس خود اعمال کنید:

manage.py migrate

این روند یکی دو ثانیه وقت خواهد برد. Wagtail طرح دیتابیس شما را به همراه مدل‌هایی که تعریف کردید، راه‌اندازی خواهد کرد.

در آخر، کاربر CMS خود را با استفاده از دستور createsuperuser بسازید:

manage.py createsuperuser

نام کاربری و رمز عبوری که انتخاب کردید را فراموش نکنید. شما برای وارد شدن به داشبورد Wagtail به آن نیاز خواهید داشت.

۴ - ساخت محصولات

با راه‌اندازی سرور توسعه‌دهی خود، با استفاده از دستور Django شروع کنید:

manage.py runserver

حال مرورگر خود را باز کنید و به این آدرس بروید: http://localhost:8000/admin. از مدارکی که پیش‌تر برای ورود تعیین کردید استفاده کنید تا وارد شوید.

صفحه Home را در منوی Wagtail انتخاب کنید. سپس بر روی Add child page button کلیک کنید.

از شما درخواست خواهد شد که یک نوع صفحه را انتخاب کنید؛ گزینه Product را انتخاب کنید.

جزئیات محصول را وارد کنید، و سپس محصول جدید خود را منتشر کنید:

شما می‌توانید هر تعداد محصولی که می‌خواهید را بسازید.

۴.۱ - اضافه کردن کلید Snipcart API

کلاس SnipcartSettings که ساختید را به یاد دارید؟ شما خواهید توانست که کلید API خود را با گسترش منوی Settings و رفتن به Snipcart Settings پیکربندی کنید.

داشبورد Snipcart را باز کنید و سپس کلید API عمومی خود را به دست خواهید آورد. به Wagtail برگردید و آن را در فیلد API Key قرار دهید.

تنظیمات خود را ذخیره کنید.

۵ - قالب‌بندی

حال backend شما حاضر است، کلید API‌ شما پیکربندی شده است، و اولین محصولات شما ساخته شده‌اند. وقت آن رسیده است که شروع به ساخت وبسایت کنید.

برای این دمو، من تصمیم گرفتم که از فریم‌وورک Spectre.css استفاده کنم. این فریم‌وورک ساده و سبک می‌باشد.

فیل base.html که در آدرس snipcartwagtaildemo/templates قرار دارد را باز کنید.

شما باید ارجاعاتی را برای Spectre.css و Snipcart اضافه کنید. این خطوط را به head سند خود اضافه کنید:

<!-- ./snipcartwagtaildemo/templates/base.html -->
{% load  static  wagtailsettings_tags %}
{% get_settings %}

{# Global stylesheets #}

<link rel="stylesheet" href="https://unpkg.com/spectre.css/dist/spectre.min.css">
<link rel="stylesheet" href="https://unpkg.com/spectre.css/dist/spectre-exp.min.css">
<link rel="stylesheet" href="https://unpkg.com/spectre.css/dist/spectre-icons.min.css">

{# Snipcart #}

{% if settings.home.SnipcartSettings.api_key %}
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/2.2.2/jquery.min.js"></script>
    <script src="https://cdn.snipcart.com/scripts/2.0/snipcart.js" id="snipcart" data-api-key="{{ settings.home.SnipcartSettings.api_key }}"></script>
    <link href="https://cdn.snipcart.com/themes/2.0/base/snipcart.min.css" type="text/css" rel="stylesheet" />
{% endif %}

کلید اِی‌پی‌آی Snipcart که شما پیش‌تر پیکربندی کردید، از این طریق قابل دسترسی است:

settings.home.SnipcartSettings.api_key

سپس، نوار راهنما و برخی عناصر طرح Spectre.css را اضافه کنید.

محتویات کد body را با این خطوط جایگزین کنید:

<!-- ./snipcartwagtaildemo/templates/base.html -->
{% wagtailuserbar %}

<div class="container grid-lg">
    <header class="navbar">

        <section class="navbar-section">
            <a href="/" class="navbar-brand mr-2">
                Products
            </a>
        </section>

        <!-- Snipcart summary and View cart button -->
        <section class="navbar-section snipcart-summary">
            <div class="input-group input-line">
                <a href="" class="btn btn-primary snipcart-checkout">
                    <i class="icon icon-apps"></i>
                    View cart (<span class="snipcart-total-items">0</span>)
                </a>
            </div>
        </section>

    </header>
</div>

<div class="container grid-lg">
    {% block content %}{% endblock %}
</div>

{# Global javascript #}
<script type="text/javascript" src="{% static 'js/snipcartwagtaildemo.js' %}"></script>

{% block extra_js %}
    {# Override this in templates to add extra javascript #}
{% endblock %}

۵.۱ - لیست کردن محصولات

اولین قالبی که شما نیاز دارید، فهرست شماست که محصولات در آن لیست خواهند شد.

شما باید محصولات خود را در زمینه صفحه خانه خود در دسترس قرار دهید. در هر صفحه Wagtail، شما می‌توانید متد get_context را بازنویسی کنید.

شما می‌توانید داده‌هایی که view شما در قالب پارامترها دریافت خواهد کرد را اضافه کنید. در این مورد، من می‌خواهم متغیر زمینه products را تنظیم کنم.

فایل models.py را در پوشه home باز کنید و کلاس HomePage را بروزرسانی کنید:

# ./home/models.py

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

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

        return context

سپس فایل home_page.html که در پوشه home/templates/home قرار دارد را باز کنید.

بیایید یک صفحه ساده بسازیم که تصویر محصول را با یک لینک به جزئیات آن محصول نمایش می‌دهد.

<!-- /.home/templates/home/home_template.html -->
{% extends "base.html" %}
{% load wagtailimages_tags %}

{% block content %}
    <h1>
        Welcome to our store
    </h1>

    <div class="columns">
        {% for product in products %}
            <div class="column col-6">
                <div class="card">
                    <div class="card-image">
                        {% image product.image fill-1000x200 as tmp_image %}
                        <img src="{{ tmp_image.url }}" alt="" class="img-responsive">
                    </div>
                    <div class="card-header">
                        <a href="{{ product.get_url }}" class="btn btn-primary float-right">
                            <i class="icon icon-plus"></i>
                        </a>
                        <div class="card-title h5">
                            {{ product.title }}
                        </div>
                    </div>
                    <div class="card-body">
                        {{ product.description }}
                    </div>
                </div>
            </div>
        {% endfor %}
    </div>
{% endblock %}

۵.۲ - جزئیات محصول

آخرین قالب، قالبی است که جزئیات یک محصول تنها را به همراه دکمه خرید Snipcart آن نمایش می‌دهد.

همچنین، خوب می‌شود اگر بتوانیم گزینه‌های محصول را قبل از اضافه کردن آن به سبد خرید، در همین صفحه به طور مستقیم انتخاب کنیم. پس من یک راه برای انتخاب تمام فیلدهای سفارشی با گزینه‌هایی به طور مستقیم در این قالب اضافه خواهم کرد.

قبل از نوشتن HTML، شما باید زمینه view را بروزرسانی کنید. قالب‌های Django به ما دسترسی ۱۰۰ درصد به متدها و آبجکت‌های Python را نمی‌دهد؛ پس کارهایی مانند تقسیم‌بندی یک رشته، خیلی خوب پاسخ نمی‌دهند.

من تصمیم گرفتم که باز هم متد get_context را بازنویسی کنم.

فایل models.py را از پوشه home باز کنید و این متد را به کلاس Product اضافه کنید:

<!-- /.home/templates/home/home_template.html -->
{% extends "base.html" %}
{% load wagtailimages_tags %}

{% block content %}
    <h1>
        Welcome to our store
    </h1>

    <div class="columns">
        {% for product in products %}
            <div class="column col-6">
                <div class="card">
                    <div class="card-image">
                        {% image product.image fill-1000x200 as tmp_image %}
                        <img src="{{ tmp_image.url }}" alt="" class="img-responsive">
                    </div>
                    <div class="card-header">
                        <a href="{{ product.get_url }}" class="btn btn-primary float-right">
                            <i class="icon icon-plus"></i>
                        </a>
                        <div class="card-title h5">
                            {{ product.title }}
                        </div>
                    </div>
                    <div class="card-body">
                        {{ product.description }}
                    </div>
                </div>
            </div>
        {% endfor %}
    </div>
{% endblock %}

یک آرایه به نام custom_fields برای قالب product.html در دسترس خواهد بود.

یک فایل به نام product.html در پوشه home/templates/home بسازید. این قالبی است که به همراه مدل صفحه Product حضور خواهد داشت.

<!-- ./home/templates/home/product.html -->
{% extends "base.html" %}
{% load wagtailimages_tags %}

{% block content %}
    <div class="container grid-lg">
        <div class="columns">
            <div class="column col-4">
                {% image page.image max-300x300 as temp_image %}
                <img src="{{ temp_image.url }}" alt="" />
            </div>
            <div class="column col-8">
                <h1>
                    {{ page.title }}
                </h1>

                <p>
                    {{ page.short_description }}
                </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"
                    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 %}>
                    <i class="icon icon-plus"></i>
                    Add to cart
                </button>
            </div>
        </div>
    </div>
{% endblock %}

سپس، مقداری JavaScript برای بروزرسانی دکمه خرید Snipcart، وقتی که انتخاب فیلدهای سفارشی بر روی صفحه انجام شده است، اضافه کنید.

این قطعه اسکریپت را به قبل از بیانیه endblock اضافه کنید:

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

این صفات کد دکمه داده را وقتی که مقدار select تغییر می‌کند، بروزرسانی می‌نمایند.

اگر بر روی دکمه + داخل هر محصولی کلیک کنید، باید جزئیات آن را ببینید:

حال شما اساس قوی‌ای برای شروع پروژه تجارت الکترونیک خود با استفاده از Django و Wagtail دارید.

این فریم‌وورک‌ها بسیار قدرتمند هستند. شما می‌توانید به سرعت برخی عملکردهای جستجو، پیشنهادات محصول، بازبینی‌ها و.. را اضافه کنید.

دمو زنده و مخزن گیت‌هاب

شما می‌توانید دمو زنده را در اینجا ببینید.

شما می‌توانید مخزن گیت‌هاب را در اینجا ببینید.

کلام آخر

من واقعا از کار با Wagtail لذت می‌برم؛ زیرا این ابزار ساده و خلاقانه است. گرچه باید اعتراف کنم که گاهی اوقات سندنگاری‌های آن‌ها ناقص به نظر می‌رسد. در ابتدا من می‌خواستم برخی تغییرات را به نحوه کار مسیریابی اضافه کنم و چیزی در این مورد در اسناد آن‌ها نیافتم.

من در هنگام شروع، Python را بر روی لپتاپ خود نداشتم؛ پس راه‌اندازی همه چیز و داشتن این دمو به صورت آماده به کار، شامل میزبانی این دمو حدود یک روز از وقت مرا برد. با خود گفتم شاید حال این روند برای توسعه دهندگان مشتاق Python سریع‌تر باشد.

منبع

مقالات پیشنهادی

چگونه با استفاده از Python، Django و Wagtail یک فروشگاه اینترنتی بسازیم؟ - بخش اول

Django یک فریم‌وورک وب متن باز و سطح بالای Python است. تاکید آن بر روی کامپوننت‌های دارای قابلیت استفاده مجدد، ساخت وب‌اپلیکیشن‌ها بر پایه Python را ب...

ایجاد اپلیکیشنی مدرن با استفاده از Django و Vue.js - بخش دوم

JWT یک URL-safe کوچک است که برای نمایش درستی یک انتقال بین دو موجودیت استفاده می‌شود. درستی در JWT در یک شئ اینکود شده داخل JSON قرار دارد و به عنوان...

آیکون های فروشگاهی و بازاریابی

در این پست لذت بخش من میخوام به شما یک مجموعه از آیکون های زیبا و ضررویه بازاریابی و فروشگاهی رو معرفی کنم که شامل +100 آیکون Swificons با 3 نوع مختلف...

ایجاد اپلیکیشنی مدرن با استفاده از Django و Vue.js - بخش اول

در این مقاله ما قصد داریم از چهارچوب Django و Django REST برای بک‌اند و Vue.js برای فرانت‌اند است. APIها با کمک Axois (یک کتابخانه HTTP Client) از طری...