Reactive model
Reactive model¶
Demonstrates that model changes made outside the channel context — such as in a
background thread or a separate process — are pushed reactively to connected
clients. A modify_project action spawns a thread that sleeps for a configurable
delay, then fetches the Project from the database and saves a new name. The
frontend receives the updated Task (with its nested Project) automatically,
without any manual refresh.
Backend¶
import threading
import time
from rxdjango import ContextChannel, rx, action
from .models import Project, Task
from .serializers import TaskSerializer
class ReactiveModelChannel(ContextChannel):
task = rx.model(TaskSerializer())
async def on_connect(self):
self.task = await Task.objects.select_related('project').aget(id=1)
@action
async def modify_project(self, name: str, delay: int):
project_id = self.task.project.id
def _update():
time.sleep(delay)
project = Project.objects.get(id=project_id)
project.name = name
project.save()
threading.Thread(target=_update, daemon=True).start()
Models¶
from django.db import models
from rxdjango.models import ReactiveModel
class Project(ReactiveModel):
name = models.CharField(max_length=64)
class Task(ReactiveModel):
name = models.CharField(max_length=64)
project = models.ForeignKey(Project, on_delete=models.CASCADE, related_name='tasks')
Serializers¶
from rest_framework import serializers
from reactive_model.models import Project, Task
class ProjectSerializer(serializers.ModelSerializer):
class Meta:
model = Project
fields = ['id', 'name']
class TaskSerializer(serializers.ModelSerializer):
project = ProjectSerializer()
class Meta:
model = Task
fields = ['id', 'name', 'project']
Frontend¶
import React, { useState } from 'react';
import { useChannel } from '@rxdjango/react';
import { ReactiveModelChannel } from '../../rx/reactive_model/reactive_model.channels';
import {
Sections,
Button,
TextInput,
Row,
Note,
Field,
Fields,
} from '../../components/demo';
export function ReactiveModelDemo() {
const channel = useChannel(ReactiveModelChannel);
const [projectName, setProjectName] = useState('');
const [delay, setDelay] = useState('2');
return (
<Sections>
<div>
<Note>
Modify the project name with a delay. The update happens in a background
thread outside the channel context, demonstrating that external changes
to a model instance are pushed reactively to the frontend.
</Note>
<Row>
<TextInput
id="reactive-model-project-name"
label="New project name"
value={projectName}
onChange={setProjectName}
/>
<TextInput
id="reactive-model-delay"
label="Delay (seconds)"
value={delay}
onChange={setDelay}
/>
<Button
variant="secondary"
onClick={() => channel.modify_project(projectName, parseInt(delay, 10))}
>
Modify
</Button>
</Row>
</div>
<div>
{channel.task ? (
<Fields>
<Field label="Task">
{channel.task.name}
</Field>
<Field label="Project">
{channel.task.project._loaded
? channel.task.project.name
: 'Loading…'}
</Field>
</Fields>
) : (
<p>
Connecting...
</p>
)}
</div>
</Sections>
);
}
export default ReactiveModelDemo;