5. Продукт с различными свойствами

2 октября 2016 г. 12:06

В предыдущих примерах мы поняли, что можем смоделировать наши продукты согласно их физическим свойствам, но что если мы хотим продать другой тип продукта с различными свойствами. Здесь полиморфизм выходит на сцену.

5.1. Запуск полиморфного демо (Polymorphic Demo)

Чтобы протестировать этот пример, нужно установить переменную окружения:

export DJANGO_SHOP_TUTORIAL=polymorphic

Затем пересоздать базу данных как объяснено в разделе Создание базы данных для демо-проекта и запустить демо-сервер:

./manage.py runserver

5.2. Полиморфная модель продукта

Если в дополнение к Картам памяти мы также хотим продавать смартфоны, мы должны объявить новую модель. Вместо дублирования всех общих полей, мы объединим их в общий базовый класс под названием Product. Затем этот базовый класс будет расширен для того, чтобы стать либо нашей известной моделью SmartCard или новой моделью SmartPhone.

Для работы с полиморфными моделями в djangoSHOP нам требуется приложение django-polymorphic. Здесь наши модели для Smart Cards или Smart Phones будут разделены в общую и специальную части. Общая часть уходит в новую модель продукта (Product model), в то время как специализированные части остаются в своих моделях.

Вам следует уже начать думать о макете предоставлении списка. Только атрибуты в модели Product будут доступны для списочных представлений, отображающих смартфоны бок о бок с картами памяти. Во-первых, мы должны создать специального менеджера (Model Manager), который объединит методы запроса для переводимых и полиморфных моделей:

myshop/models/i18n/polymorphic/product.py

from parler.models import TranslatableModelMixin, TranslatedFieldsModel
from parler.fields import TranslatedField
from parler.managers import TranslatableManager, TranslatableQuerySet
from polymorphic.query import PolymorphicQuerySet
from shop.models.product import BaseProductManager, BaseProduct, CMSPageReferenceMixin
from shop.models.defaults.mapping import ProductPage, ProductImage
from ..manufacturer import Manufacturer

class ProductQuerySet(TranslatableQuerySet, PolymorphicQuerySet):
    pass

class ProductManager(BaseProductManager, TranslatableManager):
    queryset_class = ProductQuerySet

    return qs.prefetch_related('translations')


@python_2_unicode_compatible
class Product(CMSPageReferenceMixin, TranslatableModelMixin, BaseProduct):
    # controlling the catalog

Следующий шаг - определить, какие атрибуты модели претендуют на чать нашей модели Product. К сожалению, нет серебряной пули (универсального и эффективного решения, прим. переводчика) для этой проблемы, и это одна из причин, почему djangoSHOP поставляется без подготовленной модели для нее. Если мы хотим продать как карты памяти и смартфоны, то эта модель продукта сможет сделать это дело:

# myshop/models/i18n/polymorphic/product.py

    """
    Базовый класс для описания полиморфного продукта. Здесь мы объявляем общие поля, которые доступны во всех
    наших различных типах продукта. Эти общие поля также используются для построения представления, отображающий
    список всех продуктов.
    """
    product_name = models.CharField(max_length=255, verbose_name=_("Product Name"))
    slug = models.SlugField(verbose_name=_("Slug"), unique=True)
    caption = TranslatedField()

5.2.1. Модель Smart Card

Модель используется для хранения переводимых полей и является такой же как в нашем последнем примере. Новая модель для Smart Cards сейчас наследуется от Product:

myshop/models/i18n/polymorphic/smartcard.py

from django.db import models
from django.utils.translation import ugettext_lazy as _
from djangocms_text_ckeditor.fields import HTMLField
from shop.money.fields import MoneyField
from parler.models import TranslatedFields


