19. Configuration and Settings

The django-SHOP framework itself, requires only a few configuration directives. However, since each e-commerce site built around django-SHOP consists of the merchant’s own project, plus a collection of third party Django apps, here is a summary of mandatory and some optional configuration settings:

19.1. Django-SHOP specific settings

19.1.1. App Label

This label is required internally to configure the name of the database tables used in the merchant’s implementation.

SHOP_APP_LABEL = 'myshop'

There is no default setting.

19.1.2. Site Framework

You should always activate Django’s site framework and set a default for

SITE_ID = 1

19.1.3. Alternative User Model

Django’s built-in User model lacks a few features required by django-SHOP, mainly the possibility to use the email address as the login credential. This overridden model is 100% field compatible to Django’s internal model and even reuses its own database table, namely auth_user.

AUTH_USER_MODEL = 'email_auth.User'

Since this user model intentionally does not enforce uniqueness on the email address, Django would complain if we do not silence this system check:

SILENCED_SYSTEM_CHECKS = ['auth.W004']

For further information, please refer to the Customer Model documentation.

19.1.4. Authentication Backends

AUTHENTICATION_BACKENDS = [
    'django.contrib.auth.backends.ModelBackend',
    'allauth.account.auth_backends.AuthenticationBackend',
]

19.1.5. Currency

Unless Money types are specified explicitly, each project requires a default currency:

SHOP_DEFAULT_CURRENCY = 'EUR'

The typical format to render an amount is $ 1.23, but some merchant may prefer 1.23 USD. By using the configuration setting:

SHOP_MONEY_FORMAT = '{symbol} {amount}'

we my specify our own money rendering format, where {symbol} is €, $, £, etc. and {currency} is EUR, USD, GBP, etc.

Unless amounts never reach a thousand, it is advised to activate a separator for better readability.

USE_THOUSAND_SEPARATOR = True

Outside of the US, it generally is a good idea to activate localization for numeric types.

USE_L10N = True

19.1.6. Cart Modifiers

Each project requires at least one cart modifier in order to initialize the cart. In most implementations shop.modifiers.defaults.DefaultCartModifier is enough, but depending on the product models, the merchant’s may implement an alternative.

To identify the taxes in the cart, use one of the provided tax modifiers or implement a customized one.

Other modifiers may add extra payment and shipping costs, or rebate the total amount depending on whatever appropriate.

SHOP_CART_MODIFIERS = [
    'shop.modifiers.defaults.DefaultCartModifier',
    'shop.modifiers.taxes.CartExcludedTaxModifier',
    # other modifiers
]

For further information, please refer to the Cart Modifiers documentation.

19.1.7. Installed Django Applications

This is a configuration known to work. Special and optional apps are discussed below.

