# Django

## Getting Started

## Create a connection

1. Sign in to [OpenGraph+](/)
2. Go to your website's **Meta Tags** page
3. Create a new connection and copy your connection URL

Your connection URL looks like `https://$OGPLUS_KEY.ogplus.net`.

## Find your base template

Django uses template inheritance. Your base template is typically `templates/base.html`. All other templates extend it with `{% extends "base.html" %}`.

## Add Open Graph meta tags

Add the following meta tags to your base template's `<head>`. These control how your pages appear when shared on Twitter, Slack, LinkedIn, and other platforms.

```html
<!-- templates/base.html -->
<html>
  <head>
    <meta property="og:title" content="{% block og_title %}My Site{% endblock %}">
    <meta property="og:description" content="{% block og_description %}Welcome to my site{% endblock %}">
    <meta property="og:url" content="{{ request.build_absolute_uri }}">
    <meta property="og:site_name" content="My Site">
    <meta property="og:type" content="{% block og_type %}website{% endblock %}">
    <meta property="og:image" content="https://$OGPLUS_KEY.ogplus.net{{ request.path }}">
    <meta name="twitter:card" content="summary_large_image">
  </head>
  <body>
    {% block content %}{% endblock %}
  </body>
</html>
```

Replace `https://$OGPLUS_KEY.ogplus.net` with your connection URL, and `"My Site"` with your actual site name.

### What each tag does

| Tag | Purpose |
|-----|---------|
| `og:title` | The title shown in the link preview |
| `og:description` | The description shown below the title |
| `og:url` | The canonical URL of the page |
| `og:site_name` | Your website's name |
| `og:type` | Content type (`website`, `article`, `product`) |
| `og:image` | The social card image (powered by OpenGraph+) |
| `twitter:card` | Tells Twitter to show a large image card |

## Dynamic tags from views

Override the blocks in child templates to set page-specific values from your model data:

### Blog posts

```html
<!-- templates/blog/post_detail.html -->
{% extends "base.html" %}

{% block og_title %}{{ post.title }}{% endblock %}
{% block og_description %}{{ post.excerpt }}{% endblock %}
{% block og_type %}article{% endblock %}

{% block content %}
  <h1>{{ post.title }}</h1>
  <p>{{ post.body }}</p>
{% endblock %}
```

### Products

```html
<!-- templates/products/product_detail.html -->
{% extends "base.html" %}

{% block og_title %}{{ product.name }}{% endblock %}
{% block og_description %}{{ product.price }} — {{ product.description }}{% endblock %}
{% block og_type %}product{% endblock %}

{% block content %}
  <h1>{{ product.name }}</h1>
{% endblock %}
```

## Enable the request context processor

`request.path` and `request.build_absolute_uri` require the `django.template.context_processors.request` context processor. This is enabled by default in new Django projects. If it's not present, add it:

```python
# settings.py
TEMPLATES = [
    {
        'OPTIONS': {
            'context_processors': [
                'django.template.context_processors.request',
                # ... other processors
            ],
        },
    },
]
```

## Verify

Start your development server and view the page source. Check that all `og:` meta tags are present with the correct values.

Open the preview tool in your OpenGraph+ dashboard and paste a URL from your site to see the social card image.


## Customize

OpenGraph+ captures your page in a headless browser and renders it as an image. You control what gets captured using meta tags in your layout and CSS in your stylesheets.

All of the rendering options below are standard HTML meta tags and CSS, so they work the same regardless of framework. The [HTML, CSS, & HTTP guide](/docs/html-css) covers each one in detail. This page shows how to wire them into a Django app.

## Meta tags

Add these to your base template's `<head>` alongside your `og:image` tag. They're all optional.

```html
<!-- templates/base.html -->
<head>
  <meta property="og:image" content="https://$OGPLUS_KEY.ogplus.net{{ request.path }}">
  <meta name="twitter:card" content="summary_large_image">

  {# Render at 800px wide instead of the default #}
  <meta property="og:plus:viewport:width" content="800">

  {# Only capture this element instead of the full page #}
  <meta property="og:plus:selector" content=".post-header">

  {# Inject inline styles on the captured element #}
  <meta property="og:plus:style" content="padding: 60px; background: #0f172a; color: white;">
</head>
```

See the [Rendering](/docs/html-css/rendering) guide for what each meta tag does and how they interact.

## CSS styling

OpenGraph+ adds a `data-ogplus` attribute to your `<html>` element during capture. Use it in your stylesheets to hide navigation, adjust spacing, or restyle anything for the social card without affecting your actual site.

```css
/* static/css/social-cards.css */
html[data-ogplus] {
  nav { display: none; }
  footer { display: none; }
  .hero { padding: 60px; }
}
```

