> ## Documentation Index
> Fetch the complete documentation index at: https://buttercms.com/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Django

> Build content-driven Django applications with ButterCMS. Covers setup, pages, collections, components, and blog integration.

## Overview

This integration guide shows you how to how to update your existing project to:

1. install the ButterCMS package
2. instantiate ButterCMS
3. create components to fetch and display each of the three ButterCMS content types: [Pages](../../core-concepts/content-types/pages), [Collections](../../core-concepts/content-types/collections), and [Blog Posts](../../core-concepts/content-types/blog-engine).

<Tip>
  In order for the snippets to work, you'll need to [setup your dashboard content schemas inside of ButterCMS](../buttercms-setup) first.
</Tip>

### Starter project

Or, you can jump directly to the starter project below, which will allow you to clone, install, run, and deploy a fully working
starter project that's integrated with content already inside of your ButterCMS account.

<Card title="Django Starter Project" icon="rocket" href="../starter-projects/django">
  Hit the ground running with a pre-configured Django + ButterCMS setup.
</Card>

## Installation

<Tabs>
  <Tab title="pip">
    ```bash theme={null}
    pip install buttercms-python
    ```
  </Tab>

  <Tab title="pipenv">
    ```bash theme={null}
    pipenv install buttercms-python
    ```
  </Tab>

  <Tab title="poetry">
    ```bash theme={null}
    poetry add buttercms-python
    ```
  </Tab>
</Tabs>

Add your API token to your environment or `settings.py`:

```python theme={null}
# settings.py
import os

BUTTERCMS_API_TOKEN = os.environ.get('BUTTERCMS_API_TOKEN', 'your_api_token')
```

## Initialize the client

Create a reusable client instance:

```python theme={null}
# myapp/buttercms_client.py
from butter_cms import ButterCMS
from django.conf import settings

client = ButterCMS(settings.BUTTERCMS_API_TOKEN)
```

<Info>
  For complete SDK documentation including all available methods and configuration options, see the [Python SDK Reference](../sdks/python-sdk).
</Info>

## Pages

<Tabs>
  <Tab title="Function-Based Views">
    ```python theme={null}
    # myapp/views.py
    from django.shortcuts import render
    from django.http import Http404
    from .buttercms_client import client

    def landing_page(request, slug='home'):
        try:
            response = client.pages.get('landing_page', slug)
            page = response['data']
        except:
            raise Http404("Page not found")

        return render(request, 'landing.html', {'page': page})
    ```

    ```python theme={null}
    # myapp/urls.py
    from django.urls import path
    from . import views

    urlpatterns = [
        path('', views.landing_page, name='home'),
        path('page/<slug:slug>/', views.landing_page, name='landing_page'),
    ]
    ```
  </Tab>

  <Tab title="Class-Based Views">
    ```python theme={null}
    # myapp/views.py
    from django.views.generic import TemplateView
    from django.http import Http404
    from .buttercms_client import client

    class LandingPageView(TemplateView):
        template_name = 'landing.html'

        def get_context_data(self, **kwargs):
            context = super().get_context_data(**kwargs)
            slug = self.kwargs.get('slug', 'home')
            try:
                response = client.pages.get('landing_page', slug)
                context['page'] = response['data']
            except:
                raise Http404("Page not found")
            return context
    ```

    ```python theme={null}
    # myapp/urls.py
    from django.urls import path
    from .views import LandingPageView

    urlpatterns = [
        path('', LandingPageView.as_view(), name='home'),
        path('page/<slug:slug>/', LandingPageView.as_view(), name='landing_page'),
    ]
    ```
  </Tab>
</Tabs>

```html theme={null}
<!-- templates/landing.html -->
{% extends 'base.html' %}

{% block content %}
<main>
    <h1>{{ page.fields.headline }}</h1>
    <p>{{ page.fields.subheadline }}</p>
    {% if page.fields.hero_image %}
    <img src="{{ page.fields.hero_image }}" alt="{{ page.fields.headline }}" />
    {% endif %}
    {{ page.fields.body|safe }}
</main>
{% endblock %}
```