INSTALLED_APPS = [
    'django.contrib.auth',
    'email_auth',
    'polymorphic',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.sites',
    'djangocms_admin_style',
    'django.contrib.admin',
    'django.contrib.staticfiles',
    'django.contrib.sitemaps',
    'djangocms_text_ckeditor',
    'django_select2',
    'cmsplugin_cascade',
    'cmsplugin_cascade.clipboard',
    'cmsplugin_cascade.sharable',
    'cmsplugin_cascade.extra_fields',
    'cmsplugin_cascade.segmentation',
    'cms_bootstrap3',
    'adminsortable2',
    'rest_framework',
    'rest_framework.authtoken',
    'rest_auth',
    'django_fsm',
    'fsm_admin',
    'djng',
    'cms',
    'menus',
    'treebeard',
    'compressor',
    'sekizai',
    'sass_processor',
    'django_filters',
    'filer',
    'easy_thumbnails',
    'easy_thumbnails.optimize',
    'parler',
    'post_office',
    'haystack',
    'shop',
    'my_shop_implementation',
]
  • email_auth optional but recommended, overrides the built-in authentification. It must be located after django.contrib.auth.
  • polymorphic only required, if the site requires more than one type of product model. It presumes that django-polymorphic is installed.
  • djangocms_text_ckeditor optionally adds a WYSIWYG HTML editor which integrates well with django-CMS.
  • django_select2 optionally adds a select field to Django’s admin, with integrated autocompletion. Very useful for addings links to products manually. It presumes that django-select2 is installed.
  • cmsplugin_cascade adds the functionality to add CMS plugins, as provided by django-SHOP, to arbitrary CMS placeholders. This setting including submodules can be removed, if all templates are created manually.
  • cmsplugin_cascade.clipboard allows the site administrator to copy a set of plugins in one installation and paste it into the placeholder of another one.
  • cmsplugin_cascade.sharable allows the site administrator to share a preconfigurable set of plugin attributes into an alias, to be reused by many plugins of the same type.
  • cmsplugin_cascade.extra_fields allows the site administrator to add arbitrary CSS classes, styles and ID-fields to entitled plugins.
  • cmsplugin_cascade.segmentation allows to segment a set of plugins into logical units.
  • cms_bootstrap3 adds some templates and templatetags to render Bootstrap 3 styled menus and navigation bars.
  • adminsortable2 allows the site administrator to sort various items in Django’s administration backend.
  • rest_framework, rest_framework.authtoken and rest_auth, required, add the REST functionality to the django-SHOP framework.
  • django_fsm and fsm_admin, required, add the Finite State Machine to the django-SHOP framework.
  • djng only required for installations using AngularJS, which is the recommended JavaScript framework. It adds the interface layer between Django and AngularJS and presumes that django-angular is installed.
  • cms, menus and treebeard are required if django-SHOP is used in combination with djangoCMS.
  • compressor, highly recommended. Concatenates and minifies CSS and JavaScript files on production systems. It presumes that django-compressor is installed.
  • sekizai, highly recommended, allows the template designer to group CSS and JavaScript file includes. It presumes that django-sekizai is installed.
  • sass_processor, optional but recommended, used to convert SASS into pure CSS together with debugging information. It presumes that django-sass-processor is installed.
  • django_filters, optionally used to filter products by their attributes using request parameters.
  • filer, highly recommended, manage your media files in Django. It presumes that django-filer is installed.
  • easy_thumbnails and easy_thumbnails.optimize, highly recommended, handle thumbnail generation and optimization. It presumes that easy-thumbnails is installed.
  • parler is an optional framework which handles the translation of models fields into other natural languages.
  • post_office highly recommended. An asynchronous mail delivery application which does not interrupt the request-response cycle when sending mail.
  • haystack optional, handles the interface between Django and Elasticsearch – a full-text search engine. It presumes a running and available instance of ElasticSearch and that django-haystack and drf-haystack is installed.
  • shop the django-SHOP framework.
  • my_shop_implementation replace this by the merchant’s implementation of his shop.

19.1.8. Middleware Classes

This is a configuration known to work. Special middleware classes are discussed below.

MIDDLEWARE_CLASSES = (
    'djng.middleware.AngularUrlMiddleware',
    'django.contrib.sessions.middleware.SessionMiddleware',
    'django.middleware.csrf.CsrfViewMiddleware',
    'django.contrib.auth.middleware.AuthenticationMiddleware',
    'shop.middleware.CustomerMiddleware',
    'django.contrib.messages.middleware.MessageMiddleware',
    'django.middleware.locale.LocaleMiddleware',
    'django.middleware.common.CommonMiddleware',
    'django.middleware.gzip.GZipMiddleware',
    'shop.middleware.MethodOverrideMiddleware',
    'cms.middleware.language.LanguageCookieMiddleware',
    'cms.middleware.user.CurrentUserMiddleware',
    'cms.middleware.page.CurrentPageMiddleware',
    'cms.middleware.toolbar.ToolbarMiddleware',
)
  • djng.middleware.AngularUrlMiddleware adds a special router, so that we can use Django’s reverse function from inside JavaScript. Only required in conjunction with django-angular.
  • shop.middleware.CustomerMiddleware add the Customer object to each request.
  • shop.middleware.MethodOverrideMiddleware transforms PUT requests wrapped as POST requests back into the PUT method. This is required for compatibility with some JS frameworks and proxies.

19.1.9. Static Files

If compressor and/or sass_processor are part of INSTALLED_APPS, add their finders to the list of the default STATICFILES_FINDERS:

STATICFILES_FINDERS = [
    'django.contrib.staticfiles.finders.FileSystemFinder',
    'django.contrib.staticfiles.finders.AppDirectoriesFinder',
    'sass_processor.finders.CssFinder',
    'compressor.finders.CompressorFinder',
]

