Metadata-Version: 2.4
Name: django-interlace
Version: 0.2.0
Summary: Brings full stack components to Django.
License: MIT
License-File: LICENSE
Keywords: django,components,ui,python
Author: Will Abbott
Requires-Python: >=3.10,<4
Classifier: Development Status :: 3 - Alpha
Classifier: Framework :: Django
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Programming Language :: Python :: 3.14
Provides-Extra: browser-tests
Requires-Dist: Django (>=4.2)
Requires-Dist: beautifulsoup4 (>=4.12.2,<4.13.0)
Requires-Dist: django_eventstream (>=5.3.2)
Requires-Dist: shortuuid (>=1.0.11,<1.1.0)
Description-Content-Type: text/markdown

# Interlace: Reactive Server-Side Components for Django

## Project Overview

Interlace brings reactive, server-driven components to Django inspired by Laravel Livewire and Phoenix LiveView. Build interactive web applications using Python and Django templates—no JavaScript build tools required.

## Key Features

**Server-Driven Architecture:** All state lives in Python, components render on the server, and only HTML changes are sent to the client. Write reactive UIs entirely in Python with full access to the Django ORM and ecosystem.

**Efficient DOM Morphing:** Uses morphdom to intelligently update only changed elements, preserving focus states, scroll positions, and CSS transitions. No full page reloads, no virtual DOM overhead.

**Reactive Data Binding:** Two-way binding with `lace:model`, instant updates with `.live` modifiers, debouncing with `.debounce`, and automatic form handling. Components re-render automatically when properties change.

**Rich Component Features:** Lifecycle hooks (`mount`, `updated`, `rendered`), nested components with parent-child communication, lazy loading with viewport detection, polling for real-time updates, and built-in validation.

**Zero Build Step:** Minimal client-side JavaScript (~30KB), no npm, no webpack, no build pipeline. Drop into existing Django projects with minimal configuration.

## Installation & Setup

### Prerequisites
- Python 3.10+
- Django 4.2+

### Quick Installation

```bash
pip install interlace
```

Or with Poetry:

```bash
poetry add interlace
```

### Django Configuration

Add to `INSTALLED_APPS` in `settings.py`:

```python
INSTALLED_APPS = [
    # ...
    'interlace',
]
```

Include Interlace URLs in `urls.py`:

```python
from django.urls import path, include

urlpatterns = [
    # ...
    path('interlace/', include('interlace.urls')),
]
```

Add Interlace's JavaScript to your base template:

```django
{% load static %}
<script src="{% static 'interlace/interlace.js' %}" defer></script>
```

## Quick Start

### Create a Component

```python
# app_name/interlace/Counter.py
from interlace import Component

class Counter(Component):
    count: int = 0

    def increment(self):
        self.count += 1

    def decrement(self):
        self.count -= 1

    def inline_template(self):
        return """
        <div>
            <h2>Count: {{ count }}</h2>
            <button lace:click="increment">+</button>
            <button lace:click="decrement">-</button>
        </div>
        """
```

### Use in Templates

```django
{% load interlace %}

<div>
    <h1>My Counter App</h1>
    {% interlace "Counter" %}
</div>
```

That's it! The counter is now reactive—clicking buttons updates the count without page reloads.

## Component Architecture

### Dataclass-Style Components

Components use type hints and default values for clean property definitions:

```python
class UserProfile(Component):
    username: str = ""
    email: str = ""
    is_admin: bool = False
    tags: list = []

    def mount(self):
        # Called when component initializes
        user = User.objects.get(id=self.user_id)
        self.username = user.username
        self.email = user.email
```

### Template Options

**Inline templates:**

```python
def inline_template(self):
    return """<div>{{ content }}</div>"""
```

**External template files:**

```python
# Looks for app_name/interlace/templates/component_name.html
template_name = "user_profile.html"
```

### Event Directives

- `lace:click="method"` - Handle clicks
- `lace:model="property"` - Two-way data binding
- `lace:model.live="property"` - Instant updates
- `lace:model.debounce.300ms="property"` - Debounced updates
- `lace:init="method"` - Run on component mount
- `lace:poll.2s="method"` - Poll every 2 seconds

### Lifecycle Hooks

```python
def mount(self):
    # Called once when component initializes
    pass

def updated_property_name(self, value):
    # Called when specific property changes
    pass

def updated(self, property, value):
    # Called when any property changes
    pass

def rendered(self):
    # Called after component renders
    pass
```

## Example Project Structure

```
my_app/
├── models.py                # Django models
├── views.py                 # Django views
├── urls.py                  # URL routing
├── admin.py                 # Admin configuration
├── interlace/                  # Interlace components
│   ├── __init__.py
│   ├── Counter.py           # Counter component
│   ├── TodoList.py          # Todo list component
│   ├── UserProfile.py       # User profile component
│   └── ProductFilter.py     # Product filter component
└── templates/
    └── interlace/              # Component templates (optional)
        ├── counter.html
        ├── todo_list.html
        └── user_profile.html
```

## Advanced Features

### Query Parameters

Sync component state to URL for shareable, bookmarkable pages:

```python
class ProductFilter(Component):
    search: str = ""
    category: str = "all"
    query_params = ['search', 'category']
```

### Lazy Loading

Defer expensive operations until component is visible:

```python
{% interlace "HeavyComponent" lazy=True %}
```

### Nested Components

Components can render other components and communicate via events:

```python
def delete_item(self):
    self.emit_to('parent_list', 'item_deleted', {'id': self.item_id})
```

### Computed Properties

Cache expensive calculations with `@computed`:

```python
from interlace import Component, computed

class ReportBuilder(Component):
    start_date: str = ""
    end_date: str = ""

    @computed
    def filtered_events(self):
        # Cached within request - multiple template accesses won't re-query
        return Event.objects.filter(date__range=[self.start_date, self.end_date])

    @computed(persist=True, key=['start_date', 'end_date'], seconds=300)
    def performance_data(self):
        # Cached across requests for 5 minutes, per component instance
        return calculate_kpis(self.start_date, self.end_date)

    @computed(cache=True, key=['start_date'], seconds=1800)
    def daily_totals(self):
        # Global cache shared across all users
        return DailyStats.objects.filter(date=self.start_date).aggregate(...)
```

### Database Integration

Direct ORM access in components:

```python
def mount(self):
    self.posts = Post.objects.filter(published=True).select_related('author')

def search(self, query):
    self.posts = Post.objects.filter(title__icontains=query)
```

## Documentation

- **Website:** [Coming soon]
- **Quick Start:** See above
- **Full Documentation:** Available in `/docs` directory
- **Examples:** See `/dev/example_project`

## Development

### Running Tests

```bash
# In Docker (recommended)
docker exec -t django-web-app python manage.py test --parallel

# Locally
cd dev/example_project
python manage.py test
```

### Creating Components

```bash
python manage.py interlace create ComponentName
```

## Similar Projects

- **Laravel Livewire** - PHP framework that inspired Interlace
- **Phoenix LiveView** - Elixir framework with similar architecture
- **HTMX** - Library for HTML-over-the-wire patterns
- **djust** - Django + Rust alternative with similar goals

## License

MIT License - see LICENSE file for details

## Contributing

Contributions welcome! Please see the `/dev` directory for the development setup.

---

Built with ❤️ for the Django community