## Collections

<Tabs>
  <Tab title="Function-Based Views">
    ```python theme={null}
    # myapp/views.py
    from django.shortcuts import render
    from .buttercms_client import client

    def brands(request):
        response = client.content_fields.get(['brands'])
        brands = response['data']['brands']
        return render(request, 'brands.html', {'brands': brands})
    ```

    ```python theme={null}
    # myapp/urls.py
    urlpatterns = [
        path('brands/', views.brands, name='brands'),
    ]
    ```
  </Tab>

  <Tab title="Class-Based Views">
    ```python theme={null}
    # myapp/views.py
    from django.views.generic import TemplateView
    from .buttercms_client import client

    class BrandsView(TemplateView):
        template_name = 'brands.html'

        def get_context_data(self, **kwargs):
            context = super().get_context_data(**kwargs)
            response = client.content_fields.get(['brands'])
            context['brands'] = response['data']['brands']
            return context
    ```

    ```python theme={null}
    # myapp/urls.py
    from .views import BrandsView

    urlpatterns = [
        path('brands/', BrandsView.as_view(), name='brands'),
    ]
    ```
  </Tab>
</Tabs>

```html theme={null}
<!-- templates/brands.html -->
{% extends 'base.html' %}

{% block content %}
<main>
    <h1>Our Brands</h1>
    <ul>
        {% for brand in brands %}
        <li>
            <img src="{{ brand.logo }}" alt="{{ brand.name }}" />
            <h2>{{ brand.name }}</h2>
            {{ brand.description|safe }}
        </li>
        {% endfor %}
    </ul>
</main>
{% endblock %}
```

## Dynamic components

### Component renderer

```python theme={null}
# myapp/components.py
from django.template.loader import render_to_string

COMPONENT_TEMPLATES = {
    'hero': 'components/hero.html',
    'features': 'components/features.html',
    'testimonials': 'components/testimonials.html',
    'cta': 'components/cta.html',
}

def render_components(components):
    """Render a list of ButterCMS components to HTML."""
    html_parts = []
    for component in components:
        template = COMPONENT_TEMPLATES.get(component['type'])
        if template:
            html = render_to_string(template, {'fields': component['fields']})
            html_parts.append(html)
    return ''.join(html_parts)
```

### Example component template

```html theme={null}
<!-- templates/components/hero.html -->
<section class="hero">
    <h1>{{ fields.headline }}</h1>
    <p>{{ fields.subheadline }}</p>
    {% if fields.button_label %}
    <a href="{{ fields.button_url }}" class="btn">{{ fields.button_label }}</a>
    {% endif %}
    {% if fields.image %}
    <img src="{{ fields.image }}" alt="{{ fields.headline }}" />
    {% endif %}
</section>
```

### Using in views

<Tabs>
  <Tab title="Function-Based Views">
    ```python theme={null}
    # myapp/views.py
    from django.shortcuts import render
    from django.http import Http404
    from .buttercms_client import client
    from .components import render_components

    def component_page(request, slug):
        try:
            response = client.pages.get('component_page', slug)
            page = response['data']
        except:
            raise Http404("Page not found")

        # Render components to HTML
        components_html = render_components(page['fields'].get('body', []))

        return render(request, 'component_page.html', {
            'page': page,
            'components_html': components_html,
        })
    ```
  </Tab>

  <Tab title="Class-Based Views">
    ```python theme={null}
    # myapp/views.py
    from django.views.generic import TemplateView
    from django.http import Http404
    from .buttercms_client import client
    from .components import render_components

    class ComponentPageView(TemplateView):
        template_name = 'component_page.html'

        def get_context_data(self, **kwargs):
            context = super().get_context_data(**kwargs)
            slug = self.kwargs.get('slug')
            try:
                response = client.pages.get('component_page', slug)
                page = response['data']
            except:
                raise Http404("Page not found")

            context['page'] = page
            context['components_html'] = render_components(
                page['fields'].get('body', [])
            )
            return context
    ```
  </Tab>