Django-SHOP requires a few third party packages, which are not available from PyPI, they instead must be installed via npm install. In order to make these files available to our Django application, we use the configuration setting:

STATICFILES_DIRS = [
    ('node_modules', '/path/to/project/node_modules'),
]

Some files installed by npm are processed by django-sass-processor and hence their path must be made available:

NODE_MODULES_URL = STATIC_URL + 'node_modules/'

SASS_PROCESSOR_INCLUDE_DIRS = (
    os.path.join(PROJECT_ROOT, 'node_modules'),
)
  • The string provided by NODE_MODULES_URL is used by the special function get-setting() in the provided SASS files.
  • SASS_PROCESSOR_INCLUDE_DIRS extends the list of folders to look for @import ... statements in the provided SASS files.

19.1.10. Template Context Processors

Templates rendered by the django-SHOP framework require some additional objects or configuration settings. Add them to each template using these context processors:

TEMPLATES = [{
    ...
    'OPTIONS': {
        'context_processors': (
            ...
            'shop.context_processors.customer',
            'shop.context_processors.ng_model_options',
        ),
    },
}]

shop.context_processors.customer adds the Customer object to the rendering context.

shop.context_processors.ng_model_options adds the AngularJS specific settings to the rendering context.

19.1.11. Configure the Order Workflow

The ordering workflow can be configured using a list or tuple of mixin classes.

SHOP_ORDER_WORKFLOWS = (
    'shop.payment.defaults.PayInAdvanceWorkflowMixin',
    'shop.shipping.defaults.CommissionGoodsWorkflowMixin',
    # other workflow mixins
)

This prevents to display all transitions configured by the workflow mixins inside the administration backend:

FSM_ADMIN_FORCE_PERMIT = True

19.1.12. Email settings

Since django-SHOP communicates with its customers via email, having a working outgoing e-mail service is a fundamental requirement for django-SHOP. Adopt these settings to your configuration. Please remember that e-mail is sent asynchronously via django-post_office.

EMAIL_HOST = 'smtp.example.com'
EMAIL_PORT = 587
EMAIL_HOST_USER = 'no-reply@example.com'
EMAIL_HOST_PASSWORD = 'smtp-secret-password'
EMAIL_USE_TLS = True
DEFAULT_FROM_EMAIL = 'My Shop <no-reply@example.com>'
EMAIL_REPLY_TO = 'info@example.com'
EMAIL_BACKEND = 'post_office.EmailBackend'

19.1.13. Session Handling

For performance reasons it is recommended to use a memory based session store such as Redis, rather than a database or disk based store.

SESSION_ENGINE = 'redis_sessions.session'
SESSION_SAVE_EVERY_REQUEST = True
SESSION_REDIS_PREFIX = 'myshop-session'
SESSION_REDIS_DB = 0

19.1.14. Caching Backend

For performance reasons it is recommended to use a memory based cache such as Redis, rather than a disk based store. In comparison to memcached, Redis can invalidate cache entries using keys with wildcards, which is a big advantage in django-SHOP.

CACHES = {
    'default': {
        'BACKEND': 'redis_cache.RedisCache',
        'LOCATION': os.environ.get('REDIS_LOCATION', 'redis://localhost:6379/0'),
        'KEY_PREFIX': 'myshop-cache',
    },
}

CACHE_MIDDLEWARE_ALIAS = 'default'
CACHE_MIDDLEWARE_SECONDS = 3600
CACHE_MIDDLEWARE_KEY_PREFIX = 'myshop-cache'

19.2. Internationalisation Support

Always localize decimal numbers unless you operate you site in the United States:

USE_L10N = True

These settings for internationalisation are known to work in combination with django-cms and django-parler.

USE_I18N = True

LANGUAGE_CODE = 'en'

LANGUAGES = [
    ('en', "English"),
    ('de', "Deutsch"),
]

PARLER_DEFAULT_LANGUAGE = 'en'

PARLER_LANGUAGES = {
    1: [
        {'code': 'de'},
        {'code': 'en'},
    ],
    'default': {
        'fallbacks': ['de', 'en'],
    },
}

