示例#1
0
def save_handler(sender, instance, *args, **kwargs):
    model_label = sender._meta.label  # noqa
    channel_layer = get_channel_layer()
    group_name = get_group_name(model_label)
    async_to_sync(channel_layer.group_send)(
        group_name,
        {
            "type": "model.saved",
            "model": model_label,
            "instance_pk": instance.pk,
            "channel_name": group_name,
        },
    )
示例#2
0
    def receive_json(self, content: Dict[str, Any], **kwargs):
        """
        Entrypoint for incoming messages from the connected client.
        """

        request_id = content.get("id", None)
        if request_id is None:
            return  # Can't send error message without request ID, so just return.
        message_type = content.get("type", None)
        if message_type == "subscribe":
            model_label = content.get("model")
            if model_label is None:
                self.send_error(request_id, 400, "No model specified.")
                return

            if model_label not in self.registry:
                self.send_error(
                    request_id,
                    404,
                    f"Model {model_label} not registered for realtime updates.",
                )
                return

            view_action = content.get("action", None)
            if view_action is None or view_action not in ["list", "retrieve"]:
                self.send_error(
                    request_id,
                    400,
                    "`action` must be present and the value must be either `list` or `retrieve`.",
                )

            lookup_value = content.get("lookup_by", None)
            view_kwargs = content.get("view_kwargs", dict())
            query_params = content.get("query_params", dict())

            view = self.registry[model_label].from_scope(
                view_action, self.scope, view_kwargs, query_params)
            model = view.get_model_class()

            # Check to make sure client has permissions to make this subscription.
            has_permission = True
            for permission in view.get_permissions():
                has_permission = has_permission and permission.has_permission(
                    view.request, view)

            # Retrieve actions must check has_object_permission as well.
            if view.action == "retrieve":
                try:
                    instance = view.get_queryset().get(
                        **{view.lookup_field: lookup_value})
                except model.DoesNotExist:
                    self.send_error(request_id, 404, "Instance not found.")
                    return

                for permission in view.get_permissions():
                    has_permission = (has_permission
                                      and permission.has_object_permission(
                                          view.request, view, instance))

            if not has_permission:
                self.send_error(
                    request_id,
                    403,
                    f"Unauthorized to subscribe to {model_label} for action {view_action}",
                )
                return

            # If we've reached this point, then the client can subscribe.
            group_name = get_group_name(model_label)
            print(f"[REST-LIVE] got subscription to {group_name}")

            self.subscriptions.setdefault(group_name, []).append(request_id)
            self.kwargs[request_id] = view_kwargs
            self.params[request_id] = query_params
            self.actions[request_id] = view_action
            self.visible_instance_pks[request_id] = set([
                instance1["pk"]
                for instance1 in view.get_queryset().all().values("pk")
            ])

            # Add subscribe to updates from channel layer: this is the "actual" subscription action.
            async_to_sync(self.channel_layer.group_add)(group_name,
                                                        self.channel_name)
            self.groups.append(group_name)

        elif message_type == "unsubscribe":
            # Get the group name given the request_id
            try:
                # List comprehension is empty if the provided request_id doesn't show up for this consumer
                group_name = [
                    k for k, v in self.subscriptions.items() if request_id in v
                ][0]
            except IndexError:
                self.send_error(
                    request_id,
                    404,
                    "Attempted to unsubscribe for request ID before subscribing.",
                )
                return

            del self.actions[request_id]
            del self.kwargs[request_id]
            del self.visible_instance_pks[request_id]

            self.subscriptions[group_name].remove(request_id)
            self.groups.remove(
                group_name)  # Removes the first occurance of this group name.
            if (
                    group_name not in self.groups
            ):  # If there are no more occurances, unsubscribe to the channel layer.
                async_to_sync(self.channel_layer.group_discard)(
                    group_name, self.channel_name)

            # Delete the key in the dictionary if no more subscriptions.
            if len(self.subscriptions[group_name]) == 0:
                del self.subscriptions[group_name]
        else:
            self.send_error(request_id, 400,
                            f"unknown message type `{message_type}`.")