</Tabs>

```html theme={null}
<!-- templates/component_page.html -->
{% extends 'base.html' %}

{% block content %}
<main>
    {{ components_html|safe }}
</main>
{% endblock %}
```

## Blog

<Tabs>
  <Tab title="Blog Post List">
    <Tabs>
      <Tab title="Function-Based Views">
        ```python theme={null}
        # myapp/views.py
        from django.shortcuts import render
        from .buttercms_client import client

        def blog_list(request):
            page = request.GET.get('page', 1)
            response = client.posts.all({'page': page, 'page_size': 10})
            return render(request, 'blog/list.html', {
                'posts': response['data'],
                'meta': response['meta'],
            })
        ```

        ```python theme={null}
        # myapp/urls.py
        urlpatterns = [
            path('blog/', views.blog_list, name='blog_list'),
        ]
        ```
      </Tab>

      <Tab title="Class-Based Views">
        ```python theme={null}
        # myapp/views.py
        from django.views.generic import TemplateView
        from .buttercms_client import client

        class BlogListView(TemplateView):
            template_name = 'blog/list.html'

            def get_context_data(self, **kwargs):
                context = super().get_context_data(**kwargs)
                page = self.request.GET.get('page', 1)
                response = client.posts.all({'page': page, 'page_size': 10})
                context['posts'] = response['data']
                context['meta'] = response['meta']
                return context
        ```

        ```python theme={null}
        # myapp/urls.py
        from .views import BlogListView

        urlpatterns = [
            path('blog/', BlogListView.as_view(), name='blog_list'),
        ]
        ```
      </Tab>
    </Tabs>

    ```html theme={null}
    <!-- templates/blog/list.html -->
    {% extends 'base.html' %}

    {% block content %}
    <main>
        <h1>Blog</h1>
        <ul>
            {% for post in posts %}
            <li>
                <h2><a href="{% url 'blog_post' post.slug %}">{{ post.title }}</a></h2>
                {{ post.summary|safe }}
                <span>By {{ post.author.first_name }} {{ post.author.last_name }}</span>
            </li>
            {% endfor %}
        </ul>
        {% if meta.next_page %}
        <a href="?page={{ meta.next_page }}">Next Page</a>
        {% endif %}
    </main>
    {% endblock %}
    ```
  </Tab>

  <Tab title="Single Blog Post">
    <Tabs>
      <Tab title="Function-Based Views">
        ```python theme={null}
        # myapp/views.py
        from django.shortcuts import render
        from django.http import Http404
        from .buttercms_client import client

        def blog_post(request, slug):
            try:
                response = client.posts.get(slug)
                post = response['data']
            except:
                raise Http404("Post not found")

            return render(request, 'blog/post.html', {'post': post})
        ```

        ```python theme={null}
        # myapp/urls.py
        urlpatterns = [
            path('blog/<slug:slug>/', views.blog_post, name='blog_post'),
        ]
        ```
      </Tab>

      <Tab title="Class-Based Views">
        ```python theme={null}
        # myapp/views.py
        from django.views.generic import TemplateView
        from django.http import Http404
        from .buttercms_client import client

        class BlogPostView(TemplateView):
            template_name = 'blog/post.html'

            def get_context_data(self, **kwargs):
                context = super().get_context_data(**kwargs)
                slug = self.kwargs.get('slug')
                try:
                    response = client.posts.get(slug)
                    context['post'] = response['data']
                except:
                    raise Http404("Post not found")
                return context
        ```

        ```python theme={null}
        # myapp/urls.py
        from .views import BlogPostView

        urlpatterns = [
            path('blog/<slug:slug>/', BlogPostView.as_view(), name='blog_post'),
        ]
        ```
      </Tab>
    </Tabs>

    ```html theme={null}
    <!-- templates/blog/post.html -->
    {% extends 'base.html' %}

    {% block content %}
    <article>
        <h1>{{ post.title }}</h1>
        <p>
            By {{ post.author.first_name }} {{ post.author.last_name }}
            on {{ post.published|date:"F j, Y" }}
        </p>
        {% if post.featured_image %}
        <img src="{{ post.featured_image }}" alt="{{ post.title }}" />
        {% endif %}
        {{ post.body|safe }}
        <a href="{% url 'blog_list' %}">Back to Posts</a>
    </article>
    {% endblock %}
    ```
  </Tab>
