Django Integration Guide

Lune is an excellent companion for Django applications. It allows you to add interactivity to your templates while keeping your backend logic in Python.

Installation

The easiest way to use Lune in Django is by adding a <script> tag to your base template.

html
<!-- base.html -->
<head>
  <script src="https://cdn.jsdelivr.net/npm/lune-js" defer init></script>
</head>

Data Injection

You can pass data from your Django views directly into lu-scope by serializing it as JSON.

In your Django View:

python
import json
from django.shortcuts import render

def profile_view(request):
    user_data = {
        'username': request.user.username,
        'email': request.user.email,
        'is_premium': True
    }
    return render(request, 'profile.html', {
        'user_json': json.dumps(user_data)
    })

In your Django Template:

Use the safe filter to prevent Django from escaping the JSON string.

html
<div lu-scope="{{ user_json|safe }}">
  <p>Username: {{ username }}</p>
  <p lu-if="is_premium">Premium User Badge</p>
</div>

CSRF Token Handling

Django requires a CSRF token for any POST, PUT, or DELETE requests.

Option 1: Reading from Meta Tag

Add the token to a meta tag in your base.html:

html
<meta name="csrf-token" content="{{ csrf_token }}" />

Then access it in your Lune methods:

js
function submitForm() {
  const token = document.querySelector('meta[name="csrf-token"]').content;
  fetch("/api/save/", {
    method: "POST",
    headers: {
      "X-CSRFToken": token,
      "Content-Type": "application/json"
    },
    body: JSON.stringify(this.formData)
  });
}

Practical Examples

1. Progressive Form Validation

Enhance your existing Django forms with real-time validation.

html
<script type="module">
  import { createApp } from "lune-js";

  function UsernameForm() {
    return {
      username: "",
      get isValid() {
        return this.username.length >= 3;
      }
    };
  }

  createApp({ UsernameForm }).mount();
</script>

<form method="POST" lu-scope="UsernameForm()">
  {% csrf_token %}
  <input type="text" name="username" lu-model="username" />
  <p lu-show="username && !isValid" style="color: red;">Username must be at least 3 characters.</p>
  <button :disabled="!isValid">Submit</button>
</form>

TIP

Derived values such as isValid live in a factory function: an object literal written inside lu-scope holds data only, so getters and methods go in JavaScript.

2. Dynamic Dependent Dropdowns

Update a second dropdown based on the selection of the first.

html
<script type="module">
  import { createApp } from "lune-js";

  function CategoryPicker() {
    return {
      selectedCategory: "",
      categories: {
        fruits: ["Apple", "Banana"],
        veggies: ["Carrot", "Potato"]
      },
      get currentItems() {
        return this.categories[this.selectedCategory] || [];
      }
    };
  }

  createApp({ CategoryPicker }).mount();
</script>

<div lu-scope="CategoryPicker()">
  <select lu-model="selectedCategory">
    <option value="">Select Category</option>
    <option value="fruits">Fruits</option>
    <option value="veggies">Vegetables</option>
  </select>

  <select :disabled="!selectedCategory">
    <option lu-for="item in currentItems">{{ item }}</option>
  </select>
</div>

Handling Template Conflicts

Since both Django and Lune use {{ }} for interpolation, you have two options:

1. Escape the braces

Wrap the Lune content in {% verbatim %} tags.

html
{% verbatim %}
<div>{{ pico_vue_variable }}</div>
{% endverbatim %}

2. Change Lune delimiters

Configure Lune to use different delimiters.

js
createApp({
  $delimiters: ["[[", "]]"]
}).mount();

Template:

html
<div>[[ pico_vue_variable ]]</div>