2
\$\begingroup\$

For Django sites with URLs structured in a hierarchical way I found it helpful to have an app to quickly transform an URL like https://example.com/reference/instrument/guitar/ into Home > Reference > Instrument > Guitar with links to each path part.

The following code does that:

  1. A context processor analyze the URL request e.g. https://example.com/reference/instrument/guitar/, and extracts the path: /reference/instrument/guitar/

dynamic_breadcrumbs/context_processors.py

from dynamic_breadcrumbs.utils import Breadcrumbs

def breadcrumbs(request):
    """
    Add breadcrumbs dict to the context.
    """
    base_url = request.build_absolute_uri("/")
    breadcrumbs = Breadcrumbs(base_url=base_url, path=request.path)
    return {"breadcrumbs": breadcrumbs.as_list()}
  1. For each part separated by / tries to resolve it and get the specific View that handles that URL (for customizing that part of the breadcrumbs label, and possibly further customization in the future)

dynamic_breadcrumbs/utils.py

from django.urls import resolve, Resolver404
from urllib.parse import urljoin

from . import app_settings

class Breadcrumbs:
    def __init__(self, base_url="", path=None):
        self.base_url = base_url
        self.path = path
        self.items = []

    def get_items(self):
        if not self.items:
            self._fill_items()
        return self.items

    def as_list(self):
        return [item.as_dict() for item in self.get_items()]

    def _split_path(self, path=None):
        """Returns a list of the path components between slashes"""
        if not path:
            path = self.path
        if path.endswith("/"):
            path = path[:-1]
        if path.startswith("/"):
            path = path[1:]
        result = path.split("/")
        return result

    def _fill_items(self):
        # add home
        b_item = BreadcrumbsItem(
            base_url=self.base_url,
            name_raw=app_settings.DYNAMIC_BREADCRUMBS_HOME_LABEL,
            path="/",
            position=1
        )
        self.items.append(b_item)

        # add paths
        path = "/"
        for i, item in enumerate(self._split_path()):
            path = urljoin(path, item + "/")
            b_item = BreadcrumbsItem(
                base_url=self.base_url, name_raw=item, path=path, position=i + 1
            )
            self.items.append(b_item)


class BreadcrumbsItem:
    def __init__(self, name_raw, path, position, base_url=None):
        self.name_raw = name_raw
        self.path = path
        self.position = position
        self.resolved_url = self._get_resolved_url_metadata()
        self.base_url = base_url

    def _get_resolved_url_metadata(self):
        try:
            func, args, kwargs = resolve(self.path)
            return True
        except Resolver404:
            return False

    def get_url(self):
        result = urljoin(self.base_url, self.path)
        return result

    def get_name(self):
        # if self.resolved_url:
        #     # check view
        return self.name_raw

    def as_dict(self):
        result = {
            "position": self.position,
            "name": self.get_name(),
            "path": self.path,
            "url": self.get_url(),
            "resolved": self.resolved_url,
        }
        return result

    def __str__(self):
        return "{}: {} {}".format(self.position, self.name_raw, self.path)

  1. Adds to the request context a list of names and urls
  1. A template shows the above list with links for each level

    Home > Reference > Instrument > Guitar

templates/dynamic_breadcrumbs/breadcrumbs.html

{% for item in breadcrumbs %}
  {% if item.resolved %}<a href="{{item.url}}">{% endif %}
        <span>{{item.name}}</span>
        <meta itemprop="position" content="{{item.position}}" />
  {% if item.resolved %}</a>{%endif%}
{% endfor %}

The way the Breadcrumbs class processes the path can be improved or can be structured in a simpler way?

\$\endgroup\$

0

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Browse other questions tagged or ask your own question.