Initial commit (Clean history)
This commit is contained in:
107
app/consumers.py
Normal file
107
app/consumers.py
Normal file
@@ -0,0 +1,107 @@
|
||||
import json
|
||||
from channels.generic.websocket import AsyncJsonWebsocketConsumer
|
||||
from channels.db import database_sync_to_async
|
||||
from django.core.serializers.json import DjangoJSONEncoder
|
||||
from .basic import execute_data_query
|
||||
|
||||
class DataConsumer(AsyncJsonWebsocketConsumer):
|
||||
async def encode_json(self, content):
|
||||
"""
|
||||
Encode the given content as JSON, using Django's encoder
|
||||
to handle dates, decimals, etc.
|
||||
"""
|
||||
return json.dumps(content, cls=DjangoJSONEncoder)
|
||||
|
||||
async def connect(self):
|
||||
self.subscribed_groups = set()
|
||||
self.subscription_params = {} # e.g., {'Product': {'filter': '...', 'values': '...'}}
|
||||
await self.accept()
|
||||
|
||||
async def disconnect(self, close_code):
|
||||
for group_name in self.subscribed_groups:
|
||||
await self.channel_layer.group_discard(group_name, self.channel_name)
|
||||
|
||||
async def receive_json(self, content):
|
||||
action = content.get("action")
|
||||
if action == "subscribe":
|
||||
await self.handle_subscribe(content.get("payload", {}), content.get("request_id"))
|
||||
|
||||
async def handle_subscribe(self, payload, request_id):
|
||||
model_name = payload.get("name")
|
||||
params = payload.get("params", {})
|
||||
|
||||
if not model_name:
|
||||
await self.send_json({"type": "error", "request_id": request_id, "message": "Model name is required."})
|
||||
return
|
||||
|
||||
# Store subscription params for this client
|
||||
self.subscription_params[model_name] = params
|
||||
|
||||
# Run the initial data query
|
||||
data = await database_sync_to_async(execute_data_query)(model_name, params)
|
||||
|
||||
# Send the initial result back to the client
|
||||
await self.send_json({
|
||||
"type": "subscription_response",
|
||||
"request_id": request_id,
|
||||
"data": data
|
||||
})
|
||||
|
||||
# Join the group using a lowercase model name to match the signal
|
||||
group_name = f"model_{model_name.lower()}_updates"
|
||||
if group_name not in self.subscribed_groups:
|
||||
await self.channel_layer.group_add(group_name, self.channel_name)
|
||||
self.subscribed_groups.add(group_name)
|
||||
|
||||
async def realtime_update(self, event):
|
||||
# Move imports inside the method to prevent AppRegistryNotReady error on startup
|
||||
import ast
|
||||
from django.db.models import Q
|
||||
from .views import get_serializer
|
||||
|
||||
payload = event["payload"]
|
||||
record = payload["record"]
|
||||
model_name_lower = payload["name"]
|
||||
model_name_capitalized = model_name_lower.capitalize()
|
||||
|
||||
# 1. Get this client's subscription parameters for the specific model
|
||||
client_params = self.subscription_params.get(model_name_capitalized)
|
||||
if not client_params:
|
||||
return # This client is not subscribed to this model.
|
||||
|
||||
# 2. Check if the updated record matches the client's filter
|
||||
filter_str = client_params.get('filter')
|
||||
if filter_str:
|
||||
try:
|
||||
Model, _ = get_serializer(model_name_lower)
|
||||
if not Model:
|
||||
return
|
||||
|
||||
filter_q = Q()
|
||||
filter_dict = ast.literal_eval(filter_str)
|
||||
for key, value in filter_dict.items():
|
||||
filter_q.add(Q(**{key: value}), Q.AND)
|
||||
|
||||
matches = await database_sync_to_async(
|
||||
Model.objects.filter(pk=record["id"]).filter(filter_q).exists
|
||||
)()
|
||||
|
||||
if not matches:
|
||||
return # Record does not match the client's filter, so don't send.
|
||||
except Exception:
|
||||
return # Fail silently if filter is invalid or DB check fails.
|
||||
|
||||
# 3. Create a tailored payload, respecting the 'values' parameter
|
||||
payload_for_client = payload.copy()
|
||||
values_str = client_params.get('values')
|
||||
if values_str:
|
||||
requested_values = values_str.split(',')
|
||||
# The record from the signal contains all fields. Filter it down.
|
||||
filtered_record = {key: record.get(key) for key in requested_values if key in record}
|
||||
payload_for_client['record'] = filtered_record
|
||||
|
||||
# 4. Send the final, tailored payload to the client
|
||||
await self.send_json({
|
||||
"type": "realtime_update",
|
||||
"payload": payload_for_client
|
||||
})
|
||||
Reference in New Issue
Block a user