# Django / Jinja

> SSR, script, signed-in customer and order detail integration for a store built with Django or Jinja templates.

Source: /en/integrate/django/

This page is for stores built with Django or Jinja templates. The Django kit provided by the Sayproof team brings the server call, caching, failure handling and template helpers ready made. The kit is copied into the store project; it needs no package installation and uses only Django and Python's standard library. It works with Python 3.9 and Django 2.2 and newer versions.

## SSR or script

The same template works with both methods; the only difference is the server key:

- **SSR (recommended):** When a server key is set, reviews are fetched on the server as ready HTML and are part of the page's first response. Search engines see the review text and rating without waiting for the script; the script takes over the section once it loads. Details: [Server-side rendering](/en/integrate/server-rendering.md).
- **Script:** When no server key is set, an empty container is printed and the script fetches reviews in the browser. The server makes no call to Sayproof. To switch to SSR later, only the server key is added.

## Setup

1. Copy the kit's `sayproof` folder into your project's app (for example `store_app/sayproof/`). The platform's own folders are not changed; the kit is only added to your app.
2. Define the settings:

   ```python
   SAYPROOF_BASE = os.environ.get("SAYPROOF_BASE", "")
   SAYPROOF_SITE_KEY = os.environ.get("SAYPROOF_SITE_KEY", "")
   SAYPROOF_SSR_KEY = os.environ.get("SAYPROOF_SSR_KEY", "")
   SAYPROOF_CHANNEL = os.environ.get("SAYPROOF_CHANNEL", "web")
   ```

   The **Sayproof address**, the **Site key** and the **Channel code** are shown with copy buttons in the **Values for the setup** list when **Django / Jinja (on the server)** is selected as the **Setup route** on the **Integration › Showing reviews** tab in the panel. The server key is not shown there; it is created on the **Channels › Keys** tab and appears only once, when it is created. The server key is kept only in the server's environment variables; it is never written into a template or the browser.

3. Register the template helpers in the Jinja environment's globals. If your settings file updates the globals with a dictionary (for example `GLOBAL_OPTIONS.update({...})`), add these lines:

   ```python
   GLOBAL_OPTIONS.update({
       "sayproof_ssr": "store_app.sayproof.markup.sayproof_ssr",
       "sayproof_reviews": "store_app.sayproof.markup.sayproof_reviews",
       "sayproof_script": "store_app.sayproof.markup.sayproof_script",
       "sayproof_rating": "store_app.sayproof.markup.sayproof_rating",
       "sayproof_json_ld": "store_app.sayproof.markup.sayproof_json_ld",
       "sayproof_order_reviews": "store_app.sayproof.markup.sayproof_order_reviews",
   })
   ```

   Projects that set up their own Jinja environment add the `JINJA_GLOBALS` dictionary to the environment's `globals`. In the Django template language the same names are used after `{% load sayproof_tags %}`.

Optional settings:

| Setting | Default | Use |
|---|---|---|
| `SAYPROOF_SERVER_BASE` | `SAYPROOF_BASE` | When the server reaches Sayproof at a different address than the browser |
| `SAYPROOF_SCRIPT` | `SAYPROOF_BASE` + `/storefront/sayproof.js` | When the script is served from a CDN |
| `SAYPROOF_TIMEOUT` | `1.5` | Timeout of the SSR call (seconds) |
| `SAYPROOF_VIEWER_TIMEOUT` | `3` | Timeout of the signed-in customer and order detail requests (seconds) |
| `SAYPROOF_CACHE_SECONDS` | `60` | How long an SSR response stays in the cache |
| `SAYPROOF_FAILURE_SECONDS` | `10` | How long to wait before trying again when Sayproof cannot be reached |
| `SAYPROOF_VIEWER_URL` | `/sayproof/viewer/` | Address of the signed-in customer endpoint |
| `SAYPROOF_CUSTOMER_EMAIL` | `request.user.email` | Path of the function that reads the signed-in customer's email address |
| `SAYPROOF_CUSTOMER_NAME` | (none) | Path of the function that reads the name shown next to a question; without it the question shows without a name ([Signed-in customers › Questions and answers](/en/integrate/signed-in-customers.md#questions-and-answers)) |

## Product page

```jinja
{% set reviews = sayproof_ssr(product.sku, product.name) %}
{{ sayproof_reviews(product.sku, product.name, ssr=reviews) }}
{{ sayproof_script() }}
```

- The `sayproof_ssr` response is fetched once and used for both the section and the structured data. The same product's response comes from the cache for 60 seconds.
- The product code must match the key in your product source and the line code in the order.
- To show reviews for all colours and sizes of a product together, pass the group code with `scope="group"`.
- `sayproof_script()` appears once on the page.
- If you draw the review section with your own components, pass `headless=True`; only the state tag is written into the container instead of HTML. See [Headless](/en/integrate/headless.md).

### Structured data

If the page already has the product's `Product` JSON-LD, no second `Product` is added; the rating is added to the existing object:

```jinja
{{ sayproof_json_ld(sayproof_rating(product_json_ld, reviews)) }}
```

If the page has no `Product` JSON-LD:

```jinja
{% if reviews %}{{ sayproof_json_ld(reviews.json_ld) }}{% endif %}
```

`sayproof_json_ld` escapes customer texts so they cannot break out of the tag even if they contain `</script>`.

## Signed-in customer

1. Connect the kit's endpoint: `path("", include("store_app.sayproof.urls"))`.
2. Pass `viewer=True` on the product page: `sayproof_reviews(product.sku, product.name, ssr=reviews, viewer=True)`.

The customer's state is not written into the page; the browser asks `/sayproof/viewer/`, and the store server forwards the question to Sayproof with the server key. The page stays cacheable. The email address is read from `request.user.email` by default; if the session information is elsewhere, define your own function with `SAYPROOF_CUSTOMER_EMAIL`. If the function returns an empty text, the customer is treated as anonymous and no call is made to Sayproof. Details: [Signed-in customers](/en/integrate/signed-in-customers.md).

## Order detail page

```jinja
{{ sayproof_order_reviews(order.number, customer_email) }}
{{ sayproof_script() }}
```

If the order is not found in Sayproof or belongs to another customer, nothing is printed. This call is not cached.

## Content Security Policy

If the page uses a CSP nonce, pass the `nonce` parameter to `sayproof_script`, `sayproof_reviews` (with `viewer=True`) and `sayproof_json_ld`. `script-src` and `connect-src` must include the Sayproof address.

## When something fails

- If the SSR call does not answer within 1.5 seconds or returns anything other than `200`, the container is printed empty and the script fetches reviews in the browser.
- The failure is kept in the cache for 10 seconds, so page loads do not wait again while Sayproof cannot be reached.
- The page does not break even if the cache is not working.
- Logs contain only the called path and the type of error; customer emails and keys are never logged.