</Tabs>

## Caching

Use Django's cache framework for better performance:

```python theme={null}
# myapp/views.py
from django.core.cache import cache
from django.shortcuts import render
from .buttercms_client import client

def cached_landing_page(request, slug='home'):
    cache_key = f'butter_page_landing_page_{slug}'
    page = cache.get(cache_key)

    if not page:
        try:
            response = client.pages.get('landing_page', slug)
            page = response['data']
            cache.set(cache_key, page, 3600)  # Cache for 1 hour
        except:
            raise Http404("Page not found")

    return render(request, 'landing.html', {'page': page})
```

### Webhook cache invalidation

```python theme={null}
# myapp/views.py
from django.core.cache import cache
from django.http import JsonResponse
from django.views.decorators.csrf import csrf_exempt
from django.views.decorators.http import require_POST
import json

@csrf_exempt
@require_POST
def buttercms_webhook(request):
    payload = json.loads(request.body)
    webhook_type = payload.get('webhook_type', '')

    if 'published' in webhook_type or 'updated' in webhook_type or 'deleted' in webhook_type:
        # Clear all ButterCMS cache keys
        cache.delete_many(cache.keys('butter_*'))

    return JsonResponse({'status': 'ok'})
```

## SEO

```python theme={null}
# myapp/context_processors.py
def seo_context(request):
    """Add SEO fields from ButterCMS page to context."""
    return {}

# myapp/views.py
def landing_page_with_seo(request, slug='home'):
    try:
        response = client.pages.get('landing_page', slug)
        page = response['data']
    except:
        raise Http404("Page not found")

    seo = page['fields'].get('seo', {})

    return render(request, 'landing.html', {
        'page': page,
        'seo_title': seo.get('title', page['fields'].get('headline', '')),
        'seo_description': seo.get('description', ''),
        'og_title': seo.get('og_title', seo.get('title', '')),
        'og_description': seo.get('og_description', seo.get('description', '')),
        'og_image': seo.get('og_image', ''),
    })
```

```html theme={null}
<!-- templates/base.html -->
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>{{ seo_title|default:"My Site" }}</title>
    <meta name="description" content="{{ seo_description }}">

    <!-- Open Graph -->
    <meta property="og:title" content="{% firstof og_title seo_title %}">
    <meta property="og:description" content="{% firstof og_description seo_description %}">
    {% if og_image %}
    <meta property="og:image" content="{{ og_image }}">
    {% endif %}
</head>
<body>
    {% block content %}{% endblock %}
</body>
</html>
```

## Resources

<CardGroup cols={2}>
  <Card title="Django Starter" icon="rocket" href="../starter-projects/django">
    Pre-configured starter project
  </Card>

  <Card title="Python SDK" icon="python" href="../sdks/python-sdk">
    Complete SDK reference
  </Card>

  <Card title="GitHub Repository" icon="github" href="https://github.com/ButterCMS/django-starter-buttercms">
    View source code
  </Card>

  <Card title="Content API" icon="database" href="../../api-reference/pages/get-multiple-pages">
    REST API documentation
  </Card>
</CardGroup>