If you're using Tailwind, there's a plugin that gives you `ogplus:` variants like `ogplus:hidden` and `ogplus-twitter:bg-sky-500`. See [CSS Styling](/docs/html-css/data-attributes) for plain CSS examples and [Tailwind setup](/docs/html-css/data-attributes#tailwind-css).

## Templates

For fully custom social card layouts that pull content from your page, use `<template>` elements. These let you build a completely different layout for screenshots without touching your visible page.

```html
<!-- templates/base.html -->
<template id="ogplus">
  <div style="padding: 48px; background: #0f172a; color: white; height: 100%;">
    <h1 style="font-size: 48px;">
      ${document.querySelector('h1')?.textContent}
    </h1>
  </div>
</template>
```

See the [Templates](/docs/html-css/templates) guide for expression syntax, platform-specific templates, and full examples.

## Testing locally

The [Preview Bookmarklet](/docs/html-css/bookmarklet) sets the `data-ogplus` attribute in your browser so you can see how your CSS and Tailwind variants look without deploying or waiting for a real crawler to hit your page.

## Full example

A blog post layout with all the pieces together:

```html
<head>
  <meta property="og:title" content="{% block og_title %}My Site{% endblock %}">
  <meta property="og:description" content="{% block og_description %}Welcome{% endblock %}">
  <meta property="og:image" content="https://$OGPLUS_KEY.ogplus.net{{ request.path }}">
  <meta name="twitter:card" content="summary_large_image">
  <meta property="og:plus:selector" content=".post-header">
  <meta property="og:plus:style" content="padding: 60px; background-color: #0f172a; color: white;">
  <meta property="og:plus:viewport:width" content="800">
</head>
```


## Caching

OpenGraph+ reads HTTP cache headers from your Django responses to decide when to re-render social card images. Django has built-in support for cache headers, so this is straightforward.

This page covers the Django side. For how OpenGraph+ handles caching at the HTTP level, see the [HTTP Caching](/docs/html-css/caching) guide.

## @cache_control decorator

The most direct way to set cache headers on a view:

```python
from django.views.decorators.cache import cache_control

@cache_control(max_age=86400, public=True)
def blog_post(request, slug):
    post = get_object_or_404(Post, slug=slug)
    return render(request, 'blog/post.html', {'post': post})
```

This sets `Cache-Control: public, max-age=86400`. OpenGraph+ serves the cached image for 24 hours without hitting your server.

## Class-based views

Use `method_decorator` to apply `@cache_control` to class-based views:

```python
from django.utils.decorators import method_decorator
from django.views.decorators.cache import cache_control
from django.views.generic import DetailView

@method_decorator(cache_control(max_age=86400, public=True), name='dispatch')
class PostDetailView(DetailView):
    model = Post
```

## ConditionalGetMiddleware

Add `django.middleware.http.ConditionalGetMiddleware` to your `MIDDLEWARE` setting for automatic ETag support. This middleware calculates ETags from response content and handles conditional requests automatically.

```python
# settings.py
MIDDLEWARE = [
    # ...
    'django.middleware.http.ConditionalGetMiddleware',
]
```

If your content hasn't changed, Django returns `304 Not Modified` and OpenGraph+ serves the cached image.

## Manual headers

Set headers directly on the response for full control:

```python
from django.shortcuts import render, get_object_or_404

def blog_post(request, slug):
    post = get_object_or_404(Post, slug=slug)
    response = render(request, 'blog/post.html', {'post': post})
    response['Cache-Control'] = 'public, max-age=3600'
    response['ETag'] = f'"{post.updated_at.isoformat()}"'
    return response
```

## Recommendations

| Page type | Strategy |
|-----------|----------|
| Static pages | `@cache_control(max_age=604800, public=True)` |
| Blog posts | `@cache_control(max_age=86400, public=True)` |
| Index pages | `@cache_control(max_age=3600, public=True)` |
| User dashboards | `@cache_control(no_store=True)` |

## Meta tag overrides

If you can't control HTTP headers, use meta tags instead. See the [HTTP Caching guide](/docs/html-css/caching) for the full list of meta tag overrides.

## Purging cached images

When you need to force a refresh immediately, go to your website dashboard, find the page, and click purge. This clears the cached image and triggers a re-render on the next request.


## Troubleshooting

Something not working? Here are the most common issues and how to fix them.

## Meta tags not appearing

Check that your template extends the base template that has the meta tags. View your page source to verify the `og:image` and `twitter:card` tags are present in the `<head>`.

Common causes:

1. Your template doesn't extend `base.html` (or whichever template has the meta tags)
2. The `request` variable isn't available in the template context

## request.path not available

`request.path` requires the `django.template.context_processors.request` context processor. This is enabled by default in new Django projects, but may be missing in older ones.

Add it to your `TEMPLATES` setting:

```python
# settings.py
TEMPLATES = [
    {
        'OPTIONS': {
            'context_processors': [
                'django.template.context_processors.request',
                # ... other processors
            ],
        },
    },
]
```

## Wrong image showing

This is almost always a caching issue. Social platforms and OpenGraph+ both cache images.

1. Open the preview tool in your OpenGraph+ dashboard
2. Paste your URL to see what OpenGraph+ currently has
3. If the image is stale, purge it from the dashboard
4. Re-check with the preview tool

## Social platforms not updating

Twitter, LinkedIn, and Slack cache images aggressively on their end. After confirming the correct image appears in the OpenGraph+ preview tool:

- **Twitter:** Use the [Card Validator](https://cards-dev.twitter.com/validator) to force a refresh
- **LinkedIn:** Use the [Post Inspector](https://www.linkedin.com/post-inspector/) to clear their cache
- **Facebook:** Use the [Sharing Debugger](https://developers.facebook.com/tools/debug/) to scrape again

This is platform-side caching that OpenGraph+ cannot control.

## Purging cached images

1. Go to your website dashboard in OpenGraph+
2. Find the page you want to refresh
3. Click purge to clear the cached image

The next request from a social platform will trigger a fresh render.

## Testing

Use the preview tool in your OpenGraph+ dashboard to verify your setup before sharing URLs. This shows you exactly what social platforms will see.

