diff --git a/api/__pycache__/settings.cpython-313.pyc b/api/__pycache__/settings.cpython-313.pyc index 8cb90ed5..23f6b64e 100644 Binary files a/api/__pycache__/settings.cpython-313.pyc and b/api/__pycache__/settings.cpython-313.pyc differ diff --git a/app/__pycache__/cleardata.cpython-313.pyc b/app/__pycache__/cleardata.cpython-313.pyc index 775e9501..8cbd5637 100644 Binary files a/app/__pycache__/cleardata.cpython-313.pyc and b/app/__pycache__/cleardata.cpython-313.pyc differ diff --git a/app/__pycache__/consumers.cpython-313.pyc b/app/__pycache__/consumers.cpython-313.pyc index 3d2d9ac2..c79da143 100644 Binary files a/app/__pycache__/consumers.cpython-313.pyc and b/app/__pycache__/consumers.cpython-313.pyc differ diff --git a/app/__pycache__/signals.cpython-313.pyc b/app/__pycache__/signals.cpython-313.pyc index c6fd9c2d..1bd5e096 100644 Binary files a/app/__pycache__/signals.cpython-313.pyc and b/app/__pycache__/signals.cpython-313.pyc differ diff --git a/app/consumers.py b/app/consumers.py index 82cfba8a..3cb97c16 100644 --- a/app/consumers.py +++ b/app/consumers.py @@ -54,77 +54,86 @@ class DataConsumer(AsyncJsonWebsocketConsumer): self.subscribed_groups.add(group_name) async def realtime_update(self, event): - # Move imports inside the method to prevent AppRegistryNotReady error on startup - from django.db.models import Q - from .views import get_serializer + # Bước gỡ lỗi cuối cùng: xác nhận phương thức này được gọi và bắt bất kỳ lỗi nào + print(f"--- CONSUMER: realtime_update invoked for event name: {event['payload']['name']} ---") + try: + # Move imports inside the method to prevent AppRegistryNotReady error on startup + from django.db.models import Q + from .views import get_serializer - payload = event["payload"] - change_type = payload.get("change_type") - record_id = payload["record"].get("id") + payload = event["payload"] + change_type = payload.get("change_type") + record_id = payload["record"].get("id") - if not record_id or not change_type: - return + if not record_id or not change_type: + return - model_name_lower = payload["name"] - model_name_capitalized = model_name_lower.capitalize() + model_name_lower = payload["name"] + # Chuyển đổi snake_case (từ signal) thành PascalCase với dấu gạch dưới (được sử dụng trong subscription) + # Ví dụ: 'product_note' -> 'Product_Note' + model_name_pascal_case = '_'.join(word.capitalize() for word in model_name_lower.split('_')) - # 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. + # 1. Lấy các tham số đăng ký của client này cho model cụ thể + client_params = self.subscription_params.get(model_name_pascal_case) + if not client_params: + return # Client này không đăng ký model này. - # 2. Handle DELETION event - if change_type == 'deleted': - await self.send_json({ - "type": "realtime_update", - "payload": { - "name": model_name_lower, - "change_type": "deleted", - "record": {"id": record_id} - } - }) - return - - # 3. Handle CREATION and UPDATE events - Model, _ = get_serializer(model_name_lower) - if not Model: - return - - # Build a Q object from the client's filter dictionary - filter_q = Q() - filter_dict = client_params.get('filter') - if isinstance(filter_dict, dict): - filter_q = Q(**filter_dict) - - # Check if the object actually exists with the combined filter - record_exists = await database_sync_to_async( - Model.objects.filter(Q(pk=record_id) & filter_q).exists - )() - - if record_exists: - # If record matches filter, re-run the query to get the correctly shaped data - single_record_params = client_params.copy() - single_record_params['filter'] = {'pk': record_id} - - data = await database_sync_to_async(execute_data_query)(model_name_capitalized, single_record_params) - - if data and data.get('rows'): - payload_for_client = { - "name": model_name_lower, - "change_type": change_type, - "record": data['rows'][0] - } + # 2. Xử lý sự kiện DELETION + if change_type == 'deleted': await self.send_json({ "type": "realtime_update", - "payload": payload_for_client + "payload": { + "name": model_name_lower, + "change_type": "deleted", + "record": {"id": record_id} + } }) - else: - # If the record exists but no longer matches the filter, treat as a deletion for this client - await self.send_json({ - "type": "realtime_update", - "payload": { - "name": model_name_lower, - "change_type": "deleted", - "record": {"id": record_id} - } - }) + return + + # 3. Xử lý sự kiện CREATION và UPDATE + Model, _ = get_serializer(model_name_lower) + if not Model: + return + + # Xây dựng một đối tượng Q từ từ điển bộ lọc của client + filter_q = Q() + filter_dict = client_params.get('filter') + if isinstance(filter_dict, dict): + filter_q = Q(**filter_dict) + + # Kiểm tra xem đối tượng có thực sự tồn tại với bộ lọc kết hợp không + record_exists = await database_sync_to_async( + Model.objects.filter(Q(pk=record_id) & filter_q).exists + )() + + if record_exists: + # Nếu bản ghi khớp với bộ lọc, chạy lại truy vấn để lấy dữ liệu có định dạng đúng + single_record_params = client_params.copy() + single_record_params['filter'] = {'pk': record_id} + + data = await database_sync_to_async(execute_data_query)(model_name_pascal_case, single_record_params) + + if data and data.get('rows'): + payload_for_client = { + "name": model_name_lower, + "change_type": change_type, + "record": data['rows'][0] + } + await self.send_json({ + "type": "realtime_update", + "payload": payload_for_client + }) + else: + # Nếu bản ghi tồn tại nhưng không còn khớp với bộ lọc, coi như là một thao tác xóa đối với client này + await self.send_json({ + "type": "realtime_update", + "payload": { + "name": model_name_lower, + "change_type": "deleted", + "record": {"id": record_id} + } + }) + except Exception as e: + import traceback + print(f"!!!!!!!!!! EXCEPTION in realtime_update: {e} !!!!!!!!!!!") + traceback.print_exc()