CMS_LANGUAGES = {
    'default': {
        'fallbacks': ['en', 'de'],
        'redirect_on_fallback': True,
        'public': True,
        'hide_untranslated': False,
    },
    1: [{
        'public': True,
        'code': 'en',
        'hide_untranslated': False,
        'name': 'English',
        'redirect_on_fallback': True,
    }, {
        'public': True,
        'code': 'de',
        'hide_untranslated': False,
        'name': 'Deutsch',
        'redirect_on_fallback': True,
    },]
}

19.2.1. REST Framework

The REST framework requires special settings. We namely must inform it how to serialize our special Money type:

REST_FRAMEWORK = {
    'DEFAULT_RENDERER_CLASSES': (
        'shop.rest.money.JSONRenderer',
        'rest_framework.renderers.BrowsableAPIRenderer',
    ),
    'DEFAULT_FILTER_BACKENDS': ('rest_framework.filters.DjangoFilterBackend',),
    'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.LimitOffsetPagination',
    'PAGE_SIZE': 12,
}

SERIALIZATION_MODULES = {'json': 'shop.money.serializers'}

Since the client side is not allowed to do any price and quantity computations, Decimal values are transferred to the client using strings. This also avoids nasty rounding errors.

COERCE_DECIMAL_TO_STRING = True

19.2.2. Django-CMS and Cascade settings

Django-SHOP requires at least one CMS template. Assure that it contains a placeholder able to accept

CMS_TEMPLATES = [
    ('myshop/pages/default.html', _("Default Page")),
]

CMS_PERMISSION = False

cascade_workarea_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)'],
    },
}

CMS_PLACEHOLDER_CONF = {
    'Breadcrumb': {
        'plugins': ['BreadcrumbPlugin'],
        'parent_classes': {'BreadcrumbPlugin': None},
        'glossary': cascade_workarea_glossary,
    },
    'Commodity Details': {
        'plugins': ['BootstrapContainerPlugin', 'BootstrapJumbotronPlugin'],
        'parent_classes': {
            'BootstrapContainerPlugin': None,
            'BootstrapJumbotronPlugin': None,
        },
        'glossary': cascade_workarea_glossary,
    },
    'Main Content': {
        'plugins': ['BootstrapContainerPlugin', 'BootstrapJumbotronPlugin'],
        'parent_classes': {
            'BootstrapContainerPlugin': None,
            'BootstrapJumbotronPlugin': None,
            'TextLinkPlugin': ['TextPlugin', 'AcceptConditionPlugin'],
        },
        'glossary': cascade_workarea_glossary,
    },
    'Static Footer': {
        'plugins': ['BootstrapContainerPlugin', ],
        'parent_classes': {
            'BootstrapContainerPlugin': None,
        },
        'glossary': cascade_workarea_glossary,
    },
}

Django-SHOP enriches djangocms-cascade with a few shop specific plugins.

from cmsplugin_cascade.extra_fields.config import PluginExtraFieldsConfig

CMSPLUGIN_CASCADE_PLUGINS = [
    'cmsplugin_cascade.segmentation',
    'cmsplugin_cascade.generic',
    'cmsplugin_cascade.icon',
    'cmsplugin_cascade.link',
    'shop.cascade',
    'cmsplugin_cascade.bootstrap3',
]

CMSPLUGIN_CASCADE = {
    'link_plugin_classes': [
        'shop.cascade.plugin_base.CatalogLinkPluginBase',
        'cmsplugin_cascade.link.plugin_base.LinkElementMixin',
        'shop.cascade.plugin_base.CatalogLinkForm',
    ],
    'alien_plugins': ['TextPlugin', 'TextLinkPlugin', 'AcceptConditionPlugin'],
    'bootstrap3': {
        'template_basedir': 'angular-ui',
    },
    'plugins_with_sharables': {
        'BootstrapImagePlugin': ['image_shapes', 'image_width_responsive', 'image_width_fixed',
                                 'image_height', 'resize_options'],
        'BootstrapPicturePlugin': ['image_shapes', 'responsive_heights', 'image_size', 'resize_options'],
    },
    'bookmark_prefix': '/',
    'segmentation_mixins': [
        ('shop.cascade.segmentation.EmulateCustomerModelMixin', 'shop.cascade.segmentation.EmulateCustomerAdminMixin'),
    ],
    'allow_plugin_hiding': True,
}