class SmartCard(Product):
    # common product fields
    unit_price = MoneyField(_("Unit price"), decimal_places=3,
        help_text=_("Net price for this product"))

    # product properties
    CARD_TYPE = (2 * ('{}{}'.format(s, t),)
                 for t in ('SD', 'SDXC', 'SDHC', 'SDHC II') for s in ('', 'micro '))
    card_type = models.CharField(_("Card Type"), choices=CARD_TYPE, max_length=15)
    SPEED = [(str(s), "{} MB/s".format(s)) for s in (4, 20, 30, 40, 48, 80, 95, 280)]
    speed = models.CharField(_("Transfer Speed"), choices=SPEED, max_length=8)
    product_code = models.CharField(_("Product code"), max_length=255, unique=True)
    storage = models.PositiveIntegerField(_("Storage Capacity"),

5.2.2. Модель для Smart Phone

Модель продукта для сматфонов намеренно немного сложнее. Мало того, что у него есть еще несколько атрибутов, смартфоны могут быть проданы с различными спецификациями внутренней памяти. Последнее влияет на цену и код продукта. Здесь отражается причина, почему мы не добавили поля unit_price и products_code в наш базовый класс продукта, хотя каждый продукт в нашем магазине требует эти поля.

При представлении смартфонов в наших представлениях списка, мы хотим сосредоточиться на разных моделях, но не на каждую конкретную, то есть его внутренней памяти. Поэтому клиенты должны будут различать конкретные варианты смартфона, всякий раз, когда они добавляют их в корзину, но не при просмотре их в списке каталога. Для клиента, это было бы очень скучно прокручивать списки со многими аналогичными продуктами, которые только дифференцируются на несколько вариаций.

Это означает что для некоторых моделей Smart Phone имеется более чем одна кнопка "Добавить в корзину".

При моделировании, нам, следовательно, требуется два разных класса: один для модели смартфона и один для каждого варианта смартфона.

myshop/models/polymorphic/smartphone.py

from shop.money import Money, MoneyMaker
from djangocms_text_ckeditor.fields import HTMLField
from shop.money.fields import MoneyField
from parler.models import TranslatedFields


class SmartPhoneModel(Product):
    """
    A generic smart phone model, which must be concretized by a model `SmartPhone` - see below.
    """
    BATTERY_TYPES = (
        (1, "Lithium Polymer (Li-Poly)"),
        (2, "Lithium Ion (Li-Ion)"),
    )
    WIFI_CONNECTIVITY = (
        (1, "802.11 b/g/n"),
    )
    BLUETOOTH_CONNECTIVITY = (
        (1, "Bluetooth 4.0"),
        (2, "Bluetooth 3.0"),
        (3, "Bluetooth 2.1"),
    )
    battery_type = models.PositiveSmallIntegerField(_("Battery type"),
        choices=BATTERY_TYPES)
    battery_capacity = models.PositiveIntegerField(_("Capacity"),
        help_text=_("Battery capacity in mAh"))
    ram_storage = models.PositiveIntegerField(_("RAM"),
        help_text=_("RAM storage in MB"))
    wifi_connectivity = models.PositiveIntegerField(_("WiFi"),

Здесь метод get_price() может только вернуть минимальную, среднюю и максимальную стоимость нашего продукта. В этой ситуации, большинство торговцев выражают цену, например, так: Цена от € 99.50

Конкретный смартфон моделируется так:

def get_product_variant(self, product_code):
        try:
            return self.smartphone_set.get(product_code=product_code)
        except SmartPhone.DoesNotExist as e:
            raise SmartPhoneModel.DoesNotExist(e)


class SmartPhone(models.Model):
    product = models.ForeignKey(SmartPhoneModel,
        verbose_name=_("Smart-Phone Model"))

Чтобы продолжить покупки, покупателю нужны страницы Корзина и Оформления заказа.

5.2.3. Модель для общего Товара (generic Commodity)

Для демонстрационных целей этот полиморфный пример добавляет другой тип модели Product - Общий Товар. Здесь вместо добавления каждого возможного атрибута нашего продукта в модель, мы пытаемся оставаться стандартизовано вместо того, чтобы использовать PlaceholderField, который предоставляется djangoCMS.

myshop/models/commodity.py

from cms.models.fields import PlaceholderField
from myshop.models.product import Product

class Commodity(Product):
    # other product fields
    placeholder = PlaceholderField("Commodity Details")

Это позволяет добавить любую произвольную информацию о нашем продукте к карточке товара. Единственное условие для работоспособности состоит в том, что в шаблон нужно добавить templatetag для рендера этого заполнителя.

Поскольку структура djangoSHOP смотрит в каталог папки для шаблона имени своего класса продукта, то нужно добавить этот фрагмент кода HTML:

myshop/catalog/commodity-detail.html

{% extends "myshop/pages/default.html" %} 
{% load cms_tags %} 
<div class="container"> 
    <div class="row"> 
        <div class="col-xs-12"> 
            <h1>{% render_model product "product_name" %}</h1> 
            {% render_placeholder product.placeholder %} 
        </div> 
    </div> 
</div>

Шаблон карточки товара расширяет шаблон по умолчанию нашего сайта. Помимо названия продукта (который добавляется в качестве удобства), эт представление остается пустым, когда просматриваешь в первый раз. В режиме редактирования, дважды щелкнув на заголовок, содержащий название продукта, можно открыть редактор редактирования нашего товара.

После переключения в режим "Структура" появляется заполнитель, который назван "Commodity Details". Здесь мы можем добавить любое количество Cascade плагинов, наполняя наш заполнитель строками, колонками, изображениями, текстовыми блоками и т. д. Это позволяет нам редактировать карточку товара в любой вид, который мы хотим. Недостаток использования этого подхода состоит в том, что это может привести к несоответствии с основным дизайном сайта, а также является более трудоемким, чем просто редактирование атрибутов продукта вместе с их соответствующими шаблонами.

5.2.3.1. Конфигурирование заполнителя

Так как мы используем этот заполнитель внутри жестко закодированой колонке Bootstrap, мы должны рассказать Cascade о ширине этого столбца. Это должно быть сделано в настройках проекта:

# myshop/settings.py

CMS_PLACEHOLDER_CONF = {
  ...
  'Commodity Details': {
    'plugins': ['BootstrapRowPlugin', 'TextPlugin', 'ImagePlugin', 'PicturePlugin'],
    'text_only_plugins': ['TextLinkPlugin'],
    'parent_classes': {'BootstrapRowPlugin': []},
    'require_parent': False,
    'glossary': {
      'breakpoints': ['xs', 'sm', 'md', 'lg'],
      'container_max_widths': {'xs': 750, 'sm': 750, 'md': 970, 'lg': 1170},
      'fluid': False,
      'media_queries': {
        'xs': ['(max-width: 768px)'],
        'sm': ['(min-width: 768px)', '(max-width: 992px)'],
        'md': ['(min-width: 992px)', '(max-width: 1200px)'],
        'lg': ['(min-width: 1200px)'],
      },
    }
  },
  ...
}

Эта конфигурация заполнителя эмулирует Bootstrap колонку как <divclass="col-xs-12">.

Оцените статью

0 из 5 (всего 0 оценок)

Поля, отмеченные звёздочкой ( * ) , являются обязательными.

Спасибо за ваш отзыв!

После нажатия кнопки "Отправить" ваше сообщение будет доставлено мне на почту.

Автор перевода

Права на использование материала, расположенного на этой странице http://vivazzi.pro/django-shop/polymorphic-product/:

Разрешается копировать материал с указанием её автора и ссылки на оригинал без использования параметра rel="nofollow" в теге <a>. Использование:

Автор перевода: Мальцев Артём
Ссылка на перевод статьи: <a href="http://vivazzi.pro/django-shop/polymorphic-product/">http://vivazzi.pro/django-shop/polymorphic-product/</a>

Подробнее: Правила использования сайта

Комментариев: 0

Вы можете оставить комментарий как незарегистрированный пользователь. Но, зарегистрировавшись, вы сможете получать оповещения об ответах, а также иметь доступ к своему личному аккаунту для просмотра своих комментариев.

Чтобы оставить комментарий от своего имени войдите или зарегистрируйтесь обычным способом или через социальные сети:

Отправить

На данный момент нет специального поиска, поэтому я предлагаю воспользоваться обычной поисковой системой, например, Google, добавив "vivazzi" после своего запроса.

Попробуйте