示例#3
0
    def receive_json(self, content: Dict[str, Any], **kwargs):
        """
        Entrypoint for incoming messages from the connected client.
        """

        request_id = content.get("id", None)
        if request_id is None:
            return  # Can't send error message without request ID, so just return.
        message_type = content.get("type", None)
        if message_type == "subscribe":
            model_label = content.get("model")
            if model_label is None:
                self.send_error(request_id, 400, "No model specified.")
                return

            if model_label not in self.registry:
                self.send_error(
                    request_id,
                    404,
                    f"Model {model_label} not registered for realtime updates.",
                )
                return

            view_action = content.get("action", None)
            if view_action is None or view_action not in ["list", "retrieve"]:
                self.send_error(
                    request_id,
                    400,
                    "`action` must be present and the value must be either `list` or `retrieve`.",
                )

            lookup_value = content.get("lookup_by", None)
            view_kwargs = content.get("view_kwargs", dict())
            query_params = content.get("query_params", dict())

            view = self.registry[model_label].from_scope(
                view_action, self.scope, view_kwargs, query_params)

            # Check to make sure client has permissions to make this subscription.
            has_permission = True
            for permission in view.get_permissions():
                has_permission = has_permission and permission.has_permission(
                    view.request, view)

            # Retrieve actions use get_object() to check object permissions as well.
            if view.action == "retrieve":
                view.kwargs.setdefault(view.lookup_field, lookup_value)
                try:
                    view.get_object()
                except Http404:
                    self.send_error(
                        request_id,
                        404,
                        "Instance not found. Make sure 'lookup_by' is set to a valid ID",
                    )
                    return
                except (NotAuthenticated, PermissionDenied):
                    has_permission = False

            if not has_permission:
                self.send_error(
                    request_id,
                    403,
                    f"Unauthorized to subscribe to {model_label} for action {view_action}",
                )
                return

            # If we've reached this point, then the client can subscribe.
            group_name = get_group_name(model_label)
            print(f"[REST-LIVE] got subscription to {group_name}")

            self.subscriptions.setdefault(group_name, []).append(
                Subscription(
                    request_id,
                    action=view_action,
                    view_kwargs=view_kwargs,
                    query_params=query_params,
                    pks_to_lookup_in_queryset=dict({
                        inst["pk"]: inst[view.lookup_field]
                        for inst in view.get_queryset().all().values(
                            "pk", view.lookup_field)
                    }),
                ))

            # Add subscribe to updates from channel layer: this is the "actual" subscription action.
            async_to_sync(self.channel_layer.group_add)(group_name,
                                                        self.channel_name)
            self.groups.append(group_name)

        elif message_type == "unsubscribe":
            # Get the group name given the request_id
            try:
                # List comprehension is empty if the provided request_id doesn't show up for this consumer
                group_name = [
                    k for k, v in self.subscriptions.items()
                    if request_id in [s.request_id for s in v]
                ][0]
            except IndexError:
                self.send_error(
                    request_id,
                    404,
                    "Attempted to unsubscribe for request ID before subscribing.",
                )
                return

            self.subscriptions[group_name] = [
                sub for sub in self.subscriptions[group_name]
                if sub.request_id != request_id
            ]
            self.groups.remove(
                group_name)  # Removes the first occurrence of this group name.
            if (
                    group_name not in self.groups
            ):  # If there are no more occurrences, unsubscribe to the channel layer.
                async_to_sync(self.channel_layer.group_discard)(
                    group_name, self.channel_name)

            # Delete the key in the dictionary if no more subscriptions.
            if len(self.subscriptions[group_name]) == 0:
                del self.subscriptions[group_name]
        else:
            self.send_error(request_id, 400,
                            f"unknown message type `{message_type}`.")