Since we want to add arbitrary links onto the detail view of a product, django-SHOP offers a modified link plugin. This has to be enabled using the 3-tuple link_plugin_classes.

Django-SHOP uses AngularJS rather than jQuery to control its dynamic HTML widgets. We therefore have to override the default with this settings: CMSPLUGIN_CASCADE['bootstrap3']['template_basedir'].

For a detailed explanation of these configuration settings, please refer to the documentation of djangocms-cascade.

19.2.3. CK Text Editor settings

By default, django-CMS uses the CKEditor plugin which can be heavily configured. Settings which have shown to be useful are:

CKEDITOR_SETTINGS_CAPTION = {
    'language': '{{ language }}',
    'skin': 'moono',
    'height': 70,
    'toolbar_HTMLField': [
        ['Undo', 'Redo'],
        ['Format', 'Styles'],
        ['Bold', 'Italic', 'Underline', '-', 'Subscript', 'Superscript', '-', 'RemoveFormat'],
        ['Source']
    ],
}

CKEDITOR_SETTINGS_DESCRIPTION = {
    'language': '{{ language }}',
    'skin': 'moono',
    'height': 250,
    'toolbar_HTMLField': [
        ['Undo', 'Redo'],
        ['cmsplugins', '-', 'ShowBlocks'],
        ['Format', 'Styles'],
        ['TextColor', 'BGColor', '-', 'PasteText', 'PasteFromWord'],
        ['Maximize', ''],
        '/',
        ['Bold', 'Italic', 'Underline', '-', 'Subscript', 'Superscript', '-', 'RemoveFormat'],
        ['JustifyLeft', 'JustifyCenter', 'JustifyRight'],
        ['HorizontalRule'],
        ['NumberedList', 'BulletedList', '-', 'Outdent', 'Indent', '-', 'Table'],
        ['Source']
    ],
}

19.2.4. Media assets handling

Django-CMS and django-SHOP rely on django-filer in combination with easy-thumbnails to manage the media assets.

MEDIA_ROOT = '/path/to/project/media'

MEDIA_URL = '/media/'

FILER_ALLOW_REGULAR_USERS_TO_ADD_ROOT_FOLDERS = True

FILE_UPLOAD_MAX_MEMORY_SIZE = 5242880

THUMBNAIL_OPTIMIZE_COMMAND = {
    'gif': '/usr/bin/optipng {filename}',
    'jpeg': '/usr/bin/jpegoptim {filename}',
    'png': '/usr/bin/optipng {filename}'
}

THUMBNAIL_PRESERVE_EXTENSIONS = True

THUMBNAIL_PROCESSORS = [
    'easy_thumbnails.processors.colorspace',
    'easy_thumbnails.processors.autocrop',
    'filer.thumbnail_processors.scale_and_crop_with_subject_location',
    'easy_thumbnails.processors.filters',
]

all settings are explained in detail in the documentation of django-filer and easy-thumbnails.

19.2.6. AngularJS specific settings

The cart’s totals are updated after an input field has been changed. For usability reasons it makes sense to delay this, so that only after a certain time of inactivity, the update is triggered.

SHOP_ADD2CART_NG_MODEL_OPTIONS = "{updateOn: 'default blur', debounce: {'default': 500, 'blur': 0}}"

This configuration updates the cart after changing the quantity and 500 milliseconds of inactivity or field blurring. It is used by the “Add to cart” form.

SHOP_EDITCART_NG_MODEL_OPTIONS = "{updateOn: 'default blur', debounce: {'default': 2500, 'blur': 0}}"

This configuration updates the cart after changing any of the product’s quantities and 2.5 seconds of inactivity or field blurring. It is used by the “Edit cart” form.

19.2.7. Select2 specific settings

django-select2 adds a configurable autocompletion field to the project.

Change the include path to a local directory, if you prefer to install the JavaScript dependencies via npm instead of relying on a preconfigured CDN:

SELECT2_CSS = 'node_modules/select2/dist/css/select2.min.css'
SELECT2_JS = 'node_modules/select2/dist/js/select2.min.js'