Open source catalog management system based on the Django framework and django-mptt library.
- Django 1.11
- Python 2.7
- django-classy-tags >=0.3 and <=0.9.0
- django-mptt 0.9.*
-
Install package
pip install git+git://github.com/oldroute/django-catalog-tree
-
Create application for your custom catalog models, for example
custom_catalog
models.py
from django.db import models from catalog.models import CatalogBase ... class Root(CatalogBase): title = models.CharField(max_length=400) def get_absolute_url(self): return reverse('catalog-root') def __str__(self, *args, **kwargs): return self.title class Section(CatalogBase): title = models.CharField(max_length=400) def __str__(self): return self.title class Product(CatalogBase): leaf = True # cannot have children title = models.CharField(max_length=400) def __str__(self): return self.title class Category(CatalogBase): leaf = True # cannot have children title = models.CharField(max_length=400) items = models.ManyToManyField(Item, blank=True, null=True, related_name='categories') def __str__(self): return self.title
This is example of simple catalog structure based on four typical models:
Root
- represent catalog root pageSection
- model for grouping products based on inheritanceProduct
- endpoint of catalog tree - represent product pageCategory
- special model for grouping products from any section by common attribute
admin.py
from django.contrib import admin from catalog.admin import CatalogItemBaseAdmin from .models import Root, Section, Product, Category ... @admin.register(Root) class RootAdmin(CatalogItemBaseAdmin): model = Root def has_add_permission(self, request): return not bool(Root.objects.exists()) def has_delete_permission(self, request, obj=None): return False @admin.register(Product) class ProductAdmin(CatalogItemBaseAdmin): model = Product @admin.register(Section) class SectionAdmin(CatalogItemBaseAdmin): model = Section @admin.register(Category) class CategoryAdmin(CatalogItemBaseAdmin): model = Category
Create other app files like
apps.py
. Application setup details are not relevant to this guide. -
Configure your setting file:
CATALOG_MODELS
- list of models that will be available for creation in the admin catalog interface. Simple example of configuration:INSTALLED_APPS += ['catalog', '<PROJECT_ROOT>.custom_catalog'] CATALOG_MODELS = [ 'custom_catalog.Section', 'custom_catalog.Product', 'custom_catalog.Category', ]
-
Add urlpattern to main urls.py:
urlpatterns = [ ... url(r'^catalog/', include('catalog.urls')), ... ]
-
Create templates
For each catalog model your need to create template. Follow next templates structure:
- catalog/
- breadcrumbs.html (default template exist)
- children_tag.html (default template exist)
- product.html
- root.html
- section.html
- category.html
The object instance is accessible in the template through the variable
object
or by model name. Consider how to work with data in a templates:catalog/base.html
{% load catalog_tags %} ... <h1>{{ object.title }}</h1> <div class='breadcrumbs'> {% block breadcrumbs %} {{ block.super }} {% catalog_breadcrumbs object %} {% endblock %} </div> {% block content %}{% endblock %}
In this examle
{% catalog_breadcrumbs object %}
include rendered catalog bredcrumbs for object (for customize see templatebreadcrumbs.html
). For example: Catalog-> Section-> Productcatalog/section.html
{% extends 'catalog/base.html' %} {% load catalog_tags %} {% block content %} <div class='products'> <!-- Show products here --> </div> {% endblock %}
In this example we need display products of current section. Consider ways to display a list of products
{% catalog_children for object %}
- renderchildren_tag.html
with section all children.{% catalog_children for object type product %}
- renderchildren_tag.html
with section products-children.{% catalog_children for object type product descendants all %}
- renderchildren_tag.html
with section products-descendants.{% catalog_children for object type product descendants all as descendants %}
- get section product-descendants as varibledescendants
- catalog/
-
Apply migrations and run local server
python manage.py migrate python manage.py runserver
-
Create catalog root and some catalog items in admin catalog app.
Configure is done!
All catalog items consist of two records:
- content_object - is instance of your custom models (like Product, Section) with represent custom fields.
- tree - node to represent postion in catalog structure.
Example №1. Get section tree node: section.tree.get()
Example №2. Get section children tree nodes section.tree.get().get_children()
- QuerySet in result
Example №3. Get section children products
from catalog.utils import get_content_objects
products = get_content_objects(section.tree.get().get_children()) # list in result
Example №4. Get section children products sorted by catalog tree structure
from catalog.utils import get_content_objects, get_sorted_content_objects
sorted_products = get_sorted_content_objects(get_content_objects(section.tree.get().get_children()))
See other tree methods in django-mptt docs
If your need to control catalog states you may handle following signals
- content_object_moved - fired for content_object when tree node moved. Signal provides next kwargs:
- instance - content_object
- parent_from - old parent content_object (moved from)
- parent_to - new parent content_object (moved to)
- content_object_parent_changed - fired for content_object when tree node moved by tree and when old and new parent do not match. Provides the same kwargs.
- content_object_created - fired for content_object when a new record is created. Provides next kwargs:
- instance - content_object
- parent - parent content_object (None for root nodes)
- node_moved - fired for tree node when it moved by tree. Provide next kwargs:
- instance - tree node
- target - new parent tree node (moved to)
- position - one of predefined values, see django-mptt docs
Use case example:
signals.py
from mptt.signals import node_moved
from catalog.models import TreeItem
from catalog.signals import content_object_parent_changed, content_object_created
from <PROJECT_ROOT>.custom_catalog.models import Product
def handler(sender, instance, **kwargs):
# for every signal **kwargs contain their provides kwargs
# for example parent_from = kwargs.get("parent_from")
# Do something
node_moved.connect(handler, sender=TreeItem)
content_object_created(handler)
content_object_parent_changed.connect(handler)
content_object_moved(